/*
* \brief Glue between device-specific NIC driver code and Genode
* \author Norman Feske
* \date 2011-05-21
*/
/*
* Copyright (C) 2011-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__NIC__COMPONENT_H_
#define _INCLUDE__NIC__COMPONENT_H_
#include
#include
#include
#include
#include
#include
#include
enum { VERBOSE_RX = false };
namespace Nic {
class Session_component : public Genode::Allocator_avl,
public Session_rpc_object, public Rx_buffer_alloc
{
private:
Driver_factory &_driver_factory;
Driver &_driver;
/* rx packet descriptor */
Packet_descriptor _curr_rx_packet;
enum { TX_STACK_SIZE = 8*1024 };
class Tx_thread : public Genode::Thread
{
private:
Tx::Sink *_tx_sink;
Driver &_driver;
public:
Tx_thread(Tx::Sink *tx_sink, Driver &driver)
:
Genode::Thread("tx"),
_tx_sink(tx_sink), _driver(driver)
{
start();
}
void entry()
{
using namespace Genode;
while (true) {
/* block for packet from client */
Packet_descriptor packet = _tx_sink->get_packet();
if (!packet.valid()) {
PWRN("received invalid packet");
continue;
}
_driver.tx(_tx_sink->packet_content(packet),
packet.size());
/* acknowledge packet to the client */
if (!_tx_sink->ready_to_ack())
PDBG("need to wait until ready-for-ack");
_tx_sink->acknowledge_packet(packet);
}
}
} _tx_thread;
void dump()
{
using namespace Genode;
if (!VERBOSE_RX) return;
char *buf = (char *)_rx.source()->packet_content(_curr_rx_packet);
size_t size = _curr_rx_packet.size();
printf("rx packet:");
for (unsigned i = 0; i < size; i++)
printf("%02x,", buf[i]);
printf("\n");
}
public:
/**
* Constructor
*
* \param tx_buf_size buffer size for tx channel
* \param rx_buf_size buffer size for rx channel
* \param rx_block_alloc rx block allocator
* \param ep entry point used for packet stream
*/
Session_component(Genode::size_t tx_buf_size,
Genode::size_t rx_buf_size,
Nic::Driver_factory &driver_factory,
Genode::Rpc_entrypoint &ep)
:
Genode::Allocator_avl(Genode::env()->heap()),
Session_rpc_object(Genode::env()->ram_session()->alloc(tx_buf_size),
Genode::env()->ram_session()->alloc(rx_buf_size),
static_cast(this), ep),
_driver_factory(driver_factory),
_driver(*driver_factory.create(*this)),
_tx_thread(_tx.sink(), _driver)
{ }
/**
* Destructor
*/
~Session_component()
{
_driver_factory.destroy(&_driver);
}
/*******************************
** Rx_buffer_alloc interface **
*******************************/
void *alloc(Genode::size_t size)
{
/* assign rx packet descriptor */
_curr_rx_packet = _rx.source()->alloc_packet(size);
return _rx.source()->packet_content(_curr_rx_packet);
}
void submit()
{
/* check for acknowledgements from the client */
while (_rx.source()->ack_avail()) {
Packet_descriptor packet = _rx.source()->get_acked_packet();
/* free packet buffer */
_rx.source()->release_packet(packet);
}
dump();
_rx.source()->submit_packet(_curr_rx_packet);
/* invalidate rx packet descriptor */
_curr_rx_packet = Packet_descriptor();
}
/****************************
** Nic::Session interface **
****************************/
Mac_address mac_address() { return _driver.mac_address(); }
Tx::Sink* tx_sink() { return _tx.sink(); }
Rx::Source* rx_source() { return _rx.source(); }
};
/**
* Shortcut for single-client root component
*/
typedef Genode::Root_component Root_component;
/*
* Root component, handling new session requests.
*/
class Root : public Root_component
{
private:
Driver_factory &_driver_factory;
Genode::Rpc_entrypoint &_ep;
protected:
/*
* Always returns the singleton nic-session component.
*/
Session_component *_create_session(const char *args)
{
using namespace Genode;
Genode::size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
Genode::size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
Genode::size_t rx_buf_size =
Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
/* delete ram quota by the memory needed for the session */
Genode::size_t session_size = max((Genode::size_t)4096, sizeof(Session_component)
+ sizeof(Allocator_avl));
if (ram_quota < session_size)
throw Root::Quota_exceeded();
/*
* Check if donated ram quota suffices for both
* communication buffers. Also check both sizes separately
* to handle a possible overflow of the sum of both sizes.
*/
if (tx_buf_size > ram_quota - session_size
|| rx_buf_size > ram_quota - session_size
|| tx_buf_size + rx_buf_size > ram_quota - session_size) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, tx_buf_size + rx_buf_size + session_size);
throw Root::Quota_exceeded();
}
return new (md_alloc()) Session_component(tx_buf_size,
rx_buf_size,
_driver_factory,
_ep);
}
public:
Root(Genode::Rpc_entrypoint *session_ep,
Genode::Allocator *md_alloc,
Nic::Driver_factory &driver_factory)
:
Root_component(session_ep, md_alloc),
_driver_factory(driver_factory),
_ep(*session_ep)
{ }
};
};
#endif /* _INCLUDE__NIC__COMPONENT_H_ */