genode/repos/os/src/drivers/nic/spec/gem/tx_buffer_descriptor.h
Johannes Schlatow dc0bfd7008 zynq: zero-copy implementation of nic_drv
- Packet stream buffers are directly passed to DMA.
- Also enables pause frames and checksum offloading.

Issue #3053
2018-11-29 11:46:01 +01:00

144 lines
3.8 KiB
C++

/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \author Johannes Schlatow
* \date 2015-03-10
*/
/*
* Copyright (C) 2015-2018 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__DRIVERS__NIC__GEM__TX_BUFFER_DESCRIPTOR_H_
#define _INCLUDE__DRIVERS__NIC__GEM__TX_BUFFER_DESCRIPTOR_H_
#include <base/log.h>
#include <timer_session/connection.h>
#include "buffer_descriptor.h"
using namespace Genode;
class Tx_buffer_descriptor : public Buffer_descriptor
{
private:
enum { BUFFER_COUNT = 1024 };
struct Addr : Register<0x00, 32> {};
struct Status : Register<0x04, 32> {
struct Length : Bitfield<0, 14> {};
struct Last_buffer : Bitfield<15, 1> {};
struct Wrap : Bitfield<30, 1> {};
struct Used : Bitfield<31, 1> {};
struct Chksum_err : Bitfield<20, 2> {};
};
Timer::Connection &_timer;
addr_t const _phys_base;
void _reset_descriptor(unsigned const i, addr_t phys_addr) {
if (i > _max_index())
return;
/* set physical buffer address */
_descriptors[i].addr = phys_addr;
/* set used by SW, also we do not use frame scattering */
_descriptors[i].status = Status::Used::bits(1) |
Status::Last_buffer::bits(1);
/* last buffer must be marked by Wrap bit */
if (i == _max_index())
_descriptors[i].status |= Status::Wrap::bits(1);
}
public:
class Package_send_timeout : public Genode::Exception {};
Tx_buffer_descriptor(Genode::Env &env,
Nic::Session::Rx::Sink &sink,
Timer::Connection &timer)
: Buffer_descriptor(env, BUFFER_COUNT), _timer(timer),
_phys_base(Dataspace_client(sink.dataspace()).phys_addr())
{
for (size_t i=0; i <= _max_index(); i++) {
/* configure all descriptors with address 0, which we
* interpret as invalid */
_reset_descriptor(i, 0x0);
}
}
void submit_acks(Nic::Session::Rx::Sink &sink)
{
/* the tail marks the descriptor for which we wait to
* be handed over to software */
for (size_t i=0; i <= _queued(); i++) {
/* stop if still in use by hardware */
if (!Status::Used::get(_tail().status))
break;
/* if descriptor has been configured properly */
if (_tail().addr != 0) {
/* build packet descriptor from buffer descriptor
* and acknowledge packet */
const size_t length = Status::Length::get(_tail().status);
Nic::Packet_descriptor p((addr_t)_tail().addr - _phys_base, length);
if (sink.packet_valid(p))
sink.acknowledge_packet(p);
/* erase address so that we don't send an ack again */
_tail().addr = 0;
/* TODO optionally, we may evaluate the Tx status here */
}
_advance_tail();
}
}
void add_to_queue(Nic::Packet_descriptor p)
{
/* the head marks the descriptor that we use next for
* handing over the packet to hardware */
if (p.size() > BUFFER_SIZE) {
warning("Ethernet package to big. Not sent!");
return;
}
addr_t const packet_phys = _phys_base + p.offset();
if (packet_phys & 0x1f) {
warning("Packet is not aligned properly.");
}
/* wait until the used bit is set (timeout after 10ms) */
uint32_t timeout = 10000;
while ( !Status::Used::get(_head().status) ) {
if (timeout == 0) {
throw Package_send_timeout();
}
timeout -= 1000;
/* TODO buffer is full, instead of sleeping we should
* therefore wait for tx_complete interrupt */
_timer.usleep(1000);
}
_reset_descriptor(_head_index(), packet_phys);
_head().status |= Status::Length::bits(p.size());
/* unset the used bit */
_head().status &= Status::Used::clear_mask();
_advance_head();
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__TX_BUFFER_DESCRIPTOR_H_ */