From 20d5640ca49c76c89be5bebcc00981942c7a9c19 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 19 Jun 2019 16:56:40 +0100 Subject: libcamera: controls: Introduce control-related data types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a set of data types to support controls: - ControlValue stores a control type and value in a generic way - ControlId enumerates all the control identifiers - ControlIdentifier declares the types of a control and map their names - ControlInfo stores runtime information for controls - ControlList contains a set of control info and value pairs The control definitions map is generated from the controls documentation to ensure that the two will always be synchronised. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Niklas Söderlund --- Documentation/Doxyfile.in | 3 +- include/libcamera/control_ids.h | 35 ++++ include/libcamera/controls.h | 134 ++++++++++++ include/libcamera/meson.build | 2 + src/libcamera/controls.cpp | 451 ++++++++++++++++++++++++++++++++++++++++ src/libcamera/gen-controls.awk | 106 ++++++++++ src/libcamera/meson.build | 11 + 7 files changed, 741 insertions(+), 1 deletion(-) create mode 100644 include/libcamera/control_ids.h create mode 100644 include/libcamera/controls.h create mode 100644 src/libcamera/controls.cpp create mode 100755 src/libcamera/gen-controls.awk diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index c5863120..9ca32241 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -868,7 +868,8 @@ EXCLUDE_SYMBOLS = libcamera::SignalBase \ libcamera::SlotArgs \ libcamera::SlotBase \ libcamera::SlotMember \ - libcamera::SlotStatic + libcamera::SlotStatic \ + std::* # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include diff --git a/include/libcamera/control_ids.h b/include/libcamera/control_ids.h new file mode 100644 index 00000000..d0e700da --- /dev/null +++ b/include/libcamera/control_ids.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * control_ids.h : Control ID list + */ + +#ifndef __LIBCAMERA_CONTROL_IDS_H__ +#define __LIBCAMERA_CONTROL_IDS_H__ + +#include + +namespace libcamera { + +enum ControlId { +}; + +} /* namespace libcamera */ + +namespace std { + +template<> +struct hash { + using argument_type = libcamera::ControlId; + using result_type = std::size_t; + + result_type operator()(const argument_type &key) const noexcept + { + return std::hash::type>()(key); + } +}; + +} /* namespace std */ + +#endif // __LIBCAMERA_CONTROL_IDS_H__ diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h new file mode 100644 index 00000000..22061559 --- /dev/null +++ b/include/libcamera/controls.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * controls.h - Control handling + */ + +#ifndef __LIBCAMERA_CONTROLS_H__ +#define __LIBCAMERA_CONTROLS_H__ + +#include +#include +#include + +#include + +namespace libcamera { + +class Camera; + +enum ControlValueType { + ControlValueNone, + ControlValueBool, + ControlValueInteger, + ControlValueInteger64, +}; + +class ControlValue +{ +public: + ControlValue(); + ControlValue(bool value); + ControlValue(int value); + ControlValue(int64_t value); + + ControlValueType type() const { return type_; }; + bool isNone() const { return type_ == ControlValueNone; }; + + void set(bool value); + void set(int value); + void set(int64_t value); + + bool getBool() const; + int getInt() const; + int64_t getInt64() const; + + std::string toString() const; + +private: + ControlValueType type_; + + union { + bool bool_; + int integer_; + int64_t integer64_; + }; +}; + +struct ControlIdentifier { + ControlId id; + const char *name; + ControlValueType type; +}; + +class ControlInfo +{ +public: + explicit ControlInfo(ControlId id, const ControlValue &min = 0, + const ControlValue &max = 0); + + ControlId id() const { return ident_->id; } + const char *name() const { return ident_->name; } + ControlValueType type() const { return ident_->type; } + + const ControlValue &min() const { return min_; } + const ControlValue &max() const { return max_; } + + std::string toString() const; + +private: + const struct ControlIdentifier *ident_; + ControlValue min_; + ControlValue max_; +}; + +bool operator==(const ControlInfo &lhs, const ControlInfo &rhs); +bool operator==(const ControlId &lhs, const ControlInfo &rhs); +bool operator==(const ControlInfo &lhs, const ControlId &rhs); +static inline bool operator!=(const ControlInfo &lhs, const ControlInfo &rhs) +{ + return !(lhs == rhs); +} +static inline bool operator!=(const ControlId &lhs, const ControlInfo &rhs) +{ + return !(lhs == rhs); +} +static inline bool operator!=(const ControlInfo &lhs, const ControlId &rhs) +{ + return !(lhs == rhs); +} + +class ControlList +{ +private: + using ControlListMap = std::unordered_map; + +public: + ControlList(Camera *camera); + + using iterator = ControlListMap::iterator; + using const_iterator = ControlListMap::const_iterator; + + iterator begin() { return controls_.begin(); } + iterator end() { return controls_.end(); } + const_iterator begin() const { return controls_.begin(); } + const_iterator end() const { return controls_.end(); } + + bool contains(const ControlInfo *info) const; + bool empty() const { return controls_.empty(); } + std::size_t size() const { return controls_.size(); } + void clear() { controls_.clear(); } + + ControlValue &operator[](const ControlInfo *info) { return controls_[info]; } + + void update(const ControlList &list); + +private: + Camera *camera_; + ControlListMap controls_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_CONTROLS_H__ */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 15484724..3067120a 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -2,6 +2,8 @@ libcamera_api = files([ 'buffer.h', 'camera.h', 'camera_manager.h', + 'control_ids.h', + 'controls.h', 'event_dispatcher.h', 'event_notifier.h', 'geometry.h', diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp new file mode 100644 index 00000000..7fac1169 --- /dev/null +++ b/src/libcamera/controls.cpp @@ -0,0 +1,451 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * controls.cpp - Control handling + */ + +#include + +#include +#include + +#include "log.h" +#include "utils.h" + +/** + * \file controls.h + * \brief Describes control framework and controls supported by a camera + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Controls) + +/** + * \enum ControlValueType + * \brief Define the data type of value represented by a ControlValue + * \var ControlValueNone + * Identifies an unset control value + * \var ControlValueBool + * Identifies controls storing a boolean value + * \var ControlValueInteger + * Identifies controls storing an integer value + * \var ControlValueInteger64 + * Identifies controls storing a 64-bit integer value + */ + +/** + * \class ControlValue + * \brief Abstract type representing the value of a control + */ + +/** + * \brief Construct an empty ControlValue. + */ +ControlValue::ControlValue() + : type_(ControlValueNone) +{ +} + +/** + * \brief Construct a Boolean ControlValue + * \param[in] value Boolean value to store + */ +ControlValue::ControlValue(bool value) + : type_(ControlValueBool), bool_(value) +{ +} + +/** + * \brief Construct an integer ControlValue + * \param[in] value Integer value to store + */ +ControlValue::ControlValue(int value) + : type_(ControlValueInteger), integer_(value) +{ +} + +/** + * \brief Construct a 64 bit integer ControlValue + * \param[in] value Integer value to store + */ +ControlValue::ControlValue(int64_t value) + : type_(ControlValueInteger64), integer64_(value) +{ +} + +/** + * \fn ControlValue::type() + * \brief Retrieve the data type of the value + * \return The value data type + */ + +/** + * \fn ControlValue::isNone() + * \brief Determine if the value is not initialised + * \return True if the value type is ControlValueNone, false otherwise + */ + +/** + * \brief Set the value with a boolean + * \param[in] value Boolean value to store + */ +void ControlValue::set(bool value) +{ + type_ = ControlValueBool; + bool_ = value; +} + +/** + * \brief Set the value with an integer + * \param[in] value Integer value to store + */ +void ControlValue::set(int value) +{ + type_ = ControlValueInteger; + integer_ = value; +} + +/** + * \brief Set the value with a 64 bit integer + * \param[in] value 64 bit integer value to store + */ +void ControlValue::set(int64_t value) +{ + type_ = ControlValueInteger64; + integer64_ = value; +} + +/** + * \brief Get the boolean value + * + * The value type must be Boolean. + * + * \return The boolean value + */ +bool ControlValue::getBool() const +{ + ASSERT(type_ == ControlValueBool); + + return bool_; +} + +/** + * \brief Get the integer value + * + * The value type must be Integer or Integer64. + * + * \return The integer value + */ +int ControlValue::getInt() const +{ + ASSERT(type_ == ControlValueInteger || type_ == ControlValueInteger64); + + return integer_; +} + +/** + * \brief Get the 64-bit integer value + * + * The value type must be Integer or Integer64. + * + * \return The 64-bit integer value + */ +int64_t ControlValue::getInt64() const +{ + ASSERT(type_ == ControlValueInteger || type_ == ControlValueInteger64); + + return integer64_; +} + +/** + * \brief Assemble and return a string describing the value + * \return A string describing the ControlValue + */ +std::string ControlValue::toString() const +{ + switch (type_) { + case ControlValueNone: + return ""; + case ControlValueBool: + return bool_ ? "True" : "False"; + case ControlValueInteger: + return std::to_string(integer_); + case ControlValueInteger64: + return std::to_string(integer64_); + } + + return ""; +} + +/** + * \enum ControlId + * \brief Numerical control ID + */ + +/** + * \struct ControlIdentifier + * \brief Describe a ControlId with control specific constant meta-data + * + * Defines a Control with a unique ID, a name, and a type. + * This structure is used as static part of the auto-generated control + * definitions, which are generated from the ControlId documentation. + * + * \var ControlIdentifier::id + * The unique ID for a control + * \var ControlIdentifier::name + * The string representation of the control + * \var ControlIdentifier::type + * The ValueType required to represent the control value + */ + +/* + * The controlTypes are automatically generated to produce a control_types.cpp + * output. This file is not for public use, and so no suitable header exists + * for this sole usage of the controlTypes reference. As such the extern is + * only defined here for use during the ControlInfo constructor and should not + * be referenced directly elsewhere. + */ +extern const std::unordered_map controlTypes; + +/** + * \class ControlInfo + * \brief Describe the information and capabilities of a Control + * + * The ControlInfo represents control specific meta-data which is constant on a + * per camera basis. ControlInfo classes are constructed by pipeline handlers + * to expose the controls they support and the metadata needed to utilise those + * controls. + */ + +/** + * \brief Construct a ControlInfo with minimum and maximum range parameters + * \param[in] id The control ID + * \param[in] min The control minimum value + * \param[in] max The control maximum value + */ +ControlInfo::ControlInfo(ControlId id, const ControlValue &min, + const ControlValue &max) + : min_(min), max_(max) +{ + auto iter = controlTypes.find(id); + if (iter == controlTypes.end()) { + LOG(Controls, Fatal) << "Attempt to create invalid ControlInfo"; + return; + } + + ident_ = &iter->second; +} + +/** + * \fn ControlInfo::id() + * \brief Retrieve the control ID + * \return The control ID + */ + +/** + * \fn ControlInfo::name() + * \brief Retrieve the control name string + * \return The control name string + */ + +/** + * \fn ControlInfo::type() + * \brief Retrieve the control data type + * \return The control data type + */ + +/** + * \fn ControlInfo::min() + * \brief Retrieve the minimum value of the control + * \return A ControlValue with the minimum value for the control + */ + +/** + * \fn ControlInfo::max() + * \brief Retrieve the maximum value of the control + * \return A ControlValue with the maximum value for the control + */ + +/** + * \brief Provide a string representation of the ControlInfo + */ +std::string ControlInfo::toString() const +{ + std::stringstream ss; + + ss << name() << "[" << min_.toString() << ".." << max_.toString() << "]"; + + return ss.str(); +} + +/** + * \brief Compare control information for equality + * \param[in] lhs Left-hand side control information + * \param[in] rhs Right-hand side control information + * + * Control information is compared based on the ID only, as a camera may not + * have two separate controls with the same ID. + * + * \return True if \a lhs and \a rhs are equal, false otherwise + */ +bool operator==(const ControlInfo &lhs, const ControlInfo &rhs) +{ + return lhs.id() == rhs.id(); +} + +/** + * \brief Compare control ID and information for equality + * \param[in] lhs Left-hand side control identifier + * \param[in] rhs Right-hand side control information + * + * Control information is compared based on the ID only, as a camera may not + * have two separate controls with the same ID. + * + * \return True if \a lhs and \a rhs are equal, false otherwise + */ +bool operator==(const ControlId &lhs, const ControlInfo &rhs) +{ + return lhs == rhs.id(); +} + +/** + * \brief Compare control information and ID for equality + * \param[in] lhs Left-hand side control information + * \param[in] rhs Right-hand side control identifier + * + * Control information is compared based on the ID only, as a camera may not + * have two separate controls with the same ID. + * + * \return True if \a lhs and \a rhs are equal, false otherwise + */ +bool operator==(const ControlInfo &lhs, const ControlId &rhs) +{ + return lhs.id() == rhs; +} + +/** + * \class ControlList + * \brief Associate a list of ControlId with their values for a camera + * + * A ControlList wraps a map of ControlId to ControlValue and provide + * additional validation against the control information exposed by a Camera. + * + * A list is only valid for as long as the camera it refers to is valid. After + * that calling any method of the ControlList class other than its destructor + * will cause undefined behaviour. + */ + +/** + * \brief Construct a ControlList with a reference to the Camera it applies on + * \param[in] camera The camera + */ +ControlList::ControlList(Camera *camera) + : camera_(camera) +{ +} + +/** + * \typedef ControlList::iterator + * \brief Iterator for the controls contained within the list + */ + +/** + * \typedef ControlList::const_iterator + * \brief Const iterator for the controls contained within the list + */ + +/** + * \fn iterator ControlList::begin() + * \brief Retrieve an iterator to the first Control in the list + * \return An iterator to the first Control in the list + */ + +/** + * \fn const_iterator ControlList::begin() const + * \brief Retrieve a const_iterator to the first Control in the list + * \return A const_iterator to the first Control in the list + */ + +/** + * \fn iterator ControlList::end() + * \brief Retrieve an iterator pointing to the past-the-end control in the list + * \return An iterator to the element following the last control in the list + */ + +/** + * \fn const_iterator ControlList::end() const + * \brief Retrieve a const iterator pointing to the past-the-end control in the + * list + * \return A const iterator to the element following the last control in the + * list + */ + +/** + * \brief Check if the list contains a control with the specified \a info + * \param[in] info The control info + * \return True if the list contains a matching control, false otherwise + */ +bool ControlList::contains(const ControlInfo *info) const +{ + return controls_.find(info) != controls_.end(); +} + +/** + * \fn ControlList::empty() + * \brief Identify if the list is empty + * \return True if the list does not contain any control, false otherwise + */ + +/** + * \fn ControlList::size() + * \brief Retrieve the number of controls in the list + * \return The number of Control entries stored in the list + */ + +/** + * \fn ControlList::clear() + * \brief Removes all controls from the list + */ + +/** + * \fn ControlList::operator[](const ControlInfo *info) + * \brief Access or insert the control specified by \a info + * \param[in] info The control info + * + * This method returns a reference to the control identified by \a info, + * inserting it in the list if the info is not already present. + * + * \return A reference to the value of the control identified by \a info + */ + +/** + * \brief Update the list with a union of itself and \a other + * \param other The other list + * + * Update the control list to include all values from the \a other list. + * Elements in the list whose control IDs are contained in \a other are updated + * with the value from \a other. Elements in the \a other list that have no + * corresponding element in the list are added to the list with their value. + * + * The behaviour is undefined if the two lists refer to different Camera + * instances. + */ +void ControlList::update(const ControlList &other) +{ + if (other.camera_ != camera_) { + LOG(Controls, Error) + << "Can't update ControlList from a different camera"; + return; + } + + for (auto it : other) { + const ControlInfo *info = it.first; + const ControlValue &value = it.second; + + controls_[info] = value; + } +} + +} /* namespace libcamera */ diff --git a/src/libcamera/gen-controls.awk b/src/libcamera/gen-controls.awk new file mode 100755 index 00000000..f3d06812 --- /dev/null +++ b/src/libcamera/gen-controls.awk @@ -0,0 +1,106 @@ +#!/usr/bin/awk -f + +# SPDX-License-Identifier: LGPL-2.1-or-later + +# Controls are documented using Doxygen in the main controls.cpp source. +# +# Generate control tables directly from the documentation, creating enumerations +# to support the IDs and static type information regarding each control. + +BEGIN { + id=0 + input=ARGV[1] + mode=ARGV[2] + output=ARGV[3] +} + +# Detect Doxygen style comment blocks and ignore other lines +/^\/\*\*$/ { in_doxygen=1; first_line=1; next } +// { if (!in_doxygen) next } + +# Entry point for the Control Documentation +/ * \\enum ControlId$/ { in_controls=1; first_line=0; next } +// { if (!in_controls) next } + +# Extract control information +/ \* \\var/ { names[++id]=$3; first_line=0; next } +/ \* ControlType:/ { types[id] = $3 } + +# End of comment blocks +/^ \*\// { in_doxygen=0 } + +# Identify the end of controls +/^ \* \\/ { if (first_line) exit } +// { first_line=0 } + +################################ +# Support output file generation + +function basename(file) { + sub(".*/", "", file) + return file +} + +function Header(file, description) { + print "/* SPDX-License-Identifier: LGPL-2.1-or-later */" > file + print "/*" > file + print " * Copyright (C) 2019, Google Inc." > file + print " *" > file + print " * " basename(file) " - " description > file + print " *" > file + print " * This file is auto-generated. Do not edit." > file + print " */" > file + print "" > file +} + +function EnterNameSpace(file) { + print "namespace libcamera {" > file + print "" > file +} + +function ExitNameSpace(file) { + print "" > file + print "} /* namespace libcamera */" > file +} + +function GenerateHeader(file) { + Header(file, "Control ID list") + + print "#ifndef __LIBCAMERA_CONTROL_IDS_H__" > file + print "#define __LIBCAMERA_CONTROL_IDS_H__" > file + print "" > file + + EnterNameSpace(file) + print "enum ControlId {" > file + for (i=1; i <= id; ++i) { + printf "\t%s,\n", names[i] > file + } + print "};" > file + ExitNameSpace(file) + + print "" > file + print "#endif // __LIBCAMERA_CONTROL_IDS_H__" > file +} + +function GenerateTable(file) { + Header(file, "Control types") + print "#include " > file + print "" > file + + EnterNameSpace(file) + + print "extern const std::unordered_map" > file + print "controlTypes {" > file + for (i=1; i <= id; ++i) { + printf "\t{ %s, { %s, \"%s\", ControlValue%s } },\n", names[i], names[i], names[i], types[i] > file + } + print "};" > file + ExitNameSpace(file) +} + +END { + if (mode == "--header") + GenerateHeader(output) + else + GenerateTable(output) +} diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 45bd9d17..8075b1f6 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -3,6 +3,7 @@ libcamera_sources = files([ 'camera.cpp', 'camera_manager.cpp', 'camera_sensor.cpp', + 'controls.cpp', 'device_enumerator.cpp', 'device_enumerator_sysfs.cpp', 'event_dispatcher.cpp', @@ -68,6 +69,16 @@ if libudev.found() ]) endif +gen_controls = files('gen-controls.awk') + +control_types_cpp = custom_target('control_types_cpp', + input : files('controls.cpp'), + output : 'control_types.cpp', + depend_files : gen_controls, + command : [gen_controls, '@INPUT@', '--code', '@OUTPUT@']) + +libcamera_sources += control_types_cpp + libcamera_deps = [ cc.find_library('dl'), libudev, -- cgit v1.2.1