Accept empty `ControlList`s without an info map. Serialization
supports `ControlList`s without an associated `ControlInfoMap`
object. However, for deserialization, a "v4l2" control list
without an info map resulted in a fatal error.
After 30114cadd8 ("ipa: rpi: Defer initialising AF LensPosition ControlInfo and value")
the `lensControls` member of `ipa::RPi::ConfigResult` is left empty
and without an info map in every case, not just when a lens is not present.
This causes the above assertion to trigger every single time.
Also fix the indefinite article of "a ControlInfoMap" in the
error message.
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
657 lines
19 KiB
C++
657 lines
19 KiB
C++
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
/*
|
|
* Copyright (C) 2019, Google Inc.
|
|
*
|
|
* Control (de)serializer
|
|
*/
|
|
|
|
#include "libcamera/internal/control_serializer.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <libcamera/base/log.h>
|
|
#include <libcamera/base/span.h>
|
|
|
|
#include <libcamera/control_ids.h>
|
|
#include <libcamera/controls.h>
|
|
#include <libcamera/property_ids.h>
|
|
|
|
#include <libcamera/ipa/ipa_controls.h>
|
|
|
|
#include "libcamera/internal/byte_stream_buffer.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.
|
|
*
|
|
* As independent ControlSerializer instances are used on both sides of the IPC
|
|
* boundary, and the two instances operate without a shared point of control,
|
|
* there is a potential risk of collision of the numerical handles assigned to
|
|
* each serialized ControlInfoMap. For this reason the control serializer is
|
|
* initialized with a seed and the handle is incremented by 2, so that instances
|
|
* initialized with a different seed operate on a separate numerical space,
|
|
* avoiding any collision risk.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* \enum ControlSerializer::Role
|
|
* \brief Define the role of the IPC component using the control serializer
|
|
*
|
|
* The role of the component that creates the serializer is used to initialize
|
|
* the handles numerical space.
|
|
*
|
|
* \var ControlSerializer::Role::Proxy
|
|
* \brief The control serializer is used by the IPC Proxy classes
|
|
*
|
|
* \var ControlSerializer::Role::Worker
|
|
* \brief The control serializer is used by the IPC ProxyWorker classes
|
|
*/
|
|
|
|
/**
|
|
* \brief Construct a new ControlSerializer
|
|
* \param[in] role The role of the IPC component using the serializer
|
|
*/
|
|
ControlSerializer::ControlSerializer(Role role)
|
|
{
|
|
/*
|
|
* Initialize the handle numerical space using the role of the
|
|
* component that created the instance.
|
|
*
|
|
* Instances initialized for a different role will use a different
|
|
* numerical handle space, avoiding any collision risk when, in example,
|
|
* two instances of the ControlSerializer class are used at the IPC
|
|
* boundaries.
|
|
*
|
|
* Start counting handles from '1' as '0' is a special value used as
|
|
* place holder when serializing lists that do not have a ControlInfoMap
|
|
* associated (in example list of libcamera controls::controls).
|
|
*
|
|
* \todo This is a temporary hack and should probably be better
|
|
* engineered, but for the time being it avoids collisions on the handle
|
|
* value when using IPC.
|
|
*/
|
|
serialSeed_ = role == Role::Proxy ? 1 : 2;
|
|
serial_ = serialSeed_;
|
|
}
|
|
|
|
/**
|
|
* \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_ = serialSeed_;
|
|
|
|
infoMapHandles_.clear();
|
|
infoMaps_.clear();
|
|
controlIds_.clear();
|
|
controlIdMaps_.clear();
|
|
}
|
|
|
|
size_t ControlSerializer::binarySize(const ControlValue &value)
|
|
{
|
|
return sizeof(ControlType) + value.data().size_bytes();
|
|
}
|
|
|
|
size_t ControlSerializer::binarySize(const ControlInfo &info)
|
|
{
|
|
return binarySize(info.min()) + binarySize(info.max()) + binarySize(info.def());
|
|
}
|
|
|
|
/**
|
|
* \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)
|
|
{
|
|
const ControlType type = value.type();
|
|
buffer.write(&type);
|
|
buffer.write(value.data());
|
|
}
|
|
|
|
void ControlSerializer::store(const ControlInfo &info, ByteStreamBuffer &buffer)
|
|
{
|
|
store(info.min(), buffer);
|
|
store(info.max(), buffer);
|
|
store(info.def(), 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);
|
|
|
|
const ControlIdMap *idmap = &infoMap.idmap();
|
|
enum ipa_controls_id_map_type idMapType;
|
|
if (idmap == &controls::controls)
|
|
idMapType = IPA_CONTROL_ID_MAP_CONTROLS;
|
|
else if (idmap == &properties::properties)
|
|
idMapType = IPA_CONTROL_ID_MAP_PROPERTIES;
|
|
else
|
|
idMapType = IPA_CONTROL_ID_MAP_V4L2;
|
|
|
|
/* Prepare the packet header. */
|
|
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;
|
|
hdr.id_map_type = idMapType;
|
|
|
|
buffer.write(&hdr);
|
|
|
|
/*
|
|
* Increment the handle for the ControlInfoMap by 2 to keep the handles
|
|
* numerical space partitioned between instances initialized for a
|
|
* different role.
|
|
*
|
|
* \sa ControlSerializer::Role
|
|
*/
|
|
serial_ += 2;
|
|
|
|
/*
|
|
* 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();
|
|
entry.direction = static_cast<ControlId::DirectionFlags::Type>(id->direction());
|
|
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;
|
|
}
|
|
|
|
const ControlIdMap *idmap = list.idMap();
|
|
enum ipa_controls_id_map_type idMapType;
|
|
if (idmap == &controls::controls)
|
|
idMapType = IPA_CONTROL_ID_MAP_CONTROLS;
|
|
else if (idmap == &properties::properties)
|
|
idMapType = IPA_CONTROL_ID_MAP_PROPERTIES;
|
|
else
|
|
idMapType = IPA_CONTROL_ID_MAP_V4L2;
|
|
|
|
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;
|
|
hdr.id_map_type = idMapType;
|
|
|
|
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(ByteStreamBuffer &buffer,
|
|
bool isArray,
|
|
unsigned int count)
|
|
{
|
|
ControlType type;
|
|
buffer.read(&type);
|
|
|
|
ControlValue value;
|
|
|
|
value.reserve(type, isArray, count);
|
|
buffer.read(value.data());
|
|
|
|
return value;
|
|
}
|
|
|
|
ControlInfo ControlSerializer::loadControlInfo(ByteStreamBuffer &b)
|
|
{
|
|
ControlValue min = loadControlValue(b);
|
|
ControlValue max = loadControlValue(b);
|
|
ControlValue def = loadControlValue(b);
|
|
|
|
return ControlInfo(min, max, def);
|
|
}
|
|
|
|
/**
|
|
* \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 function 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() function.
|
|
*
|
|
* \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 {};
|
|
}
|
|
|
|
/*
|
|
* Use the ControlIdMap corresponding to the id map type. If the type
|
|
* references a globally defined id map (such as controls::controls
|
|
* or properties::properties), use it. Otherwise, create a local id map
|
|
* that will be populated with dynamically created ControlId instances
|
|
* when deserializing individual ControlInfoMap entries.
|
|
*/
|
|
const ControlIdMap *idMap = nullptr;
|
|
ControlIdMap *localIdMap = nullptr;
|
|
switch (hdr->id_map_type) {
|
|
case IPA_CONTROL_ID_MAP_CONTROLS:
|
|
idMap = &controls::controls;
|
|
break;
|
|
case IPA_CONTROL_ID_MAP_PROPERTIES:
|
|
idMap = &properties::properties;
|
|
break;
|
|
case IPA_CONTROL_ID_MAP_V4L2:
|
|
controlIdMaps_.emplace_back(std::make_unique<ControlIdMap>());
|
|
localIdMap = controlIdMaps_.back().get();
|
|
idMap = localIdMap;
|
|
break;
|
|
default:
|
|
LOG(Serializer, Error)
|
|
<< "Unknown id map type: " << hdr->id_map_type;
|
|
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 {};
|
|
}
|
|
|
|
ControlType type = static_cast<ControlType>(entry->type);
|
|
|
|
/* If we're using a local id map, populate it. */
|
|
if (localIdMap) {
|
|
ControlId::DirectionFlags flags{
|
|
static_cast<ControlId::Direction>(entry->direction)
|
|
};
|
|
|
|
/**
|
|
* \todo Find a way to preserve the control name for
|
|
* debugging purpose.
|
|
*/
|
|
controlIds_.emplace_back(std::make_unique<ControlId>(entry->id,
|
|
"", "local", type,
|
|
flags));
|
|
(*localIdMap)[entry->id] = controlIds_.back().get();
|
|
}
|
|
|
|
const ControlId *controlId = idMap->at(entry->id);
|
|
ASSERT(controlId);
|
|
|
|
if (entry->offset != values.offset()) {
|
|
LOG(Serializer, Error)
|
|
<< "Bad data, entry offset mismatch (entry "
|
|
<< i << ")";
|
|
return {};
|
|
}
|
|
|
|
/* Create and store the ControlInfo. */
|
|
ctrls.emplace(controlId, loadControlInfo(values));
|
|
}
|
|
|
|
/*
|
|
* Create the ControlInfoMap in the cache, and store the map to handle
|
|
* association.
|
|
*/
|
|
infoMaps_[hdr->handle] = ControlInfoMap(std::move(ctrls), *idMap);
|
|
ControlInfoMap &map = infoMaps_[hdr->handle];
|
|
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() function.
|
|
*
|
|
* \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 ControlIdMap associated with the ControlList.
|
|
*
|
|
* The idmap is either retrieved from the list's ControlInfoMap when
|
|
* a valid handle has been initialized at serialization time, or by
|
|
* using the header's id_map_type field for lists that refer to the
|
|
* globally defined libcamera controls and properties, for which no
|
|
* ControlInfoMap is available.
|
|
*/
|
|
const ControlIdMap *idMap;
|
|
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 {};
|
|
}
|
|
|
|
const ControlInfoMap *infoMap = iter->first;
|
|
idMap = &infoMap->idmap();
|
|
} else {
|
|
switch (hdr->id_map_type) {
|
|
case IPA_CONTROL_ID_MAP_CONTROLS:
|
|
idMap = &controls::controls;
|
|
break;
|
|
|
|
case IPA_CONTROL_ID_MAP_PROPERTIES:
|
|
idMap = &properties::properties;
|
|
break;
|
|
|
|
case IPA_CONTROL_ID_MAP_V4L2:
|
|
default:
|
|
if (hdr->entries > 0)
|
|
LOG(Serializer, Fatal)
|
|
<< "A list of V4L2 controls requires a ControlInfoMap";
|
|
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/*
|
|
* \todo When available, initialize the list with the ControlInfoMap
|
|
* so that controls can be validated against their limits.
|
|
* Currently no validation is performed, so it's fine relying on the
|
|
* idmap only.
|
|
*/
|
|
ControlList ctrls(*idMap);
|
|
|
|
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 {};
|
|
}
|
|
|
|
ctrls.set(entry->id,
|
|
loadControlValue(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 */
|