| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| /* |
| * Copyright (C) 2019, Google Inc. |
| * |
| * control_serializer.cpp - Control (de)serializer |
| */ |
| |
| #include "libcamera/internal/control_serializer.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <vector> |
| |
| #include <libcamera/control_ids.h> |
| #include <libcamera/controls.h> |
| #include <libcamera/ipa/ipa_controls.h> |
| #include <libcamera/span.h> |
| |
| #include "libcamera/internal/byte_stream_buffer.h" |
| #include "libcamera/internal/log.h" |
| |
| /** |
| * \file control_serializer.h |
| * \brief Serialization and deserialization helpers for controls |
| */ |
| |
| namespace libcamera { |
| |
| LOG_DEFINE_CATEGORY(Serializer) |
| |
| /** |
| * \class ControlSerializer |
| * \brief Serializer and deserializer for control-related classes |
| * |
| * The control serializer is a helper to serialize and deserialize |
| * ControlInfoMap and ControlValue instances for the purpose of communication |
| * with IPA modules. |
| * |
| * Neither the ControlInfoMap nor the ControlList are self-contained data |
| * container. ControlInfoMap references an external ControlId in each of its |
| * entries, and ControlList references a ControlInfoMap for the purpose of |
| * validation. Serializing and deserializing those objects thus requires a |
| * context that maintains the associations between them. The control serializer |
| * fulfils this task. |
| * |
| * ControlInfoMap instances can be serialized on their own, but require |
| * ControlId instances to be provided at deserialization time. The serializer |
| * recreates those ControlId instances and stores them in an internal cache, |
| * from which the ControlInfoMap is populated. |
| * |
| * ControlList instances need to be associated with a ControlInfoMap when |
| * deserialized. To make this possible, the control lists are serialized with a |
| * handle to their ControlInfoMap, and the map is looked up from the handle at |
| * deserialization time. To make this possible, the serializer assigns a |
| * numerical handle to ControlInfoMap instances when they are serialized, and |
| * stores the mapping between handle and ControlInfoMap both when serializing |
| * (for the pipeline handler side) and deserializing (for the IPA side) them. |
| * This mapping is used when serializing a ControlList to include the |
| * corresponding ControlInfoMap handle in the binary data, and when |
| * deserializing to retrieve the corresponding ControlInfoMap. |
| * |
| * In order to perform those tasks, the serializer keeps an internal state that |
| * needs to be properly populated. This mechanism requires the ControlInfoMap |
| * corresponding to a ControlList to have been serialized or deserialized |
| * before the ControlList is serialized or deserialized. Failure to comply with |
| * that constraint results in serialization or deserialization failure of the |
| * ControlList. |
| * |
| * The serializer can be reset() to clear its internal state. This may be |
| * performed when reconfiguring an IPA to avoid constant growth of the internal |
| * state, especially if the contents of the ControlInfoMap instances change at |
| * that time. A reset of the serializer invalidates all ControlList and |
| * ControlInfoMap that have been previously deserialized. The caller shall thus |
| * proceed with care to avoid stale references. |
| */ |
| |
| ControlSerializer::ControlSerializer() |
| : serial_(0) |
| { |
| } |
| |
| /** |
| * \brief Reset the serializer |
| * |
| * Reset the internal state of the serializer. This invalidates all the |
| * ControlList and ControlInfoMap that have been previously deserialized. |
| */ |
| void ControlSerializer::reset() |
| { |
| serial_ = 0; |
| |
| infoMapHandles_.clear(); |
| infoMaps_.clear(); |
| controlIds_.clear(); |
| } |
| |
| size_t ControlSerializer::binarySize(const ControlValue &value) |
| { |
| return value.data().size_bytes(); |
| } |
| |
| size_t ControlSerializer::binarySize(const ControlInfo &info) |
| { |
| return binarySize(info.min()) + binarySize(info.max()); |
| } |
| |
| /** |
| * \brief Retrieve the size in bytes required to serialize a ControlInfoMap |
| * \param[in] infoMap The control info map |
| * |
| * Compute and return the size in bytes required to store the serialized |
| * ControlInfoMap. |
| * |
| * \return The size in bytes required to store the serialized ControlInfoMap |
| */ |
| size_t ControlSerializer::binarySize(const ControlInfoMap &infoMap) |
| { |
| size_t size = sizeof(struct ipa_controls_header) |
| + infoMap.size() * sizeof(struct ipa_control_info_entry); |
| |
| for (const auto &ctrl : infoMap) |
| size += binarySize(ctrl.second); |
| |
| return size; |
| } |
| |
| /** |
| * \brief Retrieve the size in bytes required to serialize a ControlList |
| * \param[in] list The control list |
| * |
| * Compute and return the size in bytes required to store the serialized |
| * ControlList. |
| * |
| * \return The size in bytes required to store the serialized ControlList |
| */ |
| size_t ControlSerializer::binarySize(const ControlList &list) |
| { |
| size_t size = sizeof(struct ipa_controls_header) |
| + list.size() * sizeof(struct ipa_control_value_entry); |
| |
| for (const auto &ctrl : list) |
| size += binarySize(ctrl.second); |
| |
| return size; |
| } |
| |
| void ControlSerializer::store(const ControlValue &value, |
| ByteStreamBuffer &buffer) |
| { |
| buffer.write(value.data()); |
| } |
| |
| void ControlSerializer::store(const ControlInfo &info, ByteStreamBuffer &buffer) |
| { |
| store(info.min(), buffer); |
| store(info.max(), buffer); |
| } |
| |
| /** |
| * \brief Serialize a ControlInfoMap in a buffer |
| * \param[in] infoMap The control info map to serialize |
| * \param[in] buffer The memory buffer where to serialize the ControlInfoMap |
| * |
| * Serialize the \a infoMap into the \a buffer using the serialization format |
| * defined by the IPA context interface in ipa_controls.h. |
| * |
| * The serializer stores a reference to the \a infoMap internally. The caller |
| * shall ensure that \a infoMap stays valid until the serializer is reset(). |
| * |
| * \return 0 on success, a negative error code otherwise |
| * \retval -ENOSPC Not enough space is available in the buffer |
| */ |
| int ControlSerializer::serialize(const ControlInfoMap &infoMap, |
| ByteStreamBuffer &buffer) |
| { |
| if (isCached(infoMap)) { |
| LOG(Serializer, Debug) |
| << "Skipping already serialized ControlInfoMap"; |
| return 0; |
| } |
| |
| /* Compute entries and data required sizes. */ |
| size_t entriesSize = infoMap.size() |
| * sizeof(struct ipa_control_info_entry); |
| size_t valuesSize = 0; |
| for (const auto &ctrl : infoMap) |
| valuesSize += binarySize(ctrl.second); |
| |
| /* Prepare the packet header, assign a handle to the ControlInfoMap. */ |
| struct ipa_controls_header hdr; |
| hdr.version = IPA_CONTROLS_FORMAT_VERSION; |
| hdr.handle = ++serial_; |
| hdr.entries = infoMap.size(); |
| hdr.size = sizeof(hdr) + entriesSize + valuesSize; |
| hdr.data_offset = sizeof(hdr) + entriesSize; |
| |
| buffer.write(&hdr); |
| |
| /* |
| * Serialize all entries. |
| * \todo Serialize the control name too |
| */ |
| ByteStreamBuffer entries = buffer.carveOut(entriesSize); |
| ByteStreamBuffer values = buffer.carveOut(valuesSize); |
| |
| for (const auto &ctrl : infoMap) { |
| const ControlId *id = ctrl.first; |
| const ControlInfo &info = ctrl.second; |
| |
| struct ipa_control_info_entry entry; |
| entry.id = id->id(); |
| entry.type = id->type(); |
| entry.offset = values.offset(); |
| entries.write(&entry); |
| |
| store(info, values); |
| } |
| |
| if (buffer.overflow()) |
| return -ENOSPC; |
| |
| /* |
| * Store the map to handle association, to be used to serialize and |
| * deserialize control lists. |
| */ |
| infoMapHandles_[&infoMap] = hdr.handle; |
| |
| return 0; |
| } |
| |
| /** |
| * \brief Serialize a ControlList in a buffer |
| * \param[in] list The control list to serialize |
| * \param[in] buffer The memory buffer where to serialize the ControlList |
| * |
| * Serialize the \a list into the \a buffer using the serialization format |
| * defined by the IPA context interface in ipa_controls.h. |
| * |
| * \return 0 on success, a negative error code otherwise |
| * \retval -ENOENT The ControlList is related to an unknown ControlInfoMap |
| * \retval -ENOSPC Not enough space is available in the buffer |
| */ |
| int ControlSerializer::serialize(const ControlList &list, |
| ByteStreamBuffer &buffer) |
| { |
| /* |
| * Find the ControlInfoMap handle for the ControlList if it has one, or |
| * use 0 for ControlList without a ControlInfoMap. |
| */ |
| unsigned int infoMapHandle; |
| if (list.infoMap()) { |
| auto iter = infoMapHandles_.find(list.infoMap()); |
| if (iter == infoMapHandles_.end()) { |
| LOG(Serializer, Error) |
| << "Can't serialize ControlList: unknown ControlInfoMap"; |
| return -ENOENT; |
| } |
| |
| infoMapHandle = iter->second; |
| } else { |
| infoMapHandle = 0; |
| } |
| |
| size_t entriesSize = list.size() * sizeof(struct ipa_control_value_entry); |
| size_t valuesSize = 0; |
| for (const auto &ctrl : list) |
| valuesSize += binarySize(ctrl.second); |
| |
| /* Prepare the packet header. */ |
| struct ipa_controls_header hdr; |
| hdr.version = IPA_CONTROLS_FORMAT_VERSION; |
| hdr.handle = infoMapHandle; |
| hdr.entries = list.size(); |
| hdr.size = sizeof(hdr) + entriesSize + valuesSize; |
| hdr.data_offset = sizeof(hdr) + entriesSize; |
| |
| buffer.write(&hdr); |
| |
| ByteStreamBuffer entries = buffer.carveOut(entriesSize); |
| ByteStreamBuffer values = buffer.carveOut(valuesSize); |
| |
| /* Serialize all entries. */ |
| for (const auto &ctrl : list) { |
| unsigned int id = ctrl.first; |
| const ControlValue &value = ctrl.second; |
| |
| struct ipa_control_value_entry entry; |
| entry.id = id; |
| entry.type = value.type(); |
| entry.is_array = value.isArray(); |
| entry.count = value.numElements(); |
| entry.offset = values.offset(); |
| entries.write(&entry); |
| |
| store(value, values); |
| } |
| |
| if (buffer.overflow()) |
| return -ENOSPC; |
| |
| return 0; |
| } |
| |
| ControlValue ControlSerializer::loadControlValue(ControlType type, |
| ByteStreamBuffer &buffer, |
| bool isArray, |
| unsigned int count) |
| { |
| ControlValue value; |
| |
| value.reserve(type, isArray, count); |
| buffer.read(value.data()); |
| |
| return value; |
| } |
| |
| ControlInfo ControlSerializer::loadControlInfo(ControlType type, |
| ByteStreamBuffer &b) |
| { |
| if (type == ControlTypeString) |
| type = ControlTypeInteger32; |
| |
| ControlValue min = loadControlValue(type, b); |
| ControlValue max = loadControlValue(type, b); |
| |
| return ControlInfo(min, max); |
| } |
| |
| /** |
| * \fn template<typename T> T ControlSerializer::deserialize(ByteStreamBuffer &buffer) |
| * \brief Deserialize an object from a binary buffer |
| * \param[in] buffer The memory buffer that contains the object |
| * |
| * This method is only valid when specialized for ControlInfoMap or |
| * ControlList. Any other typename \a T is not supported. |
| */ |
| |
| /** |
| * \brief Deserialize a ControlInfoMap from a binary buffer |
| * \param[in] buffer The memory buffer that contains the serialized map |
| * |
| * Re-construct a ControlInfoMap from a binary \a buffer containing data |
| * serialized using the serialize() method. |
| * |
| * \return The deserialized ControlInfoMap |
| */ |
| template<> |
| ControlInfoMap ControlSerializer::deserialize<ControlInfoMap>(ByteStreamBuffer &buffer) |
| { |
| const struct ipa_controls_header *hdr = buffer.read<decltype(*hdr)>(); |
| if (!hdr) { |
| LOG(Serializer, Error) << "Out of data"; |
| return {}; |
| } |
| |
| auto iter = infoMaps_.find(hdr->handle); |
| if (iter != infoMaps_.end()) { |
| LOG(Serializer, Debug) << "Use cached ControlInfoMap"; |
| return iter->second; |
| } |
| |
| if (hdr->version != IPA_CONTROLS_FORMAT_VERSION) { |
| LOG(Serializer, Error) |
| << "Unsupported controls format version " |
| << hdr->version; |
| return {}; |
| } |
| |
| ByteStreamBuffer entries = buffer.carveOut(hdr->data_offset - sizeof(*hdr)); |
| ByteStreamBuffer values = buffer.carveOut(hdr->size - hdr->data_offset); |
| |
| if (buffer.overflow()) { |
| LOG(Serializer, Error) << "Out of data"; |
| return {}; |
| } |
| |
| ControlInfoMap::Map ctrls; |
| |
| for (unsigned int i = 0; i < hdr->entries; ++i) { |
| const struct ipa_control_info_entry *entry = |
| entries.read<decltype(*entry)>(); |
| if (!entry) { |
| LOG(Serializer, Error) << "Out of data"; |
| return {}; |
| } |
| |
| /* Create and cache the individual ControlId. */ |
| ControlType type = static_cast<ControlType>(entry->type); |
| /** |
| * \todo Find a way to preserve the control name for debugging |
| * purpose. |
| */ |
| controlIds_.emplace_back(std::make_unique<ControlId>(entry->id, "", type)); |
| |
| if (entry->offset != values.offset()) { |
| LOG(Serializer, Error) |
| << "Bad data, entry offset mismatch (entry " |
| << i << ")"; |
| return {}; |
| } |
| |
| /* Create and store the ControlInfo. */ |
| ctrls.emplace(controlIds_.back().get(), |
| loadControlInfo(type, values)); |
| } |
| |
| /* |
| * Create the ControlInfoMap in the cache, and store the map to handle |
| * association. |
| */ |
| ControlInfoMap &map = infoMaps_[hdr->handle] = std::move(ctrls); |
| infoMapHandles_[&map] = hdr->handle; |
| |
| return map; |
| } |
| |
| /** |
| * \brief Deserialize a ControlList from a binary buffer |
| * \param[in] buffer The memory buffer that contains the serialized list |
| * |
| * Re-construct a ControlList from a binary \a buffer containing data |
| * serialized using the serialize() method. |
| * |
| * \return The deserialized ControlList |
| */ |
| template<> |
| ControlList ControlSerializer::deserialize<ControlList>(ByteStreamBuffer &buffer) |
| { |
| const struct ipa_controls_header *hdr = buffer.read<decltype(*hdr)>(); |
| if (!hdr) { |
| LOG(Serializer, Error) << "Out of data"; |
| return {}; |
| } |
| |
| if (hdr->version != IPA_CONTROLS_FORMAT_VERSION) { |
| LOG(Serializer, Error) |
| << "Unsupported controls format version " |
| << hdr->version; |
| return {}; |
| } |
| |
| ByteStreamBuffer entries = buffer.carveOut(hdr->data_offset - sizeof(*hdr)); |
| ByteStreamBuffer values = buffer.carveOut(hdr->size - hdr->data_offset); |
| |
| if (buffer.overflow()) { |
| LOG(Serializer, Error) << "Out of data"; |
| return {}; |
| } |
| |
| /* |
| * Retrieve the ControlInfoMap associated with the ControlList based on |
| * its ID. The mapping between infoMap and ID is set up when serializing |
| * or deserializing ControlInfoMap. If no mapping is found (which is |
| * currently the case for ControlList related to libcamera controls), |
| * use the global control::control idmap. |
| */ |
| const ControlInfoMap *infoMap; |
| if (hdr->handle) { |
| auto iter = std::find_if(infoMapHandles_.begin(), infoMapHandles_.end(), |
| [&](decltype(infoMapHandles_)::value_type &entry) { |
| return entry.second == hdr->handle; |
| }); |
| if (iter == infoMapHandles_.end()) { |
| LOG(Serializer, Error) |
| << "Can't deserialize ControlList: unknown ControlInfoMap"; |
| return {}; |
| } |
| |
| infoMap = iter->first; |
| } else { |
| infoMap = nullptr; |
| } |
| |
| ControlList ctrls(infoMap ? infoMap->idmap() : controls::controls); |
| |
| for (unsigned int i = 0; i < hdr->entries; ++i) { |
| const struct ipa_control_value_entry *entry = |
| entries.read<decltype(*entry)>(); |
| if (!entry) { |
| LOG(Serializer, Error) << "Out of data"; |
| return {}; |
| } |
| |
| if (entry->offset != values.offset()) { |
| LOG(Serializer, Error) |
| << "Bad data, entry offset mismatch (entry " |
| << i << ")"; |
| return {}; |
| } |
| |
| ControlType type = static_cast<ControlType>(entry->type); |
| ctrls.set(entry->id, |
| loadControlValue(type, values, entry->is_array, |
| entry->count)); |
| } |
| |
| return ctrls; |
| } |
| |
| /** |
| * \brief Check if a ControlInfoMap is cached |
| * \param[in] infoMap The ControlInfoMap to check |
| * |
| * The ControlSerializer caches all ControlInfoMaps that it has (de)serialized. |
| * This function checks if \a infoMap is in the cache. |
| * |
| * \return True if \a infoMap is in the cache or false otherwise |
| */ |
| bool ControlSerializer::isCached(const ControlInfoMap &infoMap) |
| { |
| return infoMapHandles_.count(&infoMap); |
| } |
| |
| } /* namespace libcamera */ |