Message ID | 20190701201504.28487-4-laurent.pinchart@ideasonboard.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Kieran, Thanks for your work. On 2019-07-01 23:14:54 +0300, Laurent Pinchart wrote: > From: Kieran Bingham <kieran.bingham@ideasonboard.com> > > 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 <kieran.bingham@ideasonboard.com> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> I tested the awk script, it works but my awk is too week to properly review it. So I will blindly trust you on that no backdoors exists in it (much the same way I would review perl...) ;-P Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> > --- > Changes since v3: > > - Typo fixes > - Optimized the contains() implementation > - Various documentation updates > > Changes since v2: > > - Squashed "Provide ControlValue class" > - Renamed Value to ControlValue > - Removed operator<<() > - Added control table generation > - Moved control definitions to control_definitions.h > - Renamed ControlTypes to controlTypes and make it const > - Moved the initial controls list to a separate patch > - Renamed control_definitions.h to control_ids.h and > control_definitions.cpp to control_types.cpp to match the contained > enum and variable name respectively > - Indexed ControlList by ControlInfo pointer instead of value > - Replaced ControlInfoHash with std::hash specialisation > - Added automatic conversion between 32- and 64-bit integer values > > The automatic conversion between integer types was prompted by an > assertion failure due to the use of getInt() on the min() and max() > value of an Integer control. The min and max ControlValue instances are > create as Integer64, due to the V4L2ControlInfo class returning the > range as int64_t. This may need to be reworked. > --- > 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 c58631200dd5..9ca32241b895 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 000000000000..d0e700da9844 > --- /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 <functional> > + > +namespace libcamera { > + > +enum ControlId { > +}; > + > +} /* namespace libcamera */ > + > +namespace std { > + > +template<> > +struct hash<libcamera::ControlId> { > + using argument_type = libcamera::ControlId; > + using result_type = std::size_t; > + > + result_type operator()(const argument_type &key) const noexcept > + { > + return std::hash<std::underlying_type<argument_type>::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 000000000000..2206155909ca > --- /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 <stdint.h> > +#include <string> > +#include <unordered_map> > + > +#include <libcamera/control_ids.h> > + > +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<const ControlInfo *, ControlValue>; > + > +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 15484724df01..3067120a1598 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 000000000000..7fac1169cbfd > --- /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 <libcamera/controls.h> > + > +#include <sstream> > +#include <string> > + > +#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 "<None>"; > + case ControlValueBool: > + return bool_ ? "True" : "False"; > + case ControlValueInteger: > + return std::to_string(integer_); > + case ControlValueInteger64: > + return std::to_string(integer64_); > + } > + > + return "<ValueType Error>"; > +} > + > +/** > + * \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<ControlId, ControlIdentifier> 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 000000000000..f3d068123012 > --- /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 <libcamera/controls.h>" > file > + print "" > file > + > + EnterNameSpace(file) > + > + print "extern const std::unordered_map<ControlId, ControlIdentifier>" > 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 985aa7e8ab0e..b1ee92735e41 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', > @@ -66,6 +67,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, > -- > Regards, > > Laurent Pinchart > > _______________________________________________ > libcamera-devel mailing list > libcamera-devel@lists.libcamera.org > https://lists.libcamera.org/listinfo/libcamera-devel
diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index c58631200dd5..9ca32241b895 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 000000000000..d0e700da9844 --- /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 <functional> + +namespace libcamera { + +enum ControlId { +}; + +} /* namespace libcamera */ + +namespace std { + +template<> +struct hash<libcamera::ControlId> { + using argument_type = libcamera::ControlId; + using result_type = std::size_t; + + result_type operator()(const argument_type &key) const noexcept + { + return std::hash<std::underlying_type<argument_type>::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 000000000000..2206155909ca --- /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 <stdint.h> +#include <string> +#include <unordered_map> + +#include <libcamera/control_ids.h> + +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<const ControlInfo *, ControlValue>; + +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 15484724df01..3067120a1598 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 000000000000..7fac1169cbfd --- /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 <libcamera/controls.h> + +#include <sstream> +#include <string> + +#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 "<None>"; + case ControlValueBool: + return bool_ ? "True" : "False"; + case ControlValueInteger: + return std::to_string(integer_); + case ControlValueInteger64: + return std::to_string(integer64_); + } + + return "<ValueType Error>"; +} + +/** + * \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<ControlId, ControlIdentifier> 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 000000000000..f3d068123012 --- /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 <libcamera/controls.h>" > file + print "" > file + + EnterNameSpace(file) + + print "extern const std::unordered_map<ControlId, ControlIdentifier>" > 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 985aa7e8ab0e..b1ee92735e41 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', @@ -66,6 +67,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,