{"id":14832,"url":"https://patchwork.libcamera.org/api/1.1/patches/14832/?format=json","web_url":"https://patchwork.libcamera.org/patch/14832/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>","date":"2021-11-28T23:57:52","name":"[libcamera-devel,v3,17/17] libcamera: base: Rename FileDescriptor to SharedFD","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"9ff8e43bd35ff96f0314a99211547f847d77d6e2","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/1.1/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/14832/mbox/","series":[{"id":2768,"url":"https://patchwork.libcamera.org/api/1.1/series/2768/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=2768","date":"2021-11-28T23:57:35","name":"libcamera: Introduce UniqueFD","version":3,"mbox":"https://patchwork.libcamera.org/series/2768/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/14832/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/14832/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CD33CC3252\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 28 Nov 2021 23:58:41 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 76999605B6;\n\tMon, 29 Nov 2021 00:58:41 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0010E605C5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Nov 2021 00:58:28 +0100 (CET)","from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 56AAC730;\n\tMon, 29 Nov 2021 00:58:28 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"p6R/N11z\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1638143908;\n\tbh=pB/M0LO/tbEsyyRtbOBUvJ8JrbbIWL3/jelq6Q9LXpE=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=p6R/N11zwf3HbRqdtNNiyI3zdMAEL6zHs28DydT0mmxaSLbnsLrrjnVtTnFw9aouY\n\tjhqoP+ljbCuJCExwW1V/vHS10g0c8wJj3FlTEJsCpdsBYhHSo5l1l52fodvCSXDawp\n\tJRrExk5W0SMMZXYeB3hG0ro70okBT+zZUE9OZ7Ws=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Mon, 29 Nov 2021 01:57:52 +0200","Message-Id":"<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.32.0","In-Reply-To":"<20211128235752.10836-1-laurent.pinchart@ideasonboard.com>","References":"<20211128235752.10836-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.\nRename it to SharedFD.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n include/libcamera/base/file.h                 |   4 +-\n include/libcamera/base/meson.build            |   2 +-\n .../base/{file_descriptor.h => shared_fd.h}   |  20 +-\n include/libcamera/framebuffer.h               |   4 +-\n .../libcamera/internal/ipa_data_serializer.h  |  40 +--\n include/libcamera/internal/ipc_pipe.h         |   8 +-\n include/libcamera/ipa/core.mojom              |   6 +-\n include/libcamera/ipa/raspberrypi.mojom       |   2 +-\n src/android/camera_device.cpp                 |   2 +-\n src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-\n src/libcamera/base/file.cpp                   |   6 +-\n src/libcamera/base/file_descriptor.cpp        | 266 ------------------\n src/libcamera/base/meson.build                |   2 +-\n src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++\n src/libcamera/framebuffer.cpp                 |   6 +-\n src/libcamera/ipa_data_serializer.cpp         | 100 +++----\n src/libcamera/ipc_pipe.cpp                    |   4 +-\n .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-\n src/libcamera/v4l2_videodevice.cpp            |   6 +-\n src/v4l2/v4l2_camera.h                        |   2 +-\n test/meson.build                              |   2 +-\n .../ipa_data_serializer_test.cpp              |  14 +-\n test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--\n .../module_ipa_proxy.cpp.tmpl                 |   2 +-\n .../module_ipa_proxy.h.tmpl                   |   2 +-\n .../libcamera_templates/proxy_functions.tmpl  |   2 +-\n .../libcamera_templates/serializer.tmpl       |  22 +-\n .../generators/mojom_libcamera_generator.py   |   6 +-\n 28 files changed, 422 insertions(+), 426 deletions(-)\n rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)\n delete mode 100644 src/libcamera/base/file_descriptor.cpp\n create mode 100644 src/libcamera/base/shared_fd.cpp\n rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)","diff":"diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h\nindex 47769da7abc2..691b52d6ab2d 100644\n--- a/include/libcamera/base/file.h\n+++ b/include/libcamera/base/file.h\n@@ -21,7 +21,7 @@\n \n namespace libcamera {\n \n-class FileDescriptor;\n+class SharedFD;\n \n class File\n {\n@@ -69,7 +69,7 @@ public:\n \tbool unmap(uint8_t *addr);\n \n \tstatic bool exists(const std::string &name);\n-\tstatic ino_t inode(const FileDescriptor &fd);\n+\tstatic ino_t inode(const SharedFD &fd);\n \n private:\n \tLIBCAMERA_DISABLE_COPY(File)\ndiff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build\nindex cca374a769cc..112420dab225 100644\n--- a/include/libcamera/base/meson.build\n+++ b/include/libcamera/base/meson.build\n@@ -11,13 +11,13 @@ libcamera_base_headers = files([\n     'event_dispatcher_poll.h',\n     'event_notifier.h',\n     'file.h',\n-    'file_descriptor.h',\n     'flags.h',\n     'log.h',\n     'message.h',\n     'object.h',\n     'private.h',\n     'semaphore.h',\n+    'shared_fd.h',\n     'signal.h',\n     'span.h',\n     'thread.h',\ndiff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h\nsimilarity index 55%\nrename from include/libcamera/base/file_descriptor.h\nrename to include/libcamera/base/shared_fd.h\nindex 12a43f95d414..a786885ceb32 100644\n--- a/include/libcamera/base/file_descriptor.h\n+++ b/include/libcamera/base/shared_fd.h\n@@ -2,7 +2,7 @@\n /*\n  * Copyright (C) 2019, Google Inc.\n  *\n- * file_descriptor.h - File descriptor wrapper\n+ * shared_fd.h - File descriptor wrapper with shared ownership\n  */\n \n #pragma once\n@@ -13,18 +13,18 @@ namespace libcamera {\n \n class UniqueFD;\n \n-class FileDescriptor final\n+class SharedFD final\n {\n public:\n-\texplicit FileDescriptor(const int &fd = -1);\n-\texplicit FileDescriptor(int &&fd);\n-\texplicit FileDescriptor(UniqueFD fd);\n-\tFileDescriptor(const FileDescriptor &other);\n-\tFileDescriptor(FileDescriptor &&other);\n-\t~FileDescriptor();\n+\texplicit SharedFD(const int &fd = -1);\n+\texplicit SharedFD(int &&fd);\n+\texplicit SharedFD(UniqueFD fd);\n+\tSharedFD(const SharedFD &other);\n+\tSharedFD(SharedFD &&other);\n+\t~SharedFD();\n \n-\tFileDescriptor &operator=(const FileDescriptor &other);\n-\tFileDescriptor &operator=(FileDescriptor &&other);\n+\tSharedFD &operator=(const SharedFD &other);\n+\tSharedFD &operator=(SharedFD &&other);\n \n \tbool isValid() const { return fd_ != nullptr; }\n \tint fd() const { return fd_ ? fd_->fd() : -1; }\ndiff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\nindex 2fbea9c5be16..357bbe189551 100644\n--- a/include/libcamera/framebuffer.h\n+++ b/include/libcamera/framebuffer.h\n@@ -13,7 +13,7 @@\n #include <vector>\n \n #include <libcamera/base/class.h>\n-#include <libcamera/base/file_descriptor.h>\n+#include <libcamera/base/shared_fd.h>\n #include <libcamera/base/span.h>\n \n namespace libcamera {\n@@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible\n public:\n \tstruct Plane {\n \t\tstatic constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();\n-\t\tFileDescriptor fd;\n+\t\tSharedFD fd;\n \t\tunsigned int offset = kInvalidOffset;\n \t\tunsigned int length;\n \t};\ndiff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\nindex c2f602d5b7de..a87449c9be48 100644\n--- a/include/libcamera/internal/ipa_data_serializer.h\n+++ b/include/libcamera/internal/ipa_data_serializer.h\n@@ -66,7 +66,7 @@ template<typename T>\n class IPADataSerializer\n {\n public:\n-\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n \tserialize(const T &data, ControlSerializer *cs = nullptr);\n \n \tstatic T deserialize(const std::vector<uint8_t> &data,\n@@ -76,12 +76,12 @@ public:\n \t\t\t     ControlSerializer *cs = nullptr);\n \n \tstatic T deserialize(const std::vector<uint8_t> &data,\n-\t\t\t     const std::vector<FileDescriptor> &fds,\n+\t\t\t     const std::vector<SharedFD> &fds,\n \t\t\t     ControlSerializer *cs = nullptr);\n \tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t     std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t     std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t\t     std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t     std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t\t     ControlSerializer *cs = nullptr);\n };\n \n@@ -104,11 +104,11 @@ template<typename V>\n class IPADataSerializer<std::vector<V>>\n {\n public:\n-\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n \tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n \t{\n \t\tstd::vector<uint8_t> dataVec;\n-\t\tstd::vector<FileDescriptor> fdsVec;\n+\t\tstd::vector<SharedFD> fdsVec;\n \n \t\t/* Serialize the length. */\n \t\tuint32_t vecLen = data.size();\n@@ -117,7 +117,7 @@ public:\n \t\t/* Serialize the members. */\n \t\tfor (auto const &it : data) {\n \t\t\tstd::vector<uint8_t> dvec;\n-\t\t\tstd::vector<FileDescriptor> fvec;\n+\t\t\tstd::vector<SharedFD> fvec;\n \n \t\t\tstd::tie(dvec, fvec) =\n \t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n@@ -141,11 +141,11 @@ public:\n \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n \t\t\t\t\t  ControlSerializer *cs = nullptr)\n \t{\n-\t\tstd::vector<FileDescriptor> fds;\n+\t\tstd::vector<SharedFD> fds;\n \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n \t}\n \n-\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n+\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n \t\t\t\t\t  ControlSerializer *cs = nullptr)\n \t{\n \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n@@ -153,15 +153,15 @@ public:\n \n \tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t\t\t\t  ControlSerializer *cs = nullptr)\n \t{\n \t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n \t\tstd::vector<V> ret(vecLen);\n \n \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n-\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n+\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n \t\tfor (uint32_t i = 0; i < vecLen; i++) {\n \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n@@ -201,11 +201,11 @@ template<typename K, typename V>\n class IPADataSerializer<std::map<K, V>>\n {\n public:\n-\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n \tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n \t{\n \t\tstd::vector<uint8_t> dataVec;\n-\t\tstd::vector<FileDescriptor> fdsVec;\n+\t\tstd::vector<SharedFD> fdsVec;\n \n \t\t/* Serialize the length. */\n \t\tuint32_t mapLen = data.size();\n@@ -214,7 +214,7 @@ public:\n \t\t/* Serialize the members. */\n \t\tfor (auto const &it : data) {\n \t\t\tstd::vector<uint8_t> dvec;\n-\t\t\tstd::vector<FileDescriptor> fvec;\n+\t\t\tstd::vector<SharedFD> fvec;\n \n \t\t\tstd::tie(dvec, fvec) =\n \t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n@@ -247,11 +247,11 @@ public:\n \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n \t\t\t\t\t  ControlSerializer *cs = nullptr)\n \t{\n-\t\tstd::vector<FileDescriptor> fds;\n+\t\tstd::vector<SharedFD> fds;\n \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n \t}\n \n-\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n+\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n \t\t\t\t\t  ControlSerializer *cs = nullptr)\n \t{\n \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n@@ -259,8 +259,8 @@ public:\n \n \tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t\t\t\t  ControlSerializer *cs = nullptr)\n \t{\n \t\tstd::map<K, V> ret;\n@@ -268,7 +268,7 @@ public:\n \t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n \n \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n-\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n+\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n \t\tfor (uint32_t i = 0; i < mapLen; i++) {\n \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\ndiff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h\nindex 986f8d886fa6..ab5dd67c3813 100644\n--- a/include/libcamera/internal/ipc_pipe.h\n+++ b/include/libcamera/internal/ipc_pipe.h\n@@ -9,7 +9,7 @@\n \n #include <vector>\n \n-#include <libcamera/base/file_descriptor.h>\n+#include <libcamera/base/shared_fd.h>\n #include <libcamera/base/signal.h>\n \n #include \"libcamera/internal/ipc_unixsocket.h\"\n@@ -33,17 +33,17 @@ public:\n \n \tHeader &header() { return header_; }\n \tstd::vector<uint8_t> &data() { return data_; }\n-\tstd::vector<FileDescriptor> &fds() { return fds_; }\n+\tstd::vector<SharedFD> &fds() { return fds_; }\n \n \tconst Header &header() const { return header_; }\n \tconst std::vector<uint8_t> &data() const { return data_; }\n-\tconst std::vector<FileDescriptor> &fds() const { return fds_; }\n+\tconst std::vector<SharedFD> &fds() const { return fds_; }\n \n private:\n \tHeader header_;\n \n \tstd::vector<uint8_t> data_;\n-\tstd::vector<FileDescriptor> fds_;\n+\tstd::vector<SharedFD> fds_;\n };\n \n class IPCPipe\ndiff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom\nindex f7eff0c7ab8c..74f3339e56f2 100644\n--- a/include/libcamera/ipa/core.mojom\n+++ b/include/libcamera/ipa/core.mojom\n@@ -32,7 +32,7 @@ module libcamera;\n  *   - This attribute instructs the build system that a (de)serializer is\n  *     available for the type and there's no need to generate one\n  * - hasFd - struct fields or empty structs only\n- *   - Designate that this field or empty struct contains a FileDescriptor\n+ *   - Designate that this field or empty struct contains a SharedFD\n  *\n  * Rules:\n  * - If the type is defined in a libcamera C++ header *and* a (de)serializer is\n@@ -60,7 +60,7 @@ module libcamera;\n  *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array\n  *       member\n  * - [skipHeader] and [skipSerdes] only work here in core.mojom\n- * - If a field in a struct has a FileDescriptor, but is not explicitly\n+ * - If a field in a struct has a SharedFD, but is not explicitly\n  *   defined so in mojom, then the field must be marked with the [hasFd]\n  *   attribute\n  *\n@@ -71,7 +71,7 @@ module libcamera;\n  */\n [skipSerdes, skipHeader] struct ControlInfoMap {};\n [skipSerdes, skipHeader] struct ControlList {};\n-[skipSerdes, skipHeader] struct FileDescriptor {};\n+[skipSerdes, skipHeader] struct SharedFD {};\n \n [skipHeader] struct Point {\n \tint32 x;\ndiff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\nindex e453d46cb14f..acd3cafe6c91 100644\n--- a/include/libcamera/ipa/raspberrypi.mojom\n+++ b/include/libcamera/ipa/raspberrypi.mojom\n@@ -35,7 +35,7 @@ struct ISPConfig {\n \n struct IPAConfig {\n \tuint32 transform;\n-\tlibcamera.FileDescriptor lsTableHandle;\n+\tlibcamera.SharedFD lsTableHandle;\n };\n \n struct StartConfig {\ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex f2e0bdbdbbf6..1938b10509fa 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,\n \n \tstd::vector<FrameBuffer::Plane> planes(buf.numPlanes());\n \tfor (size_t i = 0; i < buf.numPlanes(); ++i) {\n-\t\tFileDescriptor fd{ camera3buffer->data[i] };\n+\t\tSharedFD fd{ camera3buffer->data[i] };\n \t\tif (!fd.isValid()) {\n \t\t\tLOG(HAL, Fatal) << \"No valid fd\";\n \t\t\treturn nullptr;\ndiff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\nindex c6aec09046f7..aaf629eeb3fc 100644\n--- a/src/ipa/raspberrypi/raspberrypi.cpp\n+++ b/src/ipa/raspberrypi/raspberrypi.cpp\n@@ -15,8 +15,8 @@\n \n #include <linux/bcm2835-isp.h>\n \n-#include <libcamera/base/file_descriptor.h>\n #include <libcamera/base/log.h>\n+#include <libcamera/base/shared_fd.h>\n #include <libcamera/base/span.h>\n \n #include <libcamera/control_ids.h>\n@@ -164,7 +164,7 @@ private:\n \tbool processPending_;\n \n \t/* LS table allocation passed in from the pipeline handler. */\n-\tFileDescriptor lsTableHandle_;\n+\tSharedFD lsTableHandle_;\n \tvoid *lsTable_;\n \n \t/* Distinguish the first camera start from others. */\ndiff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp\nindex 66c73c406198..3ca9839bb989 100644\n--- a/src/libcamera/base/file.cpp\n+++ b/src/libcamera/base/file.cpp\n@@ -14,8 +14,8 @@\n #include <sys/types.h>\n #include <unistd.h>\n \n-#include <libcamera/base/file_descriptor.h>\n #include <libcamera/base/log.h>\n+#include <libcamera/base/shared_fd.h>\n \n /**\n  * \\file base/file.h\n@@ -473,11 +473,11 @@ bool File::exists(const std::string &name)\n }\n \n /**\n- * \\brief Retrieve the inode of a FileDescriptor\n+ * \\brief Retrieve the inode of a SharedFD\n  *\n  * \\return The file descriptor inode on success, or 0 on error\n  */\n-ino_t File::inode(const FileDescriptor &fd)\n+ino_t File::inode(const SharedFD &fd)\n {\n \tif (!fd.isValid())\n \t\treturn 0;\ndiff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp\ndeleted file mode 100644\nindex a83bf52c31e6..000000000000\n--- a/src/libcamera/base/file_descriptor.cpp\n+++ /dev/null\n@@ -1,266 +0,0 @@\n-/* SPDX-License-Identifier: LGPL-2.1-or-later */\n-/*\n- * Copyright (C) 2019, Google Inc.\n- *\n- * file_descriptor.cpp - File descriptor wrapper\n- */\n-\n-#include <libcamera/base/file_descriptor.h>\n-\n-#include <string.h>\n-#include <sys/types.h>\n-#include <unistd.h>\n-#include <utility>\n-\n-#include <libcamera/base/log.h>\n-#include <libcamera/base/unique_fd.h>\n-\n-/**\n- * \\file base/file_descriptor.h\n- * \\brief File descriptor wrapper\n- */\n-\n-namespace libcamera {\n-\n-LOG_DEFINE_CATEGORY(FileDescriptor)\n-\n-/**\n- * \\class FileDescriptor\n- * \\brief RAII-style wrapper for file descriptors\n- *\n- * The FileDescriptor class provides RAII-style lifetime management of file\n- * descriptors with an efficient mechanism for ownership sharing. At its core,\n- * an internal Descriptor object wraps a file descriptor (expressed as a signed\n- * integer) with an RAII-style interface. The Descriptor is then implicitly\n- * shared with all FileDescriptor instances constructed as copies.\n- *\n- * When constructed from a numerical file descriptor, the FileDescriptor\n- * instance either duplicates or takes over the file descriptor:\n- *\n- * - The FileDescriptor(const int &) constructor duplicates the numerical file\n- *   descriptor and wraps the duplicate in a Descriptor. The caller is\n- *   responsible for closing the original file descriptor, and the value\n- *   returned by fd() will be different from the value passed to the\n- *   constructor.\n- *\n- * - The FileDescriptor(int &&) constructor takes over the numerical file\n- *   descriptor and wraps it in a Descriptor. The caller shall not touch the\n- *   original file descriptor once the function returns, and the value returned\n- *   by fd() will be identical to the value passed to the constructor.\n- *\n- * The copy constructor and assignment operator create copies that share the\n- * Descriptor, while the move versions of those functions additionally make the\n- * other FileDescriptor invalid. When the last FileDescriptor that references a\n- * Descriptor is destroyed, the file descriptor is closed.\n- *\n- * The numerical file descriptor is available through the fd() function. All\n- * FileDescriptor instances created as copies of a FileDescriptor will report\n- * the same fd() value. Callers can perform operations on the fd(), but shall\n- * never close it manually.\n- */\n-\n-/**\n- * \\brief Create a FileDescriptor copying a given \\a fd\n- * \\param[in] fd File descriptor\n- *\n- * Construct a FileDescriptor from a numerical file descriptor by duplicating\n- * the \\a fd, and take ownership of the copy. The original \\a fd is left\n- * untouched, and the caller is responsible for closing it when appropriate.\n- * The duplicated file descriptor will be closed automatically when all\n- * FileDescriptor instances that reference it are destroyed.\n- *\n- * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n- * the fd() function will return -1.\n- */\n-FileDescriptor::FileDescriptor(const int &fd)\n-{\n-\tif (fd < 0)\n-\t\treturn;\n-\n-\tfd_ = std::make_shared<Descriptor>(fd, true);\n-\tif (fd_->fd() < 0)\n-\t\tfd_.reset();\n-}\n-\n-/**\n- * \\brief Create a FileDescriptor taking ownership of a given \\a fd\n- * \\param[in] fd File descriptor\n- *\n- * Construct a FileDescriptor from a numerical file descriptor by taking\n- * ownership of the \\a fd. The original \\a fd is set to -1 and shall not be\n- * touched by the caller anymore. In particular, the caller shall not close the\n- * original \\a fd manually. The duplicated file descriptor will be closed\n- * automatically when all FileDescriptor instances that reference it are\n- * destroyed.\n- *\n- * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n- * the fd() function will return -1.\n- */\n-FileDescriptor::FileDescriptor(int &&fd)\n-{\n-\tif (fd < 0)\n-\t\treturn;\n-\n-\tfd_ = std::make_shared<Descriptor>(fd, false);\n-\t/*\n-\t * The Descriptor constructor can't have failed here, as it took over\n-\t * the fd without duplicating it. Just set the original fd to -1 to\n-\t * implement move semantics.\n-\t */\n-\tfd = -1;\n-}\n-\n-/**\n- * \\brief Create a FileDescriptor taking ownership of a given UniqueFD \\a fd\n- * \\param[in] fd UniqueFD\n- *\n- * Construct a FileDescriptor from UniqueFD by taking ownership of the \\a fd.\n- * The original \\a fd becomes invalid.\n- */\n-FileDescriptor::FileDescriptor(UniqueFD fd)\n-\t: FileDescriptor(fd.release())\n-{\n-}\n-\n-/**\n- * \\brief Copy constructor, create a FileDescriptor from a copy of \\a other\n- * \\param[in] other The other FileDescriptor\n- *\n- * Copying a FileDescriptor implicitly shares ownership of the wrapped file\n- * descriptor. The original FileDescriptor is left untouched, and the caller is\n- * responsible for destroying it when appropriate. The wrapped file descriptor\n- * will be closed automatically when all FileDescriptor instances that\n- * reference it are destroyed.\n- */\n-FileDescriptor::FileDescriptor(const FileDescriptor &other)\n-\t: fd_(other.fd_)\n-{\n-}\n-\n-/**\n- * \\brief Move constructor, create a FileDescriptor by taking over \\a other\n- * \\param[in] other The other FileDescriptor\n- *\n- * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n- * by \\a other to the new FileDescriptor. The \\a other FileDescriptor is\n- * invalidated and its fd() function will return -1. The wrapped file descriptor\n- * will be closed automatically when all FileDescriptor instances that\n- * reference it are destroyed.\n- */\n-FileDescriptor::FileDescriptor(FileDescriptor &&other)\n-\t: fd_(std::move(other.fd_))\n-{\n-}\n-\n-/**\n- * \\brief Destroy the FileDescriptor instance\n- *\n- * Destroying a FileDescriptor instance releases its reference to the wrapped\n- * descriptor, if any. When the last instance that references a wrapped\n- * descriptor is destroyed, the file descriptor is automatically closed.\n- */\n-FileDescriptor::~FileDescriptor()\n-{\n-}\n-\n-/**\n- * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n- * copy of \\a other\n- * \\param[in] other The other FileDescriptor\n- *\n- * Copying a FileDescriptor creates a new reference to the wrapped file\n- * descriptor owner by \\a other. If \\a other is invalid, *this will also be\n- * invalid. The original FileDescriptor is left untouched, and the caller is\n- * responsible for destroying it when appropriate. The wrapped file descriptor\n- * will be closed automatically when all FileDescriptor instances that\n- * reference it are destroyed.\n- *\n- * \\return A reference to this FileDescriptor\n- */\n-FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)\n-{\n-\tfd_ = other.fd_;\n-\n-\treturn *this;\n-}\n-\n-/**\n- * \\brief Move assignment operator, replace the wrapped file descriptor by\n- * taking over \\a other\n- * \\param[in] other The other FileDescriptor\n- *\n- * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n- * by \\a other to the new FileDescriptor. If \\a other is invalid, *this will\n- * also be invalid. The \\a other FileDescriptor is invalidated and its fd()\n- * function will return -1. The wrapped file descriptor will be closed\n- * automatically when all FileDescriptor instances that reference it are\n- * destroyed.\n- *\n- * \\return A reference to this FileDescriptor\n- */\n-FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)\n-{\n-\tfd_ = std::move(other.fd_);\n-\n-\treturn *this;\n-}\n-\n-/**\n- * \\fn FileDescriptor::isValid()\n- * \\brief Check if the FileDescriptor instance is valid\n- * \\return True if the FileDescriptor is valid, false otherwise\n- */\n-\n-/**\n- * \\fn FileDescriptor::fd()\n- * \\brief Retrieve the numerical file descriptor\n- * \\return The numerical file descriptor, which may be -1 if the FileDescriptor\n- * instance is invalid\n- */\n-\n-/**\n- * \\brief Duplicate a FileDescriptor\n- *\n- * Duplicating a FileDescriptor creates a duplicate of the wrapped file\n- * descriptor and returns a UniqueFD that owns the duplicate. The fd() function\n- * of the original and the get() function of the duplicate will return different\n- * values. The duplicate instance will not be affected by destruction of the\n- * original instance or its copies.\n- *\n- * \\return A UniqueFD owning a duplicate of the original file descriptor\n- */\n-UniqueFD FileDescriptor::dup() const\n-{\n-\tint dupFd = ::dup(fd());\n-\tif (dupFd == -1) {\n-\t\tint ret = -errno;\n-\t\tLOG(FileDescriptor, Error)\n-\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n-\t}\n-\n-\treturn UniqueFD(dupFd);\n-}\n-\n-FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)\n-{\n-\tif (!duplicate) {\n-\t\tfd_ = fd;\n-\t\treturn;\n-\t}\n-\n-\t/* Failing to dup() a fd should not happen and is fatal. */\n-\tfd_ = ::dup(fd);\n-\tif (fd_ == -1) {\n-\t\tint ret = -errno;\n-\t\tLOG(FileDescriptor, Fatal)\n-\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n-\t}\n-}\n-\n-FileDescriptor::Descriptor::~Descriptor()\n-{\n-\tif (fd_ != -1)\n-\t\tclose(fd_);\n-}\n-\n-} /* namespace libcamera */\ndiff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build\nindex b0d85bc19245..ccb746c27466 100644\n--- a/src/libcamera/base/meson.build\n+++ b/src/libcamera/base/meson.build\n@@ -8,12 +8,12 @@ libcamera_base_sources = files([\n     'event_dispatcher_poll.cpp',\n     'event_notifier.cpp',\n     'file.cpp',\n-    'file_descriptor.cpp',\n     'flags.cpp',\n     'log.cpp',\n     'message.cpp',\n     'object.cpp',\n     'semaphore.cpp',\n+    'shared_fd.cpp',\n     'signal.cpp',\n     'thread.cpp',\n     'timer.cpp',\ndiff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp\nnew file mode 100644\nindex 000000000000..05b6892f7e19\n--- /dev/null\n+++ b/src/libcamera/base/shared_fd.cpp\n@@ -0,0 +1,262 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * shared_fd.cpp - File descriptor wrapper with shared ownership\n+ */\n+\n+#include <libcamera/base/shared_fd.h>\n+\n+#include <string.h>\n+#include <sys/types.h>\n+#include <unistd.h>\n+#include <utility>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/unique_fd.h>\n+\n+/**\n+ * \\file base/shared_fd.h\n+ * \\brief File descriptor wrapper\n+ */\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(SharedFD)\n+\n+/**\n+ * \\class SharedFD\n+ * \\brief RAII-style wrapper for file descriptors\n+ *\n+ * The SharedFD class provides RAII-style lifetime management of file\n+ * descriptors with an efficient mechanism for ownership sharing. At its core,\n+ * an internal Descriptor object wraps a file descriptor (expressed as a signed\n+ * integer) with an RAII-style interface. The Descriptor is then implicitly\n+ * shared with all SharedFD instances constructed as copies.\n+ *\n+ * When constructed from a numerical file descriptor, the SharedFD instance\n+ * either duplicates or takes over the file descriptor:\n+ *\n+ * - The SharedFD(const int &) constructor duplicates the numerical file\n+ *   descriptor and wraps the duplicate in a Descriptor. The caller is\n+ *   responsible for closing the original file descriptor, and the value\n+ *   returned by fd() will be different from the value passed to the\n+ *   constructor.\n+ *\n+ * - The SharedFD(int &&) constructor takes over the numerical file descriptor\n+ *   and wraps it in a Descriptor. The caller shall not touch the original file\n+ *   descriptor once the function returns, and the value returned by fd() will\n+ *   be identical to the value passed to the constructor.\n+ *\n+ * The copy constructor and assignment operator create copies that share the\n+ * Descriptor, while the move versions of those functions additionally make the\n+ * other SharedFD invalid. When the last SharedFD that references a Descriptor\n+ * is destroyed, the file descriptor is closed.\n+ *\n+ * The numerical file descriptor is available through the fd() function. All\n+ * SharedFD instances created as copies of a SharedFD will report the same fd()\n+ * value. Callers can perform operations on the fd(), but shall never close it\n+ * manually.\n+ */\n+\n+/**\n+ * \\brief Create a SharedFD copying a given \\a fd\n+ * \\param[in] fd File descriptor\n+ *\n+ * Construct a SharedFD from a numerical file descriptor by duplicating the\n+ * \\a fd, and take ownership of the copy. The original \\a fd is left untouched,\n+ * and the caller is responsible for closing it when appropriate. The duplicated\n+ * file descriptor will be closed automatically when all SharedFD instances that\n+ * reference it are destroyed.\n+ *\n+ * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n+ * function will return -1.\n+ */\n+SharedFD::SharedFD(const int &fd)\n+{\n+\tif (fd < 0)\n+\t\treturn;\n+\n+\tfd_ = std::make_shared<Descriptor>(fd, true);\n+\tif (fd_->fd() < 0)\n+\t\tfd_.reset();\n+}\n+\n+/**\n+ * \\brief Create a SharedFD taking ownership of a given \\a fd\n+ * \\param[in] fd File descriptor\n+ *\n+ * Construct a SharedFD from a numerical file descriptor by taking ownership of\n+ * the \\a fd. The original \\a fd is set to -1 and shall not be touched by the\n+ * caller anymore. In particular, the caller shall not close the original \\a fd\n+ * manually. The duplicated file descriptor will be closed automatically when\n+ * all SharedFD instances that reference it are destroyed.\n+ *\n+ * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n+ * function will return -1.\n+ */\n+SharedFD::SharedFD(int &&fd)\n+{\n+\tif (fd < 0)\n+\t\treturn;\n+\n+\tfd_ = std::make_shared<Descriptor>(fd, false);\n+\t/*\n+\t * The Descriptor constructor can't have failed here, as it took over\n+\t * the fd without duplicating it. Just set the original fd to -1 to\n+\t * implement move semantics.\n+\t */\n+\tfd = -1;\n+}\n+\n+/**\n+ * \\brief Create a SharedFD taking ownership of a given UniqueFD \\a fd\n+ * \\param[in] fd UniqueFD\n+ *\n+ * Construct a SharedFD from UniqueFD by taking ownership of the \\a fd. The\n+ * original \\a fd becomes invalid.\n+ */\n+SharedFD::SharedFD(UniqueFD fd)\n+\t: SharedFD(fd.release())\n+{\n+}\n+\n+/**\n+ * \\brief Copy constructor, create a SharedFD from a copy of \\a other\n+ * \\param[in] other The other SharedFD\n+ *\n+ * Copying a SharedFD implicitly shares ownership of the wrapped file\n+ * descriptor. The original SharedFD is left untouched, and the caller is\n+ * responsible for destroying it when appropriate. The wrapped file descriptor\n+ * will be closed automatically when all SharedFD instances that reference it\n+ * are destroyed.\n+ */\n+SharedFD::SharedFD(const SharedFD &other)\n+\t: fd_(other.fd_)\n+{\n+}\n+\n+/**\n+ * \\brief Move constructor, create a SharedFD by taking over \\a other\n+ * \\param[in] other The other SharedFD\n+ *\n+ * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n+ * \\a other to the new SharedFD. The \\a other SharedFD is invalidated and its\n+ * fd() function will return -1. The wrapped file descriptor will be closed\n+ * automatically when all SharedFD instances that reference it are destroyed.\n+ */\n+SharedFD::SharedFD(SharedFD &&other)\n+\t: fd_(std::move(other.fd_))\n+{\n+}\n+\n+/**\n+ * \\brief Destroy the SharedFD instance\n+ *\n+ * Destroying a SharedFD instance releases its reference to the wrapped\n+ * descriptor, if any. When the last instance that references a wrapped\n+ * descriptor is destroyed, the file descriptor is automatically closed.\n+ */\n+SharedFD::~SharedFD()\n+{\n+}\n+\n+/**\n+ * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n+ * copy of \\a other\n+ * \\param[in] other The other SharedFD\n+ *\n+ * Copying a SharedFD creates a new reference to the wrapped file descriptor\n+ * owner by \\a other. If \\a other is invalid, *this will also be invalid. The\n+ * original SharedFD is left untouched, and the caller is responsible for\n+ * destroying it when appropriate. The wrapped file descriptor will be closed\n+ * automatically when all SharedFD instances that reference it are destroyed.\n+ *\n+ * \\return A reference to this SharedFD\n+ */\n+SharedFD &SharedFD::operator=(const SharedFD &other)\n+{\n+\tfd_ = other.fd_;\n+\n+\treturn *this;\n+}\n+\n+/**\n+ * \\brief Move assignment operator, replace the wrapped file descriptor by\n+ * taking over \\a other\n+ * \\param[in] other The other SharedFD\n+ *\n+ * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n+ * \\a other to the new SharedFD. If \\a other is invalid, *this will also be\n+ * invalid. The \\a other SharedFD is invalidated and its fd() function will\n+ * return -1. The wrapped file descriptor will be closed automatically when\n+ * all SharedFD instances that reference it are destroyed.\n+ *\n+ * \\return A reference to this SharedFD\n+ */\n+SharedFD &SharedFD::operator=(SharedFD &&other)\n+{\n+\tfd_ = std::move(other.fd_);\n+\n+\treturn *this;\n+}\n+\n+/**\n+ * \\fn SharedFD::isValid()\n+ * \\brief Check if the SharedFD instance is valid\n+ * \\return True if the SharedFD is valid, false otherwise\n+ */\n+\n+/**\n+ * \\fn SharedFD::fd()\n+ * \\brief Retrieve the numerical file descriptor\n+ * \\return The numerical file descriptor, which may be -1 if the SharedFD\n+ * instance is invalid\n+ */\n+\n+/**\n+ * \\brief Duplicate a SharedFD\n+ *\n+ * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and\n+ * returns a UniqueFD that owns the duplicate. The fd() function of the original\n+ * and the get() function of the duplicate will return different values. The\n+ * duplicate instance will not be affected by destruction of the original\n+ * instance or its copies.\n+ *\n+ * \\return A UniqueFD owning a duplicate of the original file descriptor\n+ */\n+UniqueFD SharedFD::dup() const\n+{\n+\tint dupFd = ::dup(fd());\n+\tif (dupFd == -1) {\n+\t\tint ret = -errno;\n+\t\tLOG(SharedFD, Error)\n+\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n+\t}\n+\n+\treturn UniqueFD(dupFd);\n+}\n+\n+SharedFD::Descriptor::Descriptor(int fd, bool duplicate)\n+{\n+\tif (!duplicate) {\n+\t\tfd_ = fd;\n+\t\treturn;\n+\t}\n+\n+\t/* Failing to dup() a fd should not happen and is fatal. */\n+\tfd_ = ::dup(fd);\n+\tif (fd_ == -1) {\n+\t\tint ret = -errno;\n+\t\tLOG(SharedFD, Fatal)\n+\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n+\t}\n+}\n+\n+SharedFD::Descriptor::~Descriptor()\n+{\n+\tif (fd_ != -1)\n+\t\tclose(fd_);\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\nindex f5bcf107d7aa..0a5bf7fdbeb7 100644\n--- a/src/libcamera/framebuffer.cpp\n+++ b/src/libcamera/framebuffer.cpp\n@@ -180,9 +180,9 @@ FrameBuffer::Private::Private()\n  * offset and length.\n  *\n  * To support DMA access, planes are associated with dmabuf objects represented\n- * by FileDescriptor handles. The Plane class doesn't handle mapping of the\n- * memory to the CPU, but applications and IPAs may use the dmabuf file\n- * descriptors to map the plane memory with mmap() and access its contents.\n+ * by SharedFD handles. The Plane class doesn't handle mapping of the memory to\n+ * the CPU, but applications and IPAs may use the dmabuf file descriptors to map\n+ * the plane memory with mmap() and access its contents.\n  *\n  * \\todo Specify how an application shall decide whether to use a single or\n  * multiple dmabufs, based on the camera requirements.\ndiff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\nindex 82ec9b20a411..0a259305afa2 100644\n--- a/src/libcamera/ipa_data_serializer.cpp\n+++ b/src/libcamera/ipa_data_serializer.cpp\n@@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)\n  *\n  * \\todo Harden the vector and map deserializer\n  *\n- * \\todo For FileDescriptors, instead of storing a validity flag, store an\n+ * \\todo For SharedFDs, instead of storing a validity flag, store an\n  * index into the fd array. This will allow us to use views instead of copying.\n  */\n \n@@ -112,7 +112,7 @@ namespace {\n  * \\param[in] cs ControlSerializer\n  *\n  * This version of deserialize() can be used if the object type \\a T and its\n- * members don't have any FileDescriptor.\n+ * members don't have any SharedFD.\n  *\n  * \\a cs is only necessary if the object type \\a T or its members contain\n  * ControlList or ControlInfoMap.\n@@ -132,7 +132,7 @@ namespace {\n  * \\param[in] cs ControlSerializer\n  *\n  * This version of deserialize() can be used if the object type \\a T and its\n- * members don't have any FileDescriptor.\n+ * members don't have any SharedFD.\n  *\n  * \\a cs is only necessary if the object type \\a T or its members contain\n  * ControlList or ControlInfoMap.\n@@ -143,7 +143,7 @@ namespace {\n /**\n  * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n  * \tconst std::vector<uint8_t> &data,\n- * \tconst std::vector<FileDescriptor> &fds,\n+ * \tconst std::vector<SharedFD> &fds,\n  * \tControlSerializer *cs = nullptr)\n  * \\brief Deserialize byte vector and fd vector into an object\n  * \\tparam T Type of object to deserialize to\n@@ -152,7 +152,7 @@ namespace {\n  * \\param[in] cs ControlSerializer\n  *\n  * This version of deserialize() (or the iterator version) must be used if\n- * the object type \\a T or its members contain FileDescriptor.\n+ * the object type \\a T or its members contain SharedFD.\n  *\n  * \\a cs is only necessary if the object type \\a T or its members contain\n  * ControlList or ControlInfoMap.\n@@ -164,8 +164,8 @@ namespace {\n  * \\fn template<typename T> IPADataSerializer::deserialize(\n  * \tstd::vector<uint8_t>::const_iterator dataBegin,\n  * \tstd::vector<uint8_t>::const_iterator dataEnd,\n- * \tstd::vector<FileDescriptor>::const_iterator fdsBegin,\n- * \tstd::vector<FileDescriptor>::const_iterator fdsEnd,\n+ * \tstd::vector<SharedFD>::const_iterator fdsBegin,\n+ * \tstd::vector<SharedFD>::const_iterator fdsEnd,\n  * \tControlSerializer *cs = nullptr)\n  * \\brief Deserialize byte vector and fd vector into an object\n  * \\tparam T Type of object to deserialize to\n@@ -176,7 +176,7 @@ namespace {\n  * \\param[in] cs ControlSerializer\n  *\n  * This version of deserialize() (or the vector version) must be used if\n- * the object type \\a T or its members contain FileDescriptor.\n+ * the object type \\a T or its members contain SharedFD.\n  *\n  * \\a cs is only necessary if the object type \\a T or its members contain\n  * ControlList or ControlInfoMap.\n@@ -189,7 +189,7 @@ namespace {\n #define DEFINE_POD_SERIALIZER(type)\t\t\t\t\t\\\n \t\t\t\t\t\t\t\t\t\\\n template<>\t\t\t\t\t\t\t\t\\\n-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\t\t\\\n+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\t\t\\\n IPADataSerializer<type>::serialize(const type &data,\t\t\t\\\n \t\t\t\t  [[maybe_unused]] ControlSerializer *cs) \\\n {\t\t\t\t\t\t\t\t\t\\\n@@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n \t\t\t\t\t\t\t\t\t\\\n template<>\t\t\t\t\t\t\t\t\\\n type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n-\t\t\t\t\t  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \\\n+\t\t\t\t\t  [[maybe_unused]] const std::vector<SharedFD> &fds, \\\n \t\t\t\t\t  ControlSerializer *cs)\t\\\n {\t\t\t\t\t\t\t\t\t\\\n \treturn deserialize(data.cbegin(), data.end(), cs);\t\t\\\n@@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n template<>\t\t\t\t\t\t\t\t\\\n type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \\\n \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd, \\\n-\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \\\n-\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \\\n+\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \\\n+\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \\\n \t\t\t\t\t  ControlSerializer *cs)\t\\\n {\t\t\t\t\t\t\t\t\t\\\n \treturn deserialize(dataBegin, dataEnd, cs);\t\t\t\\\n@@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)\n  * function parameter serdes).\n  */\n template<>\n-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n IPADataSerializer<std::string>::serialize(const std::string &data,\n \t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n {\n@@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n template<>\n std::string\n IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,\n-\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n+\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n {\n \treturn { data.cbegin(), data.cend() };\n@@ -288,8 +288,8 @@ template<>\n std::string\n IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n {\n \treturn { dataBegin, dataEnd };\n@@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n  * be used. The serialized ControlInfoMap will have zero length.\n  */\n template<>\n-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)\n {\n \tif (!cs)\n@@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n template<>\n ControlList\n IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n-\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n+\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n \t\t\t\t\t    ControlSerializer *cs)\n {\n \treturn deserialize(data.cbegin(), data.end(), cs);\n@@ -417,8 +417,8 @@ template<>\n ControlList\n IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t\t\t\t    ControlSerializer *cs)\n {\n \treturn deserialize(dataBegin, dataEnd, cs);\n@@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator\n  * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n  */\n template<>\n-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,\n \t\t\t\t\t     ControlSerializer *cs)\n {\n@@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n template<>\n ControlInfoMap\n IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n-\t\t\t\t\t       [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n+\t\t\t\t\t       [[maybe_unused]] const std::vector<SharedFD> &fds,\n \t\t\t\t\t       ControlSerializer *cs)\n {\n \treturn deserialize(data.cbegin(), data.end(), cs);\n@@ -503,30 +503,30 @@ template<>\n ControlInfoMap\n IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t\t\t\t       ControlSerializer *cs)\n {\n \treturn deserialize(dataBegin, dataEnd, cs);\n }\n \n /*\n- * FileDescriptors are serialized into four bytes that tells if the\n- * FileDescriptor is valid or not. If it is valid, then for serialization\n- * the fd will be written to the fd vector, or for deserialization the\n- * fd vector const_iterator will be valid.\n+ * SharedFD instances are serialized into four bytes that tells if the SharedFD\n+ * is valid or not. If it is valid, then for serialization the fd will be\n+ * written to the fd vector, or for deserialization the fd vector const_iterator\n+ * will be valid.\n  *\n  * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n  * also allows us to simply send the entire fd vector into the deserializer\n  * and it will be recursively consumed as necessary.\n  */\n template<>\n-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n-IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n-\t\t\t\t\t     [[maybe_unused]] ControlSerializer *cs)\n+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n+IPADataSerializer<SharedFD>::serialize(const SharedFD &data,\n+\t\t\t\t       [[maybe_unused]] ControlSerializer *cs)\n {\n \tstd::vector<uint8_t> dataVec;\n-\tstd::vector<FileDescriptor> fdVec;\n+\tstd::vector<SharedFD> fdVec;\n \n \t/*\n \t * Store as uint32_t to prepare for conversion from validity flag\n@@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n }\n \n template<>\n-FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n-\t\t\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsEnd,\n-\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n+SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n+\t\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n+\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsEnd,\n+\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n {\n \tASSERT(std::distance(dataBegin, dataEnd) >= 4);\n \n@@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s\n \n \tASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n \n-\treturn valid ? *fdsBegin : FileDescriptor();\n+\treturn valid ? *fdsBegin : SharedFD();\n }\n \n template<>\n-FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,\n-\t\t\t\t\t\t\t      const std::vector<FileDescriptor> &fds,\n-\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n+SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,\n+\t\t\t\t\t\t  const std::vector<SharedFD> &fds,\n+\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n {\n \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n }\n@@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<\n /*\n  * FrameBuffer::Plane is serialized as:\n  *\n- * 4 byte  - FileDescriptor\n+ * 4 byte  - SharedFD\n  * 4 bytes - uint32_t Offset\n  * 4 bytes - uint32_t Length\n  */\n template<>\n-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,\n \t\t\t\t\t\t [[maybe_unused]] ControlSerializer *cs)\n {\n \tstd::vector<uint8_t> dataVec;\n-\tstd::vector<FileDescriptor> fdsVec;\n+\tstd::vector<SharedFD> fdsVec;\n \n \tstd::vector<uint8_t> fdBuf;\n-\tstd::vector<FileDescriptor> fdFds;\n+\tstd::vector<SharedFD> fdFds;\n \tstd::tie(fdBuf, fdFds) =\n-\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n+\t\tIPADataSerializer<SharedFD>::serialize(data.fd);\n \tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n \tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n \n@@ -597,13 +597,13 @@ template<>\n FrameBuffer::Plane\n IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t\t\t\t   std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t\t\t   std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t\t\t\t\t   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t\t\t\t\t   std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t\t\t\t\t   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t\t\t\t\t   [[maybe_unused]] ControlSerializer *cs)\n {\n \tFrameBuffer::Plane ret;\n \n-\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,\n+\tret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,\n \t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n \tret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n \tret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);\n@@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i\n template<>\n FrameBuffer::Plane\n IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,\n-\t\t\t\t\t\t   const std::vector<FileDescriptor> &fds,\n+\t\t\t\t\t\t   const std::vector<SharedFD> &fds,\n \t\t\t\t\t\t   ControlSerializer *cs)\n {\n \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\ndiff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp\nindex ad870fd4137f..3b47032de0a2 100644\n--- a/src/libcamera/ipc_pipe.cpp\n+++ b/src/libcamera/ipc_pipe.cpp\n@@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)\n \tdata_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),\n \t\t\t\t     payload.data.end());\n \tfor (int32_t &fd : payload.fds)\n-\t\tfds_.push_back(FileDescriptor(std::move(fd)));\n+\t\tfds_.push_back(SharedFD(std::move(fd)));\n }\n \n /**\n@@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const\n \t\t       data_.data(), data_.size());\n \t}\n \n-\tfor (const FileDescriptor &fd : fds_)\n+\tfor (const SharedFD &fd : fds_)\n \t\tpayload.fds.push_back(fd.fd());\n \n \treturn payload;\ndiff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\nindex ffa51a0c65ca..ea8243912a29 100644\n--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n@@ -12,7 +12,7 @@\n #include <queue>\n #include <unordered_set>\n \n-#include <libcamera/base/file_descriptor.h>\n+#include <libcamera/base/shared_fd.h>\n \n #include <libcamera/camera.h>\n #include <libcamera/control_ids.h>\n@@ -228,7 +228,7 @@ public:\n \n \t/* DMAHEAP allocation helper. */\n \tRPi::DmaHeap dmaHeap_;\n-\tFileDescriptor lsTable_;\n+\tSharedFD lsTable_;\n \n \tstd::unique_ptr<DelayedControls> delayedCtrls_;\n \tbool sensorMetadata_;\n@@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n \t\tif (!fd.isValid())\n \t\t\treturn -ENOMEM;\n \n-\t\tlsTable_ = FileDescriptor(std::move(fd));\n+\t\tlsTable_ = SharedFD(std::move(fd));\n \n \t\t/* Allow the IPA to mmap the LS table via the file descriptor. */\n \t\t/*\ndiff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\nindex 3966483a365f..97d431071def 100644\n--- a/src/libcamera/v4l2_videodevice.cpp\n+++ b/src/libcamera/v4l2_videodevice.cpp\n@@ -22,8 +22,8 @@\n #include <linux/version.h>\n \n #include <libcamera/base/event_notifier.h>\n-#include <libcamera/base/file_descriptor.h>\n #include <libcamera/base/log.h>\n+#include <libcamera/base/shared_fd.h>\n #include <libcamera/base/unique_fd.h>\n #include <libcamera/base/utils.h>\n \n@@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n \t\t\treturn nullptr;\n \n \t\tFrameBuffer::Plane plane;\n-\t\tplane.fd = FileDescriptor(std::move(fd));\n+\t\tplane.fd = SharedFD(std::move(fd));\n \t\t/*\n \t\t * V4L2 API doesn't provide dmabuf offset information of plane.\n \t\t * Set 0 as a placeholder offset.\n@@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n \t\tASSERT(numPlanes == 1u);\n \n \t\tplanes.resize(formatInfo_->numPlanes());\n-\t\tconst FileDescriptor &fd = planes[0].fd;\n+\t\tconst SharedFD &fd = planes[0].fd;\n \t\tsize_t offset = 0;\n \n \t\tfor (auto [i, plane] : utils::enumerate(planes)) {\ndiff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\nindex 9817fd393d59..586347829845 100644\n--- a/src/v4l2/v4l2_camera.h\n+++ b/src/v4l2/v4l2_camera.h\n@@ -11,8 +11,8 @@\n #include <mutex>\n #include <utility>\n \n-#include <libcamera/base/file_descriptor.h>\n #include <libcamera/base/semaphore.h>\n+#include <libcamera/base/shared_fd.h>\n \n #include <libcamera/camera.h>\n #include <libcamera/framebuffer.h>\ndiff --git a/test/meson.build b/test/meson.build\nindex 42dfbc1f8ee9..daaa3862cdd6 100644\n--- a/test/meson.build\n+++ b/test/meson.build\n@@ -40,7 +40,6 @@ internal_tests = [\n     ['event-dispatcher',                'event-dispatcher.cpp'],\n     ['event-thread',                    'event-thread.cpp'],\n     ['file',                            'file.cpp'],\n-    ['file-descriptor',                 'file-descriptor.cpp'],\n     ['flags',                           'flags.cpp'],\n     ['hotplug-cameras',                 'hotplug-cameras.cpp'],\n     ['mapped-buffer',                   'mapped-buffer.cpp'],\n@@ -49,6 +48,7 @@ internal_tests = [\n     ['object-delete',                   'object-delete.cpp'],\n     ['object-invoke',                   'object-invoke.cpp'],\n     ['pixel-format',                    'pixel-format.cpp'],\n+    ['shared-fd',                       'shared-fd.cpp'],\n     ['signal-threads',                  'signal-threads.cpp'],\n     ['threads',                         'threads.cpp'],\n     ['timer',                           'timer.cpp'],\ndiff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp\nindex 5fcdcb8eae92..d2050a868b38 100644\n--- a/test/serialization/ipa_data_serializer_test.cpp\n+++ b/test/serialization/ipa_data_serializer_test.cpp\n@@ -53,7 +53,7 @@ template<typename T>\n int testPodSerdes(T in)\n {\n \tstd::vector<uint8_t> buf;\n-\tstd::vector<FileDescriptor> fds;\n+\tstd::vector<SharedFD> fds;\n \n \tstd::tie(buf, fds) = IPADataSerializer<T>::serialize(in);\n \tT out = IPADataSerializer<T>::deserialize(buf, fds);\n@@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,\n \t\t     ControlSerializer *cs = nullptr)\n {\n \tstd::vector<uint8_t> buf;\n-\tstd::vector<FileDescriptor> fds;\n+\tstd::vector<SharedFD> fds;\n \n \tstd::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);\n \tstd::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);\n@@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,\n \t\t  ControlSerializer *cs = nullptr)\n {\n \tstd::vector<uint8_t> buf;\n-\tstd::vector<FileDescriptor> fds;\n+\tstd::vector<SharedFD> fds;\n \n \tstd::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);\n \tstd::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);\n@@ -198,7 +198,7 @@ private:\n \t\tControlSerializer cs(ControlSerializer::Role::Proxy);\n \n \t\t/*\n-\t\t * We don't test FileDescriptor serdes because it dup()s, so we\n+\t\t * We don't test SharedFD serdes because it dup()s, so we\n \t\t * can't check for equality.\n \t\t */\n \t\tstd::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };\n@@ -219,7 +219,7 @@ private:\n \t\t};\n \n \t\tstd::vector<uint8_t> buf;\n-\t\tstd::vector<FileDescriptor> fds;\n+\t\tstd::vector<SharedFD> fds;\n \n \t\tif (testVectorSerdes(vecUint8) != TestPass)\n \t\t\treturn TestFail;\n@@ -291,7 +291,7 @@ private:\n \t\t\t{ { \"a\", { 1, 2, 3 } }, { \"b\", { 4, 5, 6 } }, { \"c\", { 7, 8, 9 } } };\n \n \t\tstd::vector<uint8_t> buf;\n-\t\tstd::vector<FileDescriptor> fds;\n+\t\tstd::vector<SharedFD> fds;\n \n \t\tif (testMapSerdes(mapUintStr) != TestPass)\n \t\t\treturn TestFail;\n@@ -359,7 +359,7 @@ private:\n \t\tstd::string strEmpty = \"\";\n \n \t\tstd::vector<uint8_t> buf;\n-\t\tstd::vector<FileDescriptor> fds;\n+\t\tstd::vector<SharedFD> fds;\n \n \t\tif (testPodSerdes(u32min) != TestPass)\n \t\t\treturn TestFail;\ndiff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp\nsimilarity index 80%\nrename from test/file-descriptor.cpp\nrename to test/shared-fd.cpp\nindex 76badc4c5fad..60e5d0aaa395 100644\n--- a/test/file-descriptor.cpp\n+++ b/test/shared-fd.cpp\n@@ -2,7 +2,7 @@\n /*\n  * Copyright (C) 2019, Google Inc.\n  *\n- * file_descriptor.cpp - FileDescriptor test\n+ * shared_fd.cpp - SharedFD test\n  */\n \n #include <fcntl.h>\n@@ -11,7 +11,7 @@\n #include <sys/types.h>\n #include <unistd.h>\n \n-#include <libcamera/base/file_descriptor.h>\n+#include <libcamera/base/shared_fd.h>\n #include <libcamera/base/utils.h>\n \n #include \"test.h\"\n@@ -19,7 +19,7 @@\n using namespace libcamera;\n using namespace std;\n \n-class FileDescriptorTest : public Test\n+class SharedFDTest : public Test\n {\n protected:\n \tint init()\n@@ -43,8 +43,8 @@ protected:\n \n \tint run()\n \t{\n-\t\t/* Test creating empty FileDescriptor. */\n-\t\tdesc1_ = new FileDescriptor();\n+\t\t/* Test creating empty SharedFD. */\n+\t\tdesc1_ = new SharedFD();\n \n \t\tif (desc1_->fd() != -1) {\n \t\t\tstd::cout << \"Failed fd numerical check (default constructor)\"\n@@ -56,10 +56,10 @@ protected:\n \t\tdesc1_ = nullptr;\n \n \t\t/*\n-\t\t * Test creating FileDescriptor by copying numerical file\n+\t\t * Test creating SharedFD by copying numerical file\n \t\t * descriptor.\n \t\t */\n-\t\tdesc1_ = new FileDescriptor(fd_);\n+\t\tdesc1_ = new SharedFD(fd_);\n \t\tif (desc1_->fd() == fd_) {\n \t\t\tstd::cout << \"Failed fd numerical check (lvalue ref constructor)\"\n \t\t\t\t  << std::endl;\n@@ -84,13 +84,13 @@ protected:\n \t\t}\n \n \t\t/*\n-\t\t * Test creating FileDescriptor by taking ownership of\n+\t\t * Test creating SharedFD by taking ownership of\n \t\t * numerical file descriptor.\n \t\t */\n \t\tint dupFd = dup(fd_);\n \t\tint dupFdCopy = dupFd;\n \n-\t\tdesc1_ = new FileDescriptor(std::move(dupFd));\n+\t\tdesc1_ = new SharedFD(std::move(dupFd));\n \t\tif (desc1_->fd() != dupFdCopy) {\n \t\t\tstd::cout << \"Failed fd numerical check (rvalue ref constructor)\"\n \t\t\t\t  << std::endl;\n@@ -114,9 +114,9 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\t/* Test creating FileDescriptor from other FileDescriptor. */\n-\t\tdesc1_ = new FileDescriptor(fd_);\n-\t\tdesc2_ = new FileDescriptor(*desc1_);\n+\t\t/* Test creating SharedFD from other SharedFD. */\n+\t\tdesc1_ = new SharedFD(fd_);\n+\t\tdesc2_ = new SharedFD(*desc1_);\n \n \t\tif (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {\n \t\t\tstd::cout << \"Failed fd numerical check (copy constructor)\"\n@@ -142,10 +142,10 @@ protected:\n \t\tdelete desc2_;\n \t\tdesc2_ = nullptr;\n \n-\t\t/* Test creating FileDescriptor by taking over other FileDescriptor. */\n-\t\tdesc1_ = new FileDescriptor(fd_);\n+\t\t/* Test creating SharedFD by taking over other SharedFD. */\n+\t\tdesc1_ = new SharedFD(fd_);\n \t\tfd = desc1_->fd();\n-\t\tdesc2_ = new FileDescriptor(std::move(*desc1_));\n+\t\tdesc2_ = new SharedFD(std::move(*desc1_));\n \n \t\tif (desc1_->fd() != -1 || desc2_->fd() != fd) {\n \t\t\tstd::cout << \"Failed fd numerical check (move constructor)\"\n@@ -164,9 +164,9 @@ protected:\n \t\tdelete desc2_;\n \t\tdesc2_ = nullptr;\n \n-\t\t/* Test creating FileDescriptor by copy assignment. */\n-\t\tdesc1_ = new FileDescriptor();\n-\t\tdesc2_ = new FileDescriptor(fd_);\n+\t\t/* Test creating SharedFD by copy assignment. */\n+\t\tdesc1_ = new SharedFD();\n+\t\tdesc2_ = new SharedFD(fd_);\n \n \t\tfd = desc2_->fd();\n \t\t*desc1_ = *desc2_;\n@@ -188,9 +188,9 @@ protected:\n \t\tdelete desc2_;\n \t\tdesc2_ = nullptr;\n \n-\t\t/* Test creating FileDescriptor by move assignment. */\n-\t\tdesc1_ = new FileDescriptor();\n-\t\tdesc2_ = new FileDescriptor(fd_);\n+\t\t/* Test creating SharedFD by move assignment. */\n+\t\tdesc1_ = new SharedFD();\n+\t\tdesc2_ = new SharedFD(fd_);\n \n \t\tfd = desc2_->fd();\n \t\t*desc1_ = std::move(*desc2_);\n@@ -237,7 +237,7 @@ private:\n \n \tint fd_;\n \tino_t inodeNr_;\n-\tFileDescriptor *desc1_, *desc2_;\n+\tSharedFD *desc1_, *desc2_;\n };\n \n-TEST_REGISTER(FileDescriptorTest)\n+TEST_REGISTER(SharedFDTest)\ndiff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\nindex d856339aa9ee..c37c4941b528 100644\n--- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n+++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n@@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n void {{proxy_name}}::{{method.mojom_name}}IPC(\n \tstd::vector<uint8_t>::const_iterator data,\n \tsize_t dataSize,\n-\t[[maybe_unused]] const std::vector<FileDescriptor> &fds)\n+\t[[maybe_unused]] const std::vector<SharedFD> &fds)\n {\n {%- for param in method.parameters %}\n \t{{param|name}} {{param.mojom_name}};\ndiff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\nindex ce396c183d0c..c308dd10c7e5 100644\n--- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n+++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n@@ -64,7 +64,7 @@ private:\n \tvoid {{method.mojom_name}}IPC(\n \t\tstd::vector<uint8_t>::const_iterator data,\n \t\tsize_t dataSize,\n-\t\tconst std::vector<FileDescriptor> &fds);\n+\t\tconst std::vector<SharedFD> &fds);\n {% endfor %}\n \n \t/* Helper class to invoke async functions in another thread. */\ndiff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\nindex ebcd2aaaafae..bac826a74c2d 100644\n--- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n+++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n@@ -54,7 +54,7 @@\n {%- for param in params %}\n \tstd::vector<uint8_t> {{param.mojom_name}}Buf;\n {%- if param|has_fd %}\n-\tstd::vector<FileDescriptor> {{param.mojom_name}}Fds;\n+\tstd::vector<SharedFD> {{param.mojom_name}}Fds;\n \tstd::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =\n {%- else %}\n \tstd::tie({{param.mojom_name}}Buf, std::ignore) =\ndiff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl\nindex b8ef8e7b974e..77bae36fe6b7 100644\n--- a/utils/ipc/generators/libcamera_templates/serializer.tmpl\n+++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n@@ -40,7 +40,7 @@\n \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n {%- elif field|is_fd %}\n \t\tstd::vector<uint8_t> {{field.mojom_name}};\n-\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n+\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n \t\t\tIPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});\n \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n@@ -58,7 +58,7 @@\n {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}\n \t\tstd::vector<uint8_t> {{field.mojom_name}};\n \t{%- if field|has_fd %}\n-\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n+\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n \t{%- else %}\n \t\tstd::tie({{field.mojom_name}}, std::ignore) =\n@@ -177,7 +177,7 @@\n  # \\a struct.\n  #}\n {%- macro serializer(struct, namespace) %}\n-\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n \tserialize(const {{struct|name_full}} &data,\n {%- if struct|needs_control_serializer %}\n \t\t  ControlSerializer *cs)\n@@ -187,7 +187,7 @@\n \t{\n \t\tstd::vector<uint8_t> retData;\n {%- if struct|has_fd %}\n-\t\tstd::vector<FileDescriptor> retFds;\n+\t\tstd::vector<SharedFD> retFds;\n {%- endif %}\n {%- for field in struct.fields %}\n {{serializer_field(field, namespace, loop)}}\n@@ -210,7 +210,7 @@\n {%- macro deserializer_fd(struct, namespace) %}\n \tstatic {{struct|name_full}}\n \tdeserialize(std::vector<uint8_t> &data,\n-\t\t    std::vector<FileDescriptor> &fds,\n+\t\t    std::vector<SharedFD> &fds,\n {%- if struct|needs_control_serializer %}\n \t\t    ControlSerializer *cs)\n {%- else %}\n@@ -224,8 +224,8 @@\n \tstatic {{struct|name_full}}\n \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t    std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t    std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t    std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t    std::vector<SharedFD>::const_iterator fdsEnd,\n {%- if struct|needs_control_serializer %}\n \t\t    ControlSerializer *cs)\n {%- else %}\n@@ -234,7 +234,7 @@\n \t{\n \t\t{{struct|name_full}} ret;\n \t\tstd::vector<uint8_t>::const_iterator m = dataBegin;\n-\t\tstd::vector<FileDescriptor>::const_iterator n = fdsBegin;\n+\t\tstd::vector<SharedFD>::const_iterator n = fdsBegin;\n \n \t\tsize_t dataSize = std::distance(dataBegin, dataEnd);\n \t\t[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);\n@@ -255,7 +255,7 @@\n {%- macro deserializer_fd_simple(struct, namespace) %}\n \tstatic {{struct|name_full}}\n \tdeserialize(std::vector<uint8_t> &data,\n-\t\t    [[maybe_unused]] std::vector<FileDescriptor> &fds,\n+\t\t    [[maybe_unused]] std::vector<SharedFD> &fds,\n \t\t    ControlSerializer *cs = nullptr)\n \t{\n \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);\n@@ -264,8 +264,8 @@\n \tstatic {{struct|name_full}}\n \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n-\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n+\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n+\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n \t\t    ControlSerializer *cs = nullptr)\n \t{\n \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);\ndiff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py\nindex c609f4e5c062..753bfc734e56 100644\n--- a/utils/ipc/generators/mojom_libcamera_generator.py\n+++ b/utils/ipc/generators/mojom_libcamera_generator.py\n@@ -77,7 +77,7 @@ def GetDefaultValue(element):\n     if mojom.IsEnumKind(element.kind):\n         return f'static_cast<{element.kind.mojom_name}>(0)'\n     if isinstance(element.kind, mojom.Struct) and \\\n-       element.kind.mojom_name == 'FileDescriptor':\n+       element.kind.mojom_name == 'SharedFD':\n         return '-1'\n     return ''\n \n@@ -140,7 +140,7 @@ def HasFd(element):\n         types = GetAllTypes(element)\n     else:\n         types = GetAllTypes(element.kind)\n-    return \"FileDescriptor\" in types or (attrs is not None and \"hasFd\" in attrs)\n+    return \"SharedFD\" in types or (attrs is not None and \"hasFd\" in attrs)\n \n def WithDefaultValues(element):\n     return [x for x in element if HasDefaultValue(x)]\n@@ -221,7 +221,7 @@ def IsEnum(element):\n     return mojom.IsEnumKind(element.kind)\n \n def IsFd(element):\n-    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"FileDescriptor\"\n+    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"SharedFD\"\n \n def IsMap(element):\n     return mojom.IsMapKind(element.kind)\n","prefixes":["libcamera-devel","v3","17/17"]}