diff --git a/repos/libports/lib/mk/vfs_libusb.mk b/repos/libports/lib/mk/vfs_libusb.mk new file mode 100644 index 0000000000..6d7d7e734d --- /dev/null +++ b/repos/libports/lib/mk/vfs_libusb.mk @@ -0,0 +1,7 @@ +SRC_CC = vfs_libusb.cc + +vpath %.cc $(REP_DIR)/src/lib/vfs/libusb + +SHARED_LIB = yes + +CC_CXX_WARN_STRICT = diff --git a/repos/libports/recipes/pkg/usb_webcam/archives b/repos/libports/recipes/pkg/usb_webcam/archives index 15a5624f8a..1b2091cf70 100644 --- a/repos/libports/recipes/pkg/usb_webcam/archives +++ b/repos/libports/recipes/pkg/usb_webcam/archives @@ -11,5 +11,5 @@ _/src/rom_filter _/src/report_rom _/src/stdcxx _/src/vfs +_/src/vfs_libusb _/src/vfs_pipe - diff --git a/repos/libports/recipes/pkg/usb_webcam/runtime b/repos/libports/recipes/pkg/usb_webcam/runtime index ea7354a3f7..aa1db16023 100644 --- a/repos/libports/recipes/pkg/usb_webcam/runtime +++ b/repos/libports/recipes/pkg/usb_webcam/runtime @@ -6,6 +6,7 @@ + diff --git a/repos/libports/recipes/raw/usb_webcam/usb_webcam.config b/repos/libports/recipes/raw/usb_webcam/usb_webcam.config index a017619f88..7becd5177a 100644 --- a/repos/libports/recipes/raw/usb_webcam/usb_webcam.config +++ b/repos/libports/recipes/raw/usb_webcam/usb_webcam.config @@ -75,7 +75,11 @@ - 2018-01-01 00:01 + + + 2018-01-01 00:01 + + diff --git a/repos/libports/recipes/src/vfs_libusb/content.mk b/repos/libports/recipes/src/vfs_libusb/content.mk new file mode 100644 index 0000000000..9b99967540 --- /dev/null +++ b/repos/libports/recipes/src/vfs_libusb/content.mk @@ -0,0 +1,9 @@ +MIRROR_FROM_REP_DIR := lib/mk/vfs_libusb.mk src/lib/vfs/libusb + +content: $(MIRROR_FROM_REP_DIR) LICENSE + +$(MIRROR_FROM_REP_DIR): + $(mirror_from_rep_dir) + +LICENSE: + cp $(GENODE_DIR)/LICENSE $@ diff --git a/repos/libports/recipes/src/vfs_libusb/hash b/repos/libports/recipes/src/vfs_libusb/hash new file mode 100644 index 0000000000..6d1608f261 --- /dev/null +++ b/repos/libports/recipes/src/vfs_libusb/hash @@ -0,0 +1 @@ +2022-02-01 a4dec9e3717d7b06e20b057905b2c5e8c0608d93 diff --git a/repos/libports/recipes/src/vfs_libusb/used_apis b/repos/libports/recipes/src/vfs_libusb/used_apis new file mode 100644 index 0000000000..5830357be3 --- /dev/null +++ b/repos/libports/recipes/src/vfs_libusb/used_apis @@ -0,0 +1,5 @@ +base +os +so +usb_session +vfs diff --git a/repos/libports/run/smartcard.run b/repos/libports/run/smartcard.run index 379ccffa08..6ba9f2ffca 100644 --- a/repos/libports/run/smartcard.run +++ b/repos/libports/run/smartcard.run @@ -26,6 +26,7 @@ set build_components { core init timer drivers/usb_host test/smartcard + lib/vfs/libusb lib/vfs/pipe } @@ -91,7 +92,11 @@ append config { - 2018-01-01 00:01 + + + 2018-01-01 00:01 + + @@ -116,7 +121,7 @@ set boot_modules { core init timer test-smartcard ld.lib.so pcsc-lite.lib.so ccid.lib.so libusb.lib.so libc.lib.so vfs.lib.so libm.lib.so posix.lib.so - Info.plist vfs_pipe.lib.so + Info.plist vfs_libusb.lib.so vfs_pipe.lib.so } lappend boot_modules [usb_host_drv_binary] diff --git a/repos/libports/src/lib/libusb/README b/repos/libports/src/lib/libusb/README index 97462f0452..8ff3c2b4b1 100644 --- a/repos/libports/src/lib/libusb/README +++ b/repos/libports/src/lib/libusb/README @@ -11,3 +11,9 @@ the application using libusb: See also the README file of the USB driver for additional policy attributes. + +The Genode USB connection object resides in a VFS plugin named 'vfs_libusb', +which is necessary to handle the 'ack_avail' signal in the libc kernel context. +So, an application using libusb needs to have a '' +node in its VFS configuration and the 'vfs_libusb.lib.so' file loadable at +runtime. diff --git a/repos/libports/src/lib/libusb/genode_usb_raw.cc b/repos/libports/src/lib/libusb/genode_usb_raw.cc index 6cb784d581..e2e960bc32 100644 --- a/repos/libports/src/lib/libusb/genode_usb_raw.cc +++ b/repos/libports/src/lib/libusb/genode_usb_raw.cc @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -36,66 +37,9 @@ static Genode::Env &genode_env() abort(); } - -struct Usb_ep -{ - Genode::Entrypoint _ep; - pthread_t _pthread; - - void _handle_pthread_registration() - { - Genode::Thread *myself = Genode::Thread::myself(); - if (!myself || Libc::pthread_create_from_thread(&_pthread, *myself, &myself)) { - Genode::error("cannot register thread for pthread"); - return; - } - } - - Genode::Io_signal_handler _pthread_reg_sigh { - _ep, *this, &Usb_ep::_handle_pthread_registration }; - - Usb_ep(Genode::Env &env, size_t stack_size, char const *name, - Genode::Affinity::Location location) - : _ep { env, stack_size, name, location } - { - Genode::Signal_transmitter(_pthread_reg_sigh).submit(); - } - - Genode::Entrypoint &ep() { return _ep; } -}; - - -/* - * Entrypoint for handling 'ack avail' signals from the USB driver. - * - * The entrypoint is needed because the main thread of an application - * using libusb might be blocking on a pthread locking function, which - * currently do not dispatch signals while blocking. - */ -static Genode::Entrypoint &ep() -{ - static Usb_ep instance(genode_env(), - 2*1024*sizeof(Genode::addr_t), - "usb_ack_ep", - Genode::Affinity::Location()); - return instance.ep(); -} - - static Libc::Allocator libc_alloc { }; -/* - * Prevent modification of packet allocator - * by multiple threads. - */ -static Genode::Mutex &usb_packet_allocator_mutex() -{ - static Genode::Mutex instance; - return instance; -} - - struct Completion : Usb::Completion { struct usbi_transfer *itransfer; @@ -111,36 +55,95 @@ struct Usb_device { private: - Genode::Allocator_avl _alloc { &libc_alloc }; - - Genode::Io_signal_handler _state_changed_handler { - genode_env().ep(), *this, &Usb_device::_handle_state_changed }; - unsigned _open { 0 }; - void _handle_state_changed() + public: + + Usb::Connection *usb_connection; + + Usb::Device_descriptor device_descriptor; + Usb::Config_descriptor config_descriptor; + char *raw_config_descriptor = nullptr; + + Usb_device(Usb::Connection *usb) + : usb_connection(usb) { - /* - * The handler is installed only to receive state-change signals - * from the USB connection using the 'Usb_device' constructor. - */ + Genode::log("libusb: waiting until device is plugged..."); + while (!usb_connection->plugged()) + genode_env().ep().wait_and_dispatch_one_io_signal(); + Genode::log("libusb: device is plugged"); + + usb_connection->config_descriptor(&device_descriptor, &config_descriptor); + + raw_config_descriptor = (char*)malloc(config_descriptor.total_length); + + Usb::Packet_descriptor p = + usb_connection->source()->alloc_packet(config_descriptor.total_length); + + p.type = Usb::Packet_descriptor::CTRL; + p.control.request = LIBUSB_REQUEST_GET_DESCRIPTOR; + p.control.request_type = LIBUSB_ENDPOINT_IN; + p.control.value = (LIBUSB_DT_CONFIG << 8) | 0; + p.control.index = 0; + + usb_connection->source()->submit_packet(p); + + while (!usb_connection->source()->ack_avail()) + genode_env().ep().wait_and_dispatch_one_io_signal(); + + p = usb_connection->source()->get_acked_packet(); + + if (!p.succeded) + Genode::error(__PRETTY_FUNCTION__, + ": could not read raw configuration descriptor"); + + if (p.control.actual_size != config_descriptor.total_length) + Genode::error(__PRETTY_FUNCTION__, + ": received configuration descriptor of unexpected size"); + + char *packet_content = usb_connection->source()->packet_content(p); + Genode::memcpy(raw_config_descriptor, packet_content, + config_descriptor.total_length); + + usb_connection->source()->release_packet(p); } - Genode::Io_signal_handler _ack_avail_handler { - ep(), *this, &Usb_device::_handle_ack_avail }; + ~Usb_device() + { + free(raw_config_descriptor); + } - void _handle_ack_avail() + bool altsetting(int number, int alt_setting) + { + if (!usb_connection->source()->ready_to_submit()) + return false; + + Usb::Packet_descriptor p = + usb_connection->source()->alloc_packet(0); + + p.type = Usb::Packet_descriptor::ALT_SETTING; + p.interface.number = number; + p.interface.alt_setting = alt_setting; + + usb_connection->source()->submit_packet(p); + + return true; + } + + void close() { _open--; } + void open() { _open++; } + + void handle_events() { struct libusb_context *ctx = nullptr; - while (usb_connection.source()->ack_avail()) { + while (usb_connection->source()->ack_avail()) { Usb::Packet_descriptor p = - usb_connection.source()->get_acked_packet(); + usb_connection->source()->get_acked_packet(); if (p.type == Usb::Packet_descriptor::ALT_SETTING) { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - usb_connection.source()->release_packet(p); + usb_connection->source()->release_packet(p); continue; } @@ -153,8 +156,7 @@ struct Usb_device destroy(libc_alloc, completion); if (_open == 0) { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - usb_connection.source()->release_packet(p); + usb_connection->source()->release_packet(p); continue; } @@ -162,15 +164,12 @@ struct Usb_device if (!p.succeded) Genode::error("USB transfer failed: ", (unsigned)p.type); itransfer->transferred = 0; - { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - usb_connection.source()->release_packet(p); - } + usb_connection->source()->release_packet(p); usbi_signal_transfer_completion(itransfer); continue; } - char *packet_content = usb_connection.source()->packet_content(p); + char *packet_content = usb_connection->source()->packet_content(p); struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); @@ -239,15 +238,11 @@ struct Usb_device default: Genode::error(__PRETTY_FUNCTION__, ": unsupported transfer type"); - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - usb_connection.source()->release_packet(p); + usb_connection->source()->release_packet(p); continue; } - { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - usb_connection.source()->release_packet(p); - } + usb_connection->source()->release_packet(p); usbi_signal_transfer_completion(itransfer); } @@ -255,100 +250,38 @@ struct Usb_device if (ctx != nullptr) usbi_signal_event(ctx); } - - public: - - Usb::Connection usb_connection { genode_env(), - &_alloc, - "usb_device", - 1024*1024, - _state_changed_handler }; - - Usb::Device_descriptor device_descriptor; - Usb::Config_descriptor config_descriptor; - char *raw_config_descriptor = nullptr; - - Usb_device() - { - Genode::log("libusb: waiting until device is plugged..."); - while (!usb_connection.plugged()) - genode_env().ep().wait_and_dispatch_one_io_signal(); - Genode::log("libusb: device is plugged"); - - usb_connection.config_descriptor(&device_descriptor, &config_descriptor); - - raw_config_descriptor = (char*)malloc(config_descriptor.total_length); - - Usb::Packet_descriptor p = - usb_connection.source()->alloc_packet(config_descriptor.total_length); - - p.type = Usb::Packet_descriptor::CTRL; - p.control.request = LIBUSB_REQUEST_GET_DESCRIPTOR; - p.control.request_type = LIBUSB_ENDPOINT_IN; - p.control.value = (LIBUSB_DT_CONFIG << 8) | 0; - p.control.index = 0; - - usb_connection.source()->submit_packet(p); - - while (!usb_connection.source()->ack_avail()) - genode_env().ep().wait_and_dispatch_one_io_signal(); - - p = usb_connection.source()->get_acked_packet(); - - if (!p.succeded) - Genode::error(__PRETTY_FUNCTION__, - ": could not read raw configuration descriptor"); - - if (p.control.actual_size != config_descriptor.total_length) - Genode::error(__PRETTY_FUNCTION__, - ": received configuration descriptor of unexpected size"); - - char *packet_content = usb_connection.source()->packet_content(p); - Genode::memcpy(raw_config_descriptor, packet_content, - config_descriptor.total_length); - - usb_connection.source()->release_packet(p); - - usb_connection.tx_channel()->sigh_ack_avail(_ack_avail_handler); - } - - ~Usb_device() - { - free(raw_config_descriptor); - } - - bool altsetting(int number, int alt_setting) - { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - - if (!usb_connection.source()->ready_to_submit()) - return false; - - Usb::Packet_descriptor p = - usb_connection.source()->alloc_packet(0); - - p.type = Usb::Packet_descriptor::ALT_SETTING; - p.interface.number = number; - p.interface.alt_setting = alt_setting; - - usb_connection.source()->submit_packet(p); - - return true; - } - - void close() { _open--; } - void open() { _open++; } }; -static Usb_device *device_instance; + +static int vfs_libusb_fd { -1 }; +static Usb::Connection *usb_connection { nullptr }; +static Usb_device *device_instance { nullptr }; + + +/* + * This function is called by the VFS plugin on 'open()' + * to pass a pointer to the USB connection. + */ +void libusb_genode_usb_connection(Usb::Connection *usb) +{ + usb_connection = usb; +} + static int genode_init(struct libusb_context* ctx) { if (!device_instance) { - device_instance = new (libc_alloc) Usb_device; + vfs_libusb_fd = open("/dev/libusb", O_RDONLY); + if (vfs_libusb_fd == -1) { + Genode::error("could not open /dev/libusb"); + return LIBUSB_ERROR_OTHER; + } + device_instance = new (libc_alloc) Usb_device(usb_connection); } else { Genode::error("tried to init genode usb context twice"); + return LIBUSB_ERROR_OTHER; } + return LIBUSB_SUCCESS; } @@ -358,6 +291,9 @@ static void genode_exit(void) if (device_instance) { destroy(libc_alloc, device_instance); device_instance = nullptr; + close(vfs_libusb_fd); + usb_connection = nullptr; + vfs_libusb_fd = -1; } } @@ -436,7 +372,8 @@ static int genode_open(struct libusb_device_handle *dev_handle) if (device_instance) device_instance->open(); - return LIBUSB_SUCCESS; + return usbi_add_pollfd(HANDLE_CTX(dev_handle), vfs_libusb_fd, + POLLIN); } @@ -444,6 +381,8 @@ static void genode_close(struct libusb_device_handle *dev_handle) { if (device_instance) device_instance->close(); + + usbi_remove_pollfd(HANDLE_CTX(dev_handle), vfs_libusb_fd); } @@ -511,7 +450,7 @@ static int genode_claim_interface(struct libusb_device_handle *dev_handle, Usb_device *usb_device = *(Usb_device**)dev_handle->dev->os_priv; try { - usb_device->usb_connection.claim_interface(interface_number); + usb_device->usb_connection->claim_interface(interface_number); } catch (Usb::Session::Interface_not_found) { Genode::error(__PRETTY_FUNCTION__, ": interface not found"); return LIBUSB_ERROR_NOT_FOUND; @@ -533,7 +472,7 @@ static int genode_release_interface(struct libusb_device_handle *dev_handle, Usb_device *usb_device = *(Usb_device**)dev_handle->dev->os_priv; try { - usb_device->usb_connection.release_interface(interface_number); + usb_device->usb_connection->release_interface(interface_number); } catch (Usb::Session::Interface_not_found) { Genode::error(__PRETTY_FUNCTION__, ": interface not found"); return LIBUSB_ERROR_NOT_FOUND; @@ -564,7 +503,7 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) Usb_device *usb_device = *(Usb_device**)transfer->dev_handle->dev->os_priv; - if (!usb_device->usb_connection.source()->ready_to_submit()) + if (!usb_device->usb_connection->source()->ready_to_submit()) return LIBUSB_ERROR_BUSY; switch (transfer->type) { @@ -577,8 +516,7 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) Usb::Packet_descriptor p; try { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - p = usb_device->usb_connection.source()->alloc_packet(setup->wLength); + p = usb_device->usb_connection->source()->alloc_packet(setup->wLength); } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { return LIBUSB_ERROR_BUSY; } @@ -596,14 +534,14 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) LIBUSB_ENDPOINT_OUT) { char *packet_content = - usb_device->usb_connection.source()->packet_content(p); + usb_device->usb_connection->source()->packet_content(p); Genode::memcpy(packet_content, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, setup->wLength); } - usb_device->usb_connection.source()->submit_packet(p); + usb_device->usb_connection->source()->submit_packet(p); return LIBUSB_SUCCESS; } @@ -622,8 +560,7 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) Usb::Packet_descriptor p; try { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - p = usb_device->usb_connection.source()->alloc_packet(transfer->length); + p = usb_device->usb_connection->source()->alloc_packet(transfer->length); } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { return LIBUSB_ERROR_BUSY; } @@ -640,12 +577,12 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) if (IS_XFEROUT(transfer)) { char *packet_content = - usb_device->usb_connection.source()->packet_content(p); + usb_device->usb_connection->source()->packet_content(p); Genode::memcpy(packet_content, transfer->buffer, transfer->length); } - usb_device->usb_connection.source()->submit_packet(p); + usb_device->usb_connection->source()->submit_packet(p); return LIBUSB_SUCCESS; } @@ -659,8 +596,7 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) Usb::Packet_descriptor p; try { - Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); - p = usb_device->usb_connection.source()->alloc_packet(total_length); + p = usb_device->usb_connection->source()->alloc_packet(total_length); } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { return LIBUSB_ERROR_BUSY; } @@ -679,12 +615,12 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) if (IS_XFEROUT(transfer)) { char *packet_content = - usb_device->usb_connection.source()->packet_content(p); + usb_device->usb_connection->source()->packet_content(p); Genode::memcpy(packet_content, transfer->buffer, transfer->length); } - usb_device->usb_connection.source()->submit_packet(p); + usb_device->usb_connection->source()->submit_packet(p); return LIBUSB_SUCCESS; } @@ -706,6 +642,19 @@ static int genode_cancel_transfer(struct usbi_transfer * itransfer) static void genode_clear_transfer_priv(struct usbi_transfer * itransfer) { } +static int genode_handle_events(struct libusb_context *, + struct pollfd *, + POLL_NFDS_TYPE, int) +{ + if (device_instance) { + device_instance->handle_events(); + return LIBUSB_SUCCESS; + } + + return LIBUSB_ERROR_NO_DEVICE; +} + + static int genode_handle_transfer_completion(struct usbi_transfer * itransfer) { enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; @@ -768,7 +717,7 @@ const struct usbi_os_backend genode_usb_raw_backend = { /*.cancel_transfer =*/ genode_cancel_transfer, /*.clear_transfer_priv =*/ genode_clear_transfer_priv, - /*.handle_events =*/ NULL, + /*.handle_events =*/ genode_handle_events, /*.handle_transfer_completion =*/ genode_handle_transfer_completion, /*.clock_gettime =*/ genode_clock_gettime, @@ -808,3 +757,4 @@ void __attribute__((constructor)) init_libc_libusb(void) { static Plugin plugin; } + diff --git a/repos/libports/src/lib/vfs/libusb/target.mk b/repos/libports/src/lib/vfs/libusb/target.mk new file mode 100644 index 0000000000..7e63071cc8 --- /dev/null +++ b/repos/libports/src/lib/vfs/libusb/target.mk @@ -0,0 +1,4 @@ +TARGET = dummy-vfs_libusb +LIBS = vfs_libusb + +CC_CXX_WARN_STRICT = diff --git a/repos/libports/src/lib/vfs/libusb/vfs_libusb.cc b/repos/libports/src/lib/vfs/libusb/vfs_libusb.cc new file mode 100644 index 0000000000..dde8a44b04 --- /dev/null +++ b/repos/libports/src/lib/vfs/libusb/vfs_libusb.cc @@ -0,0 +1,144 @@ +/* + * \brief libusb file system + * \author Christian Prochaska + * \date 2022-01-31 + */ + +/* + * Copyright (C) 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. + */ + + +/* Genode includes */ +#include +#include +#include +#include +#include + + +/* + * This function is implemented in the Genode backend + * of libusb. + */ +extern void libusb_genode_usb_connection(Usb::Connection *); + +class Libusb_file_system : public Vfs::Single_file_system +{ + private: + + Vfs::Env &_env; + + class Libusb_vfs_handle : public Single_vfs_handle + { + private: + + Genode::Env &_env; + Genode::Allocator_avl _alloc_avl; + Usb::Connection _usb_connection; + + Genode::Io_signal_handler _state_changed_handler { + _env.ep(), *this, &Libusb_vfs_handle::_handle_state_changed }; + + void _handle_state_changed() + { + /* + * The handler is installed only to receive state-change + * signals from the USB connection using the 'Usb_device' + * constructor. + */ + } + + Genode::Io_signal_handler _ack_avail_handler { + _env.ep(), *this, &Libusb_vfs_handle::_handle_ack_avail }; + + void _handle_ack_avail() + { + io_progress_response(); + } + + public: + + Libusb_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Genode::Env &env) + : Single_vfs_handle(ds, fs, alloc, 0), + _env(env), _alloc_avl(&alloc), + _usb_connection(_env, &_alloc_avl, + "usb_device", + 1024*1024, + _state_changed_handler) + { + _usb_connection.tx_channel()->sigh_ack_avail(_ack_avail_handler); + libusb_genode_usb_connection(&_usb_connection); + } + + bool read_ready() override + { + return _usb_connection.source()->ack_avail(); + } + + Read_result read(char *dst, Vfs::file_size count, + Vfs::file_size &out_count) override + { + return READ_ERR_IO; + } + + Write_result write(char const *src, Vfs::file_size count, + Vfs::file_size &out_count) override + { + return WRITE_ERR_IO; + } + }; + + public: + + Libusb_file_system(Vfs::Env &env, + Genode::Xml_node config) + : + Single_file_system(Vfs::Node_type::CONTINUOUS_FILE, name(), + Vfs::Node_rwx::ro(), config), + _env(env) { } + + ~Libusb_file_system() { } + + static char const *name() { return "libusb"; } + char const *type() override { return "libusb"; } + + /********************************* + ** Directory service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) + Libusb_vfs_handle(*this, *this, alloc, _env.env()); + return OPEN_OK; + } + +}; + + +struct Libusb_factory : Vfs::File_system_factory +{ + Vfs::File_system *create(Vfs::Env &env, Genode::Xml_node node) override + { + return new (env.alloc()) Libusb_file_system(env, node); + } +}; + + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + static Libusb_factory factory; + return &factory; +}