libusb: handle 'ack_avail' signals in a VFS plugin

Issue #4392
This commit is contained in:
Christian Prochaska 2022-02-01 17:39:37 +01:00 committed by Norman Feske
parent cf0d007fd4
commit d4d875f2e6
12 changed files with 329 additions and 193 deletions

View File

@ -0,0 +1,7 @@
SRC_CC = vfs_libusb.cc
vpath %.cc $(REP_DIR)/src/lib/vfs/libusb
SHARED_LIB = yes
CC_CXX_WARN_STRICT =

View File

@ -11,5 +11,5 @@ _/src/rom_filter
_/src/report_rom _/src/report_rom
_/src/stdcxx _/src/stdcxx
_/src/vfs _/src/vfs
_/src/vfs_libusb
_/src/vfs_pipe _/src/vfs_pipe

View File

@ -6,6 +6,7 @@
<rom label="init"/> <rom label="init"/>
<rom label="jpeg.lib.so"/> <rom label="jpeg.lib.so"/>
<rom label="vfs.lib.so"/> <rom label="vfs.lib.so"/>
<rom label="vfs_libusb.lib.so"/>
<rom label="vfs_pipe.lib.so"/> <rom label="vfs_pipe.lib.so"/>
<rom label="libc.lib.so"/> <rom label="libc.lib.so"/>
<rom label="libm.lib.so"/> <rom label="libm.lib.so"/>

View File

@ -75,7 +75,11 @@
<resource name="RAM" quantum="48M"/> <resource name="RAM" quantum="48M"/>
<config ld_verbose="no" enabled="yes" width="640" height="480" format="yuv" fps="15"> <config ld_verbose="no" enabled="yes" width="640" height="480" format="yuv" fps="15">
<vfs> <vfs>
<dir name="dev"> <log/> <inline name="rtc">2018-01-01 00:01</inline> </dir> <dir name="dev">
<log/>
<inline name="rtc">2018-01-01 00:01</inline>
<libusb/>
</dir>
<dir name="pipe"> <pipe/> </dir> <dir name="pipe"> <pipe/> </dir>
</vfs> </vfs>
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc" pipe="/pipe"/> <libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc" pipe="/pipe"/>

View File

@ -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 $@

View File

@ -0,0 +1 @@
2022-02-01 a4dec9e3717d7b06e20b057905b2c5e8c0608d93

View File

@ -0,0 +1,5 @@
base
os
so
usb_session
vfs

View File

