mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-24 04:55:42 +00:00
os: introduce C-API to Genode services
This commit introduces a C-API to the Uplink session, as well as to serve as a Block service. It can be used by drivers ported from C-only projects, like the Linux kernel, or BSD kernels for instance. Fix #4226
This commit is contained in:
parent
1a526e73a3
commit
ee045a68cc
79
repos/os/include/genode_c_api/base.h
Normal file
79
repos/os/include/genode_c_api/base.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* \brief C interface to Genode's base types
|
||||
* \author Norman Feske
|
||||
* \date 2021-07-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 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.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__GENODE_C_API__BASE_H_
|
||||
#define _INCLUDE__GENODE_C_API__BASE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**********************************************
|
||||
** Forward-declared types used in the C API **
|
||||
**********************************************/
|
||||
|
||||
struct genode_env;
|
||||
struct genode_allocator;
|
||||
struct genode_signal_handler;
|
||||
struct genode_attached_dataspace;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
** Mapping between C types and their corresponding Genode C++ types **
|
||||
**********************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/allocator.h>
|
||||
#include <base/attached_dataspace.h>
|
||||
|
||||
struct genode_env : Genode::Env { };
|
||||
struct genode_allocator : Genode::Allocator { };
|
||||
struct genode_signal_handler : Genode::Signal_dispatcher_base { };
|
||||
struct genode_attached_dataspace : Genode::Attached_dataspace { };
|
||||
|
||||
static inline auto genode_env_ptr(Genode::Env &env)
|
||||
{
|
||||
return static_cast<genode_env *>(&env);
|
||||
}
|
||||
|
||||
static inline auto genode_allocator_ptr(Genode::Allocator &alloc)
|
||||
{
|
||||
return static_cast<genode_allocator *>(&alloc);
|
||||
}
|
||||
|
||||
static inline auto genode_signal_handler_ptr(Genode::Signal_dispatcher_base &sigh)
|
||||
{
|
||||
return static_cast<genode_signal_handler *>(&sigh);
|
||||
}
|
||||
|
||||
static inline Genode::Signal_context_capability cap(genode_signal_handler *sigh_ptr)
|
||||
{
|
||||
Genode::Signal_dispatcher_base *dispatcher_ptr = sigh_ptr;
|
||||
return *static_cast<Genode::Signal_handler<Genode::Meta::Empty> *>(dispatcher_ptr);
|
||||
}
|
||||
|
||||
static inline auto genode_attached_dataspace_ptr(Genode::Attached_dataspace &ds)
|
||||
{
|
||||
return static_cast<genode_attached_dataspace *>(&ds);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _INCLUDE__GENODE_C_API__BASE_H_ */
|
102
repos/os/include/genode_c_api/block.h
Normal file
102
repos/os/include/genode_c_api/block.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* \brief C-API Genode block backend
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-07-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2021 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.
|
||||
*/
|
||||
|
||||
#ifndef _GENODE_C_API__BLOCK_H_
|
||||
#define _GENODE_C_API__BLOCK_H_
|
||||
|
||||
#include <genode_c_api/base.h>
|
||||
|
||||
|
||||
struct genode_block_session; /* definition is private to the implementation */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/********************
|
||||
** Initialization **
|
||||
********************/
|
||||
|
||||
/**
|
||||
* Callback called during peer session request to allocate dma-capable shared buffer
|
||||
*/
|
||||
typedef struct genode_attached_dataspace * (*genode_block_alloc_peer_buffer_t)
|
||||
(unsigned long size);
|
||||
|
||||
/**
|
||||
* Callback called when closing peer session to free shared buffer
|
||||
*/
|
||||
typedef void (*genode_block_free_peer_buffer_t)
|
||||
(struct genode_attached_dataspace * ds);
|
||||
|
||||
/**
|
||||
* Initialize block root component
|
||||
*
|
||||
* \param handler signal handler to be installed at each block session
|
||||
*/
|
||||
void genode_block_init(struct genode_env *env,
|
||||
struct genode_allocator *alloc,
|
||||
struct genode_signal_handler *handler,
|
||||
genode_block_alloc_peer_buffer_t,
|
||||
genode_block_free_peer_buffer_t);
|
||||
|
||||
|
||||
/**************************************
|
||||
** Block device lifetime management **
|
||||
**************************************/
|
||||
|
||||
typedef unsigned long long genode_block_sector_t;
|
||||
|
||||
void genode_block_announce_device(const char * name,
|
||||
genode_block_sector_t sectors,
|
||||
int writeable);
|
||||
|
||||
void genode_block_discontinue_device(const char * name);
|
||||
|
||||
|
||||
/************************************
|
||||
** Block session request handling **
|
||||
************************************/
|
||||
|
||||
enum Operation {
|
||||
GENODE_BLOCK_READ,
|
||||
GENODE_BLOCK_WRITE,
|
||||
GENODE_BLOCK_SYNC,
|
||||
GENODE_BLOCK_UNAVAIL,
|
||||
};
|
||||
|
||||
struct genode_block_request {
|
||||
int op;
|
||||
genode_block_sector_t blk_nr;
|
||||
genode_block_sector_t blk_cnt;
|
||||
void * addr;
|
||||
};
|
||||
|
||||
struct genode_block_session * genode_block_session_by_name(const char * name);
|
||||
|
||||
struct genode_block_request *
|
||||
genode_block_request_by_session(struct genode_block_session * const session);
|
||||
|
||||
void genode_block_ack_request(struct genode_block_session * const session,
|
||||
struct genode_block_request * const request,
|
||||
int success);
|
||||
|
||||
void genode_block_notify_peers(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _GENODE_C_API__BLOCK_H_ */
|
107
repos/os/include/genode_c_api/uplink.h
Normal file
107
repos/os/include/genode_c_api/uplink.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* \brief C interface to Genode's uplink session
|
||||
* \author Norman Feske
|
||||
* \date 2021-07-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 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.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__GENODE_C_API__UPLINK_H_
|
||||
#define _INCLUDE__GENODE_C_API__UPLINK_H_
|
||||
|
||||
#include <genode_c_api/base.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize uplink handling
|
||||
*
|
||||
* \param sigh signal handler to be installed at the uplink connection
|
||||
*/
|
||||
void genode_uplink_init(struct genode_env *,
|
||||
struct genode_allocator *,
|
||||
struct genode_signal_handler *);
|
||||
|
||||
/**
|
||||
* Wake up uplink server if progress can be made at the server side
|
||||
*
|
||||
* This function should be called whenever the component becomes idle.
|
||||
*/
|
||||
void genode_uplink_notify_peers(void);
|
||||
|
||||
|
||||
|
||||
struct genode_uplink; /* definition is private to the implementation */
|
||||
|
||||
|
||||
/*********************
|
||||
** Uplink lifetime **
|
||||
*********************/
|
||||
|
||||
struct genode_uplink_args
|
||||
{
|
||||
unsigned char mac_address[6];
|
||||
char const *label;
|
||||
};
|
||||
|
||||
struct genode_uplink *genode_uplink_create(struct genode_uplink_args const *);
|
||||
|
||||
void genode_uplink_destroy(struct genode_uplink *);
|
||||
|
||||
|
||||
/*************************************************
|
||||
** Transmit packets towards the uplink session **
|
||||
*************************************************/
|
||||
|
||||
struct genode_uplink_tx_packet_context;
|
||||
|
||||
/**
|
||||
* Callback called by 'genode_uplink_tx_packet' to provide the content
|
||||
*/
|
||||
typedef unsigned long (*genode_uplink_tx_packet_content_t)
|
||||
(struct genode_uplink_tx_packet_context *, char *dst, unsigned long dst_len);
|
||||
|
||||
/**
|
||||
* Process packet transmission
|
||||
*
|
||||
* \return true if progress was made
|
||||
*/
|
||||
bool genode_uplink_tx_packet(struct genode_uplink *,
|
||||
genode_uplink_tx_packet_content_t,
|
||||
struct genode_uplink_tx_packet_context *);
|
||||
|
||||
|
||||
/*********************************************
|
||||
** Receive packets from the uplink session **
|
||||
*********************************************/
|
||||
|
||||
struct genode_uplink_rx_context;
|
||||
|
||||
typedef enum { GENODE_UPLINK_RX_REJECTED,
|
||||
GENODE_UPLINK_RX_ACCEPTED,
|
||||
GENODE_UPLINK_RX_RETRY } genode_uplink_rx_result_t;
|
||||
|
||||
typedef genode_uplink_rx_result_t (*genode_uplink_rx_one_packet_t)
|
||||
(struct genode_uplink_rx_context *, char const *ptr, unsigned long len);
|
||||
|
||||
/**
|
||||
* Process packet reception
|
||||
*
|
||||
* \return true if progress was made
|
||||
*/
|
||||
bool genode_uplink_rx(struct genode_uplink *,
|
||||
genode_uplink_rx_one_packet_t rx_one_packet,
|
||||
struct genode_uplink_rx_context *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INCLUDE__GENODE_C_API__UPLINK_H_ */
|
7
repos/os/recipes/api/genode_c_api/content.mk
Normal file
7
repos/os/recipes/api/genode_c_api/content.mk
Normal file
@ -0,0 +1,7 @@
|
||||
content: include/genode_c_api LICENSE
|
||||
|
||||
include/genode_c_api:
|
||||
$(mirror_from_rep_dir)
|
||||
|
||||
LICENSE:
|
||||
cp $(GENODE_DIR)/LICENSE $@
|
1
repos/os/recipes/api/genode_c_api/hash
Normal file
1
repos/os/recipes/api/genode_c_api/hash
Normal file
@ -0,0 +1 @@
|
||||
2021-07-20 fc931fb44dcb01b2175db78c6c153f69eaf99027
|
411
repos/os/src/lib/genode_c_api/block.cc
Normal file
411
repos/os/src/lib/genode_c_api/block.cc
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* \brief Genode block service provider C-API
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-07-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2021 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 <base/attached_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/env.h>
|
||||
#include <block/request_stream.h>
|
||||
#include <root/component.h>
|
||||
#include <os/reporter.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
#include <genode_c_api/block.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
struct genode_block_session : Rpc_object<Block::Session>
|
||||
{
|
||||
enum { MAX_REQUESTS = 32 };
|
||||
|
||||
struct Request {
|
||||
enum State { FREE, IN_FLY, DONE };
|
||||
|
||||
State state { FREE };
|
||||
genode_block_request dev_req { GENODE_BLOCK_UNAVAIL, 0, 0, nullptr };
|
||||
Block::Request peer_req {};
|
||||
};
|
||||
|
||||
Attached_dataspace & ds;
|
||||
Block::Request_stream rs;
|
||||
Request requests[MAX_REQUESTS];
|
||||
|
||||
template <typename FUNC>
|
||||
void first_request(Request::State state, FUNC const & fn)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_REQUESTS; idx++) {
|
||||
if (requests[idx].state == state) {
|
||||
fn(requests[idx]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each_request(Request::State state, FUNC const & fn)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_REQUESTS; idx++) {
|
||||
if (requests[idx].state == state)
|
||||
fn(requests[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
genode_block_session(Env & env,
|
||||
Block::Session::Info info,
|
||||
Signal_context_capability cap,
|
||||
size_t buffer_size);
|
||||
|
||||
Info info() const override { return rs.info(); }
|
||||
|
||||
Capability<Tx> tx_cap() override { return rs.tx_cap(); }
|
||||
|
||||
genode_block_request * request();
|
||||
void ack(genode_block_request * req, bool success);
|
||||
|
||||
void notify_peers() { rs.wakeup_client_if_needed(); }
|
||||
};
|
||||
|
||||
|
||||
class Root : public Root_component<genode_block_session>
|
||||
{
|
||||
private:
|
||||
|
||||
enum { MAX_BLOCK_DEVICES = 32 };
|
||||
|
||||
struct Session_info {
|
||||
using Name = String<64>;
|
||||
|
||||
Name name;
|
||||
Block::Session::Info info;
|
||||
genode_block_session * block_session { nullptr };
|
||||
|
||||
Session_info(const char * name, Block::Session::Info info)
|
||||
: name(name), info(info) {}
|
||||
};
|
||||
|
||||
Env & _env;
|
||||
Signal_context_capability _sigh_cap;
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
Reporter _reporter { _env, "block_devices" };
|
||||
Constructible<Session_info> _sessions[MAX_BLOCK_DEVICES];
|
||||
bool _announced { false };
|
||||
|
||||
Root(const Root&);
|
||||
Root & operator=(const Root&);
|
||||
|
||||
genode_block_session * _create_session(const char * args,
|
||||
Affinity const &) override;
|
||||
|
||||
void _destroy_session(genode_block_session * session) override;
|
||||
|
||||
template <typename FUNC>
|
||||
void _for_each_session_info(FUNC const & fn)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_BLOCK_DEVICES; idx++)
|
||||
if (_sessions[idx].constructed())
|
||||
fn(*_sessions[idx]);
|
||||
}
|
||||
|
||||
void _report();
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid_block_device_id {};
|
||||
|
||||
Root(Env & env, Allocator & alloc, Signal_context_capability);
|
||||
|
||||
void announce_device(const char * name, Block::Session::Info info);
|
||||
void discontinue_device(const char * name);
|
||||
genode_block_session * session(const char * name);
|
||||
void notify_peers();
|
||||
};
|
||||
|
||||
|
||||
static ::Root * _block_root = nullptr;
|
||||
static genode_block_alloc_peer_buffer_t _alloc_peer_buffer = nullptr;
|
||||
static genode_block_free_peer_buffer_t _free_peer_buffer = nullptr;
|
||||
|
||||
|
||||
genode_block_request * genode_block_session::request()
|
||||
{
|
||||
using Response = Block::Request_stream::Response;
|
||||
|
||||
genode_block_request * ret = nullptr;
|
||||
|
||||
rs.with_requests([&] (Block::Request request) {
|
||||
|
||||
if (ret)
|
||||
return Response::RETRY;
|
||||
|
||||
/* ignored operations */
|
||||
if (request.operation.type == Block::Operation::Type::TRIM ||
|
||||
request.operation.type == Block::Operation::Type::INVALID) {
|
||||
request.success = true;
|
||||
return Response::REJECTED;
|
||||
}
|
||||
|
||||
Response response = Response::RETRY;
|
||||
|
||||
first_request(Request::FREE, [&] (Request & r) {
|
||||
|
||||
r.state = Request::IN_FLY;
|
||||
r.peer_req = request;
|
||||
|
||||
Block::Operation const op = request.operation;
|
||||
switch(op.type) {
|
||||
case Block::Operation::Type::SYNC:
|
||||
r.dev_req.op = GENODE_BLOCK_SYNC;
|
||||
break;
|
||||
case Block::Operation::Type::READ:
|
||||
r.dev_req.op = GENODE_BLOCK_READ;
|
||||
break;
|
||||
case Block::Operation::Type::WRITE:
|
||||
r.dev_req.op = GENODE_BLOCK_WRITE;
|
||||
break;
|
||||
default:
|
||||
r.dev_req.op = GENODE_BLOCK_UNAVAIL;
|
||||
};
|
||||
|
||||
r.dev_req.blk_nr = op.block_number;
|
||||
r.dev_req.blk_cnt = op.count;
|
||||
r.dev_req.addr = (void*)((addr_t)ds.local_addr<void>()
|
||||
+ request.offset);
|
||||
|
||||
ret = &r.dev_req;
|
||||
response = Response::ACCEPTED;
|
||||
});
|
||||
|
||||
return response;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void genode_block_session::ack(genode_block_request * req, bool success)
|
||||
{
|
||||
for_each_request(Request::IN_FLY, [&] (Request & r) {
|
||||
if (&r.dev_req == req)
|
||||
r.state = Request::DONE;
|
||||
});
|
||||
|
||||
/* Acknowledge any pending packets */
|
||||
rs.try_acknowledge([&](Block::Request_stream::Ack & ack) {
|
||||
first_request(Request::DONE, [&] (Request & r) {
|
||||
r.state = Request::FREE;
|
||||
r.peer_req.success = success;
|
||||
ack.submit(r.peer_req);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
genode_block_session::genode_block_session(Env & env,
|
||||
Block::Session::Info info,
|
||||
Signal_context_capability cap,
|
||||
size_t buffer_size)
|
||||
:
|
||||
ds(*static_cast<Attached_dataspace*>(_alloc_peer_buffer(buffer_size))),
|
||||
rs(env.rm(), ds.cap(), env.ep(), cap, info) { }
|
||||
|
||||
|
||||
genode_block_session * ::Root::_create_session(const char * args,
|
||||
Affinity const &)
|
||||
{
|
||||
Session_label const label = label_from_args(args);
|
||||
Session_policy const policy(label, _config.xml());
|
||||
Session_info::Name const device =
|
||||
policy.attribute_value("device", Session_info::Name());
|
||||
|
||||
Ram_quota const ram_quota = ram_quota_from_args(args);
|
||||
size_t const tx_buf_size =
|
||||
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
||||
|
||||
if (!tx_buf_size)
|
||||
throw Service_denied();
|
||||
|
||||
if (tx_buf_size > ram_quota.value) {
|
||||
error("insufficient 'ram_quota' from '", label, "',"
|
||||
" got ", ram_quota, ", need ", tx_buf_size);
|
||||
throw Insufficient_ram_quota();
|
||||
}
|
||||
|
||||
genode_block_session * ret = nullptr;
|
||||
|
||||
_for_each_session_info([&] (Session_info & si) {
|
||||
if (si.block_session || si.name != device)
|
||||
return;
|
||||
ret = new (md_alloc())
|
||||
genode_block_session(_env, si.info, _sigh_cap, tx_buf_size);
|
||||
si.block_session = ret;
|
||||
});
|
||||
|
||||
if (!ret) throw Service_denied();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ::Root::_destroy_session(genode_block_session * session)
|
||||
{
|
||||
_for_each_session_info([&] (Session_info & si) {
|
||||
if (si.block_session == session)
|
||||
si.block_session = nullptr;
|
||||
});
|
||||
|
||||
Attached_dataspace & ds = session->ds;
|
||||
Genode::destroy(md_alloc(), session);
|
||||
_free_peer_buffer(genode_attached_dataspace_ptr(ds));
|
||||
}
|
||||
|
||||
|
||||
void ::Root::_report()
|
||||
{
|
||||
if (!_config.xml().attribute_value("report", false))
|
||||
return;
|
||||
|
||||
_reporter.enabled(true);
|
||||
Reporter::Xml_generator xml(_reporter, [&] () {
|
||||
_for_each_session_info([&] (Session_info & si) {
|
||||
xml.node("device", [&] {
|
||||
xml.attribute("label", si.name);
|
||||
xml.attribute("block_size", si.info.block_size);
|
||||
xml.attribute("block_count", si.info.block_count);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void ::Root::announce_device(const char * name, Block::Session::Info info)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_BLOCK_DEVICES; idx++) {
|
||||
if (_sessions[idx].constructed())
|
||||
continue;
|
||||
|
||||
_sessions[idx].construct(name, info);
|
||||
if (!_announced) {
|
||||
_env.parent().announce(_env.ep().manage(*this));
|
||||
_announced = true;
|
||||
}
|
||||
_report();
|
||||
return;
|
||||
}
|
||||
|
||||
error("Could not announce driver for device ", name, ", no slot left!");
|
||||
}
|
||||
|
||||
|
||||
void ::Root::discontinue_device(const char * name)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_BLOCK_DEVICES; idx++) {
|
||||
if (!_sessions[idx].constructed() || _sessions[idx]->name != name)
|
||||
continue;
|
||||
|
||||
_sessions[idx].destruct();
|
||||
_report();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
genode_block_session * ::Root::session(const char * name)
|
||||
{
|
||||
genode_block_session * ret = nullptr;
|
||||
_for_each_session_info([&] (Session_info & si) {
|
||||
if (si.name == name)
|
||||
ret = si.block_session;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ::Root::notify_peers()
|
||||
{
|
||||
_for_each_session_info([&] (Session_info & si) {
|
||||
if (si.block_session)
|
||||
si.block_session->notify_peers();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
::Root::Root(Env & env, Allocator & alloc, Signal_context_capability cap)
|
||||
:
|
||||
Root_component<genode_block_session>(env.ep(), alloc),
|
||||
_env(env), _sigh_cap(cap) { }
|
||||
|
||||
|
||||
extern "C" void genode_block_init(genode_env *env_ptr,
|
||||
genode_allocator *alloc_ptr,
|
||||
genode_signal_handler *sigh_ptr,
|
||||
genode_block_alloc_peer_buffer_t alloc_func,
|
||||
genode_block_free_peer_buffer_t free_func)
|
||||
{
|
||||
static ::Root root(*static_cast<Env*>(env_ptr),
|
||||
*static_cast<Allocator*>(alloc_ptr),
|
||||
cap(sigh_ptr));
|
||||
_alloc_peer_buffer = alloc_func;
|
||||
_free_peer_buffer = free_func;
|
||||
_block_root = &root;
|
||||
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_block_announce_device(const char * name,
|
||||
unsigned long long sectors,
|
||||
int writeable)
|
||||
{
|
||||
enum { SIZE_LOG2_512 = 9 };
|
||||
|
||||
if (!_block_root)
|
||||
return;
|
||||
|
||||
_block_root->announce_device(name, { 1UL << SIZE_LOG2_512,
|
||||
sectors, SIZE_LOG2_512,
|
||||
(writeable != 0) ? true : false });
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_block_discontinue_device(const char * name)
|
||||
{
|
||||
if (_block_root)
|
||||
_block_root->discontinue_device(name);
|
||||
}
|
||||
|
||||
|
||||
extern "C" struct genode_block_session *
|
||||
genode_block_session_by_name(const char * name)
|
||||
{
|
||||
return _block_root ? _block_root->session(name) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
extern "C" struct genode_block_request *
|
||||
genode_block_request_by_session(struct genode_block_session * session)
|
||||
{
|
||||
return session ? session->request() : nullptr;
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_block_ack_request(struct genode_block_session * session,
|
||||
struct genode_block_request * req,
|
||||
int success)
|
||||
{
|
||||
if (session)
|
||||
session->ack(req, success ? true : false);
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_block_notify_peers()
|
||||
{
|
||||
if (_block_root) _block_root->notify_peers();
|
||||
}
|
230
repos/os/src/lib/genode_c_api/uplink.cc
Normal file
230
repos/os/src/lib/genode_c_api/uplink.cc
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* \brief C interface to Genode's uplink session
|
||||
* \author Norman Feske
|
||||
* \date 2021-07-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is distributed under the terms of the GNU General Public License
|
||||
* version 2.
|
||||
*/
|
||||
|
||||
#include <base/registry.h>
|
||||
#include <base/log.h>
|
||||
#include <base/session_label.h>
|
||||
#include <uplink_session/connection.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <genode_c_api/uplink.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
static Env *_env_ptr;
|
||||
static Allocator *_alloc_ptr;
|
||||
static Signal_context_capability _sigh { };
|
||||
static Registry<Registered<genode_uplink>> _uplinks { };
|
||||
|
||||
|
||||
struct genode_uplink : private Noncopyable, private Interface
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Allocator &_alloc;
|
||||
|
||||
Nic::Packet_allocator _packet_alloc { &_alloc };
|
||||
|
||||
enum { PACKET_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
|
||||
enum { BUF_SIZE = Uplink::Session::QUEUE_SIZE * PACKET_SIZE };
|
||||
|
||||
Net::Mac_address const _mac_address;
|
||||
|
||||
Session_label const _session_label;
|
||||
|
||||
Uplink::Connection _connection { _env, &_packet_alloc,
|
||||
BUF_SIZE, BUF_SIZE,
|
||||
_mac_address,
|
||||
_session_label.string() };
|
||||
|
||||
public:
|
||||
|
||||
genode_uplink(Env &env, Allocator &alloc, Signal_context_capability sigh,
|
||||
Net::Mac_address const &mac_address,
|
||||
Session_label const &session_label)
|
||||
:
|
||||
_env(env), _alloc(alloc),
|
||||
_mac_address(mac_address),
|
||||
_session_label(session_label)
|
||||
{
|
||||
_connection.rx_channel()->sigh_ready_to_ack (sigh);
|
||||
_connection.rx_channel()->sigh_packet_avail (sigh);
|
||||
_connection.tx_channel()->sigh_ack_avail (sigh);
|
||||
_connection.tx_channel()->sigh_ready_to_submit(sigh);
|
||||
|
||||
/* trigger signal handling once after construction */
|
||||
Signal_transmitter(sigh).submit();
|
||||
}
|
||||
|
||||
void notify_peer()
|
||||
{
|
||||
_connection.rx()->wakeup();
|
||||
_connection.tx()->wakeup();
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
bool tx_one_packet(FN const &fn)
|
||||
{
|
||||
bool progress = false;
|
||||
|
||||
Uplink::Session::Tx::Source &tx_source = *_connection.tx();
|
||||
|
||||
/*
|
||||
* Process acknowledgements
|
||||
*/
|
||||
|
||||
while (tx_source.ack_avail()) {
|
||||
tx_source.release_packet(tx_source.try_get_acked_packet());
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit packet
|
||||
*/
|
||||
|
||||
if (!tx_source.ready_to_submit(1))
|
||||
return progress;
|
||||
|
||||
typedef Uplink::Packet_descriptor Packet_descriptor;
|
||||
|
||||
Packet_descriptor packet { };
|
||||
size_t const max_bytes = Nic::Packet_allocator::DEFAULT_PACKET_SIZE;
|
||||
|
||||
try { packet = tx_source.alloc_packet(max_bytes); }
|
||||
catch (Uplink::Session::Tx::Source::Packet_alloc_failed) {
|
||||
return progress; /* packet-stream buffer is saturated */ }
|
||||
|
||||
char * const dst_ptr = tx_source.packet_content(packet);
|
||||
size_t const payload_bytes = min(max_bytes, fn(dst_ptr, max_bytes));
|
||||
|
||||
/* imprint payload size into packet descriptor */
|
||||
packet = Packet_descriptor(packet.offset(), payload_bytes);
|
||||
|
||||
tx_source.try_submit_packet(packet);
|
||||
progress = true;
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
bool for_each_rx_packet(FN const &fn)
|
||||
{
|
||||
/*
|
||||
* Implementation mirrored from (commented) block/request_stream.h
|
||||
*/
|
||||
bool overall_progress = false;
|
||||
|
||||
Uplink::Session::Rx::Sink &rx_sink = *_connection.rx();
|
||||
|
||||
for (;;) {
|
||||
|
||||
if (!rx_sink.packet_avail() || !rx_sink.ack_slots_free())
|
||||
break;
|
||||
|
||||
typedef Uplink::Packet_descriptor Packet_descriptor;
|
||||
|
||||
Packet_descriptor const packet = rx_sink.peek_packet();
|
||||
|
||||
bool const packet_valid = rx_sink.packet_valid(packet)
|
||||
&& (packet.offset() >= 0);
|
||||
|
||||
char const *content = rx_sink.packet_content(packet);
|
||||
|
||||
genode_uplink_rx_result_t const
|
||||
response = packet_valid
|
||||
? fn(content, packet.size())
|
||||
: GENODE_UPLINK_RX_REJECTED;
|
||||
|
||||
bool progress = false;
|
||||
|
||||
switch (response) {
|
||||
|
||||
case GENODE_UPLINK_RX_ACCEPTED:
|
||||
case GENODE_UPLINK_RX_REJECTED:
|
||||
|
||||
(void)rx_sink.try_get_packet();
|
||||
rx_sink.try_ack_packet(packet);
|
||||
progress = true;
|
||||
break;
|
||||
|
||||
case GENODE_UPLINK_RX_RETRY:
|
||||
break;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
overall_progress = true;
|
||||
|
||||
if (!progress)
|
||||
break;
|
||||
}
|
||||
return overall_progress;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void genode_uplink_init(genode_env *env_ptr,
|
||||
genode_allocator *alloc_ptr,
|
||||
genode_signal_handler *sigh_ptr)
|
||||
{
|
||||
_env_ptr = env_ptr;
|
||||
_alloc_ptr = alloc_ptr;
|
||||
_sigh = cap(sigh_ptr);
|
||||
}
|
||||
|
||||
|
||||
void genode_uplink_notify_peers()
|
||||
{
|
||||
_uplinks.for_each([&] (genode_uplink &uplink) {
|
||||
uplink.notify_peer(); });
|
||||
}
|
||||
|
||||
|
||||
bool genode_uplink_tx_packet(struct genode_uplink *uplink_ptr,
|
||||
genode_uplink_tx_packet_content_t tx_packet_content_cb,
|
||||
struct genode_uplink_tx_packet_context *ctx_ptr)
|
||||
{
|
||||
return uplink_ptr->tx_one_packet([&] (char *dst, size_t len) {
|
||||
return tx_packet_content_cb(ctx_ptr, dst, len); });
|
||||
}
|
||||
|
||||
|
||||
bool genode_uplink_rx(struct genode_uplink *uplink_ptr,
|
||||
genode_uplink_rx_one_packet_t rx_one_packet_cb,
|
||||
struct genode_uplink_rx_context *ctx_ptr)
|
||||
{
|
||||
return uplink_ptr->for_each_rx_packet([&] (char const *ptr, size_t len) {
|
||||
return rx_one_packet_cb(ctx_ptr, ptr, len); });
|
||||
}
|
||||
|
||||
|
||||
struct genode_uplink *genode_uplink_create(struct genode_uplink_args const *args)
|
||||
{
|
||||
if (!_env_ptr || !_alloc_ptr) {
|
||||
error("genode_uplink_create: missing call of genode_uplink_init");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Net::Mac_address mac { };
|
||||
for (unsigned i = 0; i < sizeof(args->mac_address); i++)
|
||||
mac.addr[i] = args->mac_address[i];
|
||||
|
||||
return new (*_alloc_ptr)
|
||||
Registered<genode_uplink>(_uplinks, *_env_ptr, *_alloc_ptr, _sigh,
|
||||
mac, Session_label(args->label));
|
||||
}
|
||||
|
||||
|
||||
void genode_uplink_destroy(struct genode_uplink *uplink_ptr)
|
||||
{
|
||||
destroy(*_alloc_ptr, uplink_ptr);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user