diff --git a/repos/os/lib/mk/vfs_capture.mk b/repos/os/lib/mk/vfs_capture.mk new file mode 100644 index 0000000000..9d97414eb5 --- /dev/null +++ b/repos/os/lib/mk/vfs_capture.mk @@ -0,0 +1,5 @@ +SRC_CC = plugin.cc + +vpath %.cc $(REP_DIR)/src/lib/vfs/capture + +SHARED_LIB = yes diff --git a/repos/os/recipes/src/vfs_capture/content.mk b/repos/os/recipes/src/vfs_capture/content.mk new file mode 100644 index 0000000000..b8fdaecc00 --- /dev/null +++ b/repos/os/recipes/src/vfs_capture/content.mk @@ -0,0 +1,9 @@ +MIRROR_FROM_REP_DIR := lib/mk/vfs_capture.mk src/lib/vfs/capture + +content: $(MIRROR_FROM_REP_DIR) LICENSE + +$(MIRROR_FROM_REP_DIR): + $(mirror_from_rep_dir) + +LICENSE: + cp $(GENODE_DIR)/LICENSE $@ diff --git a/repos/os/recipes/src/vfs_capture/hash b/repos/os/recipes/src/vfs_capture/hash new file mode 100644 index 0000000000..3016817ccb --- /dev/null +++ b/repos/os/recipes/src/vfs_capture/hash @@ -0,0 +1 @@ +2021-09-23 809e1ec6bbaceecae65325506cc6f06c3386f798 diff --git a/repos/os/recipes/src/vfs_capture/used_apis b/repos/os/recipes/src/vfs_capture/used_apis new file mode 100644 index 0000000000..fe97d4eeb5 --- /dev/null +++ b/repos/os/recipes/src/vfs_capture/used_apis @@ -0,0 +1,6 @@ +base +blit +capture_session +os +so +vfs diff --git a/repos/os/src/lib/vfs/capture/README b/repos/os/src/lib/vfs/capture/README new file mode 100644 index 0000000000..d3efcfe0a0 --- /dev/null +++ b/repos/os/src/lib/vfs/capture/README @@ -0,0 +1,6 @@ +This VFS plugin offers access to Genode's 'Capture' session. + +In its current state, it merely provides a single file named +"capture". Reading from this file delivers the pixel data of +a 640x480 image with 4 bytes per pixel, which is mainly useful +to receive images from a webcam. diff --git a/repos/os/src/lib/vfs/capture/plugin.cc b/repos/os/src/lib/vfs/capture/plugin.cc new file mode 100644 index 0000000000..0accec57b9 --- /dev/null +++ b/repos/os/src/lib/vfs/capture/plugin.cc @@ -0,0 +1,264 @@ +/* + * \brief VFS capture plugin + * \author Christian Prochaska + * \date 2021-09-08 + */ + +/* + * Copyright (C) 2021-2022 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include +#include +#include + + +namespace Vfs_capture +{ + using namespace Genode; + using namespace Vfs; + + typedef String<64> Name; + + struct Data_file_system; + struct Local_factory; + struct File_system; +}; + + +class Vfs_capture::Data_file_system : public Single_file_system +{ + private: + + Name const &_name; + + typedef Genode::String<64> Label; + Label const &_label; + + Genode::Env &_env; + + Capture::Area const _capture_area { 640, 480 }; + Constructible _capture { }; + Constructible _capture_ds { }; + + unsigned int _open_count { 0 }; + + struct Capture_vfs_handle : Single_vfs_handle + { + Constructible &_capture; + Constructible &_capture_ds; + + bool notifying = false; + bool blocked = false; + + Capture_vfs_handle(Constructible &capture, + Constructible &capture_ds, + Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + int flags) + : + Single_vfs_handle(ds, fs, alloc, flags), + _capture(capture), _capture_ds(capture_ds) + { } + + bool read_ready() override { + return true; } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + _capture->capture_at(Point(0, 0)); + + Genode::memcpy(dst, _capture_ds->local_addr(), (size_t)count); + + out_count = count; + + return READ_OK; + } + + Write_result write(char const *, file_size, file_size &) override + { + return WRITE_ERR_IO; + } + }; + + typedef Genode::Registered Registered_handle; + typedef Genode::Registry Handle_registry; + + Handle_registry _handle_registry { }; + + public: + + Data_file_system(Name const &name, + Label const &label, + Genode::Env &env) + : + Single_file_system(Node_type::TRANSACTIONAL_FILE, name.string(), + Node_rwx::rw(), Genode::Xml_node("")), + _name(name), _label(label), _env(env) + { } + + static const char *name() { return "data"; } + char const *type() override { return "data"; } + + Open_result open(char const *path, unsigned flags, + Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + if (_open_count == 0) { + try { + _capture.construct(_env, _label.string()); + } catch (Genode::Service_denied) { + return OPEN_ERR_UNACCESSIBLE; + } + _capture->buffer(_capture_area); + _capture_ds.construct(_env.rm(), _capture->dataspace()); + } + + try { + *out_handle = new (alloc) + Registered_handle(_handle_registry, + _capture, _capture_ds, + *this, *this, alloc, flags); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + + void close(Vfs_handle *handle) override + { + _open_count--; + + if (_open_count == 0) { + _capture_ds.destruct(); + _capture.destruct(); + } + + Single_file_system::close(handle); + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + bool notify_read_ready(Vfs_handle *vfs_handle) override + { + Capture_vfs_handle *handle = + static_cast(vfs_handle); + if (!handle) + return false; + + handle->notifying = true; + return true; + } + + Ftruncate_result ftruncate(Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +struct Vfs_capture::Local_factory : File_system_factory +{ + typedef Genode::String<64> Label; + Label const _label; + + Name const _name; + + Genode::Env &_env; + + Data_file_system _data_fs { _name, _label, _env }; + + static Name name(Xml_node config) + { + return config.attribute_value("name", Name("capture")); + } + + Local_factory(Vfs::Env &env, Xml_node config) + : + _label(config.attribute_value("label", Label(""))), + _name(name(config)), + _env(env.env()) + { } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + if (node.has_type("data")) return &_data_fs; + + return nullptr; + } +}; + + +class Vfs_capture::File_system : private Local_factory, + public Vfs::Dir_file_system +{ + private: + + typedef Vfs_capture::Name Name; + + typedef String<200> Config; + static Config _config(Name const &name) + { + char buf[Config::capacity()] { }; + + /* + * By not using the node type "dir", we operate the + * 'Dir_file_system' in root mode, allowing multiple sibling nodes + * to be present at the mount point. + */ + Genode::Xml_generator xml(buf, sizeof(buf), "compound", [&] () { + + xml.node("data", [&] () { + xml.attribute("name", name); }); + + xml.node("dir", [&] () { + xml.attribute("name", Name(".", name)); + }); + }); + + return Config(Genode::Cstring(buf)); + } + + public: + + File_system(Vfs::Env &vfs_env, Genode::Xml_node node) + : + Local_factory(vfs_env, node), + Vfs::Dir_file_system(vfs_env, + Xml_node(_config(Local_factory::name(node)).string()), + *this) + { } + + static const char *name() { return "capture"; } + + char const *type() override { return name(); } +}; + + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + struct Factory : Vfs::File_system_factory + { + Vfs::File_system *create(Vfs::Env &env, Genode::Xml_node node) override + { + return new (env.alloc()) Vfs_capture::File_system(env, node); + } + }; + + static Factory f; + return &f; +} diff --git a/repos/os/src/lib/vfs/capture/target.mk b/repos/os/src/lib/vfs/capture/target.mk new file mode 100644 index 0000000000..66c29b5dbd --- /dev/null +++ b/repos/os/src/lib/vfs/capture/target.mk @@ -0,0 +1,2 @@ +TARGET = dummy-vfs_capture +LIBS = vfs_capture