@ -26,6 +26,7 @@ set build_components {
core init timer core init timer
drivers/usb_host drivers/usb_host
test/smartcard test/smartcard
lib/vfs/libusb
lib/vfs/pipe lib/vfs/pipe
} }
@ -91,7 +92,11 @@ append config {
<resource name="RAM" quantum="4M"/> <resource name="RAM" quantum="4M"/>
<config> <config>
<vfs> <vfs>
<dir name="dev"> <log/> <inline name="rtc">2018-01-01 00:01</inline> </dir> <dir name="dev">
<log/>
<inline name="rtc">2018-01-01 00:01</inline>
<libusb/>
</dir>
<dir name="pipe"> <pipe/> </dir> <dir name="pipe"> <pipe/> </dir>
<dir name="ifd-ccid.bundle"> <dir name="ifd-ccid.bundle">
<dir name="Contents"> <dir name="Contents">
@ -116,7 +121,7 @@ set boot_modules {
core init timer test-smartcard core init timer test-smartcard
ld.lib.so pcsc-lite.lib.so ccid.lib.so libusb.lib.so 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 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] lappend boot_modules [usb_host_drv_binary]

View File

@ -11,3 +11,9 @@ the application using libusb:
</config> </config>
See also the README file of the USB driver for additional policy attributes. 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 '<dir name="dev"><libusb/></dev>'
node in its VFS configuration and the 'vfs_libusb.lib.so' file loadable at
runtime.

View File

@ -17,6 +17,7 @@
#include <usb/usb.h> #include <usb/usb.h>
#include <usb_session/connection.h> #include <usb_session/connection.h>
#include <fcntl.h>
#include <time.h> #include <time.h>
#include <libc/allocator.h> #include <libc/allocator.h>
#include <libc-plugin/plugin.h> #include <libc-plugin/plugin.h>
@ -36,66 +37,9 @@ static Genode::Env &genode_env()
abort(); 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<Usb_ep> _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 { }; 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 Completion : Usb::Completion
{ {
struct usbi_transfer *itransfer; struct usbi_transfer *itransfer;
@ -111,36 +55,95 @@ struct Usb_device
{ {
private: private:
Genode::Allocator_avl _alloc { &libc_alloc };
Genode::Io_signal_handler<Usb_device> _state_changed_handler {
genode_env().ep(), *this, &Usb_device::_handle_state_changed };
unsigned _open { 0 }; 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)
{ {
/* Genode::log("libusb: waiting until device is plugged...");
* The handler is installed only to receive state-change signals while (!usb_connection->plugged())
* from the USB connection using the 'Usb_device' constructor. 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<Usb_device> _ack_avail_handler { ~Usb_device()
ep(), *this, &Usb_device::_handle_ack_avail }; {
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; struct libusb_context *ctx = nullptr;
while (usb_connection.source()->ack_avail()) { while (usb_connection->source()->ack_avail()) {
Usb::Packet_descriptor p = Usb::Packet_descriptor p =
usb_connection.source()->get_acked_packet(); usb_connection->source()->get_acked_packet();
if (p.type == Usb::Packet_descriptor::ALT_SETTING) { 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; continue;
} }
@ -153,8 +156,7 @@ struct Usb_device
destroy(libc_alloc, completion); destroy(libc_alloc, completion);
if (_open == 0) { if (_open == 0) {
Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); usb_connection->source()->release_packet(p);
usb_connection.source()->release_packet(p);
continue; continue;
} }
@ -162,15 +164,12 @@ struct Usb_device
if (!p.succeded) if (!p.succeded)
Genode::error("USB transfer failed: ", (unsigned)p.type); Genode::error("USB transfer failed: ", (unsigned)p.type);
itransfer->transferred = 0; itransfer->transferred = 0;
{ usb_connection->source()->release_packet(p);
Genode::Mutex::Guard guard(usb_packet_allocator_mutex());
usb_connection.source()->release_packet(p);
}
usbi_signal_transfer_completion(itransfer); usbi_signal_transfer_completion(itransfer);
continue; continue;
} }
char *packet_content = usb_connection.source()->packet_content(p); char *packet_content = usb_connection->source()->packet_content(p);
struct libusb_transfer *transfer = struct libusb_transfer *transfer =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
@ -239,15 +238,11 @@ struct Usb_device
default: default:
Genode::error(__PRETTY_FUNCTION__, Genode::error(__PRETTY_FUNCTION__,
": unsupported transfer type"); ": unsupported transfer type");
Genode::Mutex::Guard guard(usb_packet_allocator_mutex()); usb_connection->source()->release_packet(p);
usb_connection.source()->release_packet(p);
continue; continue;
} }
{ usb_connection->source()->release_packet(p);
Genode::Mutex::Guard guard(usb_packet_allocator_mutex());
usb_connection.source()->release_packet(p);
}
usbi_signal_transfer_completion(itransfer); usbi_signal_transfer_completion(itransfer);
} }
@ -255,100 +250,38 @@ struct Usb_device
if (ctx != nullptr) if (ctx != nullptr)
usbi_signal_event(ctx); 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) static int genode_init(struct libusb_context* ctx)
{ {
if (!device_instance) { 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 { } else {
Genode::error("tried to init genode usb context twice"); Genode::error("tried to init genode usb context twice");
return LIBUSB_ERROR_OTHER;
} }
return LIBUSB_SUCCESS; return LIBUSB_SUCCESS;
} }
@ -358,6 +291,9 @@ static void genode_exit(void)
if (device_instance) { if (device_instance) {
destroy(libc_alloc, device_instance); destroy(libc_alloc, device_instance);
device_instance = nullptr; 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) if (device_instance)
device_instance->open(); 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) if (device_instance)
device_instance->close(); 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; Usb_device *usb_device = *(Usb_device**)dev_handle->dev->os_priv;
try { try {
usb_device->usb_connection.claim_interface(interface_number); usb_device->usb_connection->claim_interface(interface_number);
} catch (Usb::Session::Interface_not_found) { } catch (Usb::Session::Interface_not_found) {
Genode::error(__PRETTY_FUNCTION__, ": interface not found"); Genode::error(__PRETTY_FUNCTION__, ": interface not found");
return LIBUSB_ERROR_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; Usb_device *usb_device = *(Usb_device**)dev_handle->dev->os_priv;
try { try {
usb_device->usb_connection.release_interface(interface_number); usb_device->usb_connection->release_interface(interface_number);
} catch (Usb::Session::Interface_not_found) { } catch (Usb::Session::Interface_not_found) {
Genode::error(__PRETTY_FUNCTION__, ": interface not found"); Genode::error(__PRETTY_FUNCTION__, ": interface not found");
return LIBUSB_ERROR_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; 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; return LIBUSB_ERROR_BUSY;
switch (transfer->type) { switch (transfer->type) {
@ -577,8 +516,7 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer)
Usb::Packet_descriptor p; Usb::Packet_descriptor p;
try { 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) { } catch (Usb::Session::Tx::Source::Packet_alloc_failed) {
return LIBUSB_ERROR_BUSY; return LIBUSB_ERROR_BUSY;
} }
@ -596,14 +534,14 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer)
LIBUSB_ENDPOINT_OUT) { LIBUSB_ENDPOINT_OUT) {
char *packet_content = char *packet_content =
usb_device->usb_connection.source()->packet_content(p); usb_device->usb_connection->source()->packet_content(p);
Genode::memcpy(packet_content, Genode::memcpy(packet_content,
transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE,
setup->wLength); setup->wLength);
} }
usb_device->usb_connection.source()->submit_packet(p); usb_device->usb_connection->source()->submit_packet(p);
return LIBUSB_SUCCESS; return LIBUSB_SUCCESS;
} }
@ -622,8 +560,7 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer)
Usb::Packet_descriptor p; Usb::Packet_descriptor p;
try { 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) { } catch (Usb::Session::Tx::Source::Packet_alloc_failed) {
return LIBUSB_ERROR_BUSY; return LIBUSB_ERROR_BUSY;
} }
@ -640,12 +577,12 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer)
if (IS_XFEROUT(transfer)) { if (IS_XFEROUT(transfer)) {
char *packet_content = 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, Genode::memcpy(packet_content, transfer->buffer,
transfer->length); transfer->length);
} }
usb_device->usb_connection.source()->submit_packet(p); usb_device->usb_connection->source()->submit_packet(p);
return LIBUSB_SUCCESS; return LIBUSB_SUCCESS;
} }
@ -659,8 +596,7 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer)
Usb::Packet_descriptor p; Usb::Packet_descriptor p;
try { 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) { } catch (Usb::Session::Tx::Source::Packet_alloc_failed) {
return LIBUSB_ERROR_BUSY; return LIBUSB_ERROR_BUSY;
} }
@ -679,12 +615,12 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer)
if (IS_XFEROUT(transfer)) { if (IS_XFEROUT(transfer)) {
char *packet_content = 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, Genode::memcpy(packet_content, transfer->buffer,
transfer->length); transfer->length);
} }
usb_device->usb_connection.source()->submit_packet(p); usb_device->usb_connection->source()->submit_packet(p);
return LIBUSB_SUCCESS; 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 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) static int genode_handle_transfer_completion(struct usbi_transfer * itransfer)
{ {
enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; 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, /*.cancel_transfer =*/ genode_cancel_transfer,
/*.clear_transfer_priv =*/ genode_clear_transfer_priv, /*.clear_transfer_priv =*/ genode_clear_transfer_priv,
/*.handle_events =*/ NULL, /*.handle_events =*/ genode_handle_events,
/*.handle_transfer_completion =*/ genode_handle_transfer_completion, /*.handle_transfer_completion =*/ genode_handle_transfer_completion,
/*.clock_gettime =*/ genode_clock_gettime, /*.clock_gettime =*/ genode_clock_gettime,
@ -808,3 +757,4 @@ void __attribute__((constructor)) init_libc_libusb(void)
{ {
static Plugin plugin; static Plugin plugin;
} }

View File

@ -0,0 +1,4 @@
TARGET = dummy-vfs_libusb
LIBS = vfs_libusb
CC_CXX_WARN_STRICT =

View File

@ -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 <base/allocator_avl.h>
#include <usb_session/connection.h>
#include <util/xml_node.h>
#include <vfs/file_system_factory.h>
#include <vfs/single_file_system.h>
/*
* 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<Libusb_vfs_handle> _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<Libusb_vfs_handle> _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;
}