mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-17 14:48:20 +00:00
zynq: zero-copy implementation of nic_drv
- Packet stream buffers are directly passed to DMA. - Also enables pause frames and checksum offloading. Issue #3053
This commit is contained in:
committed by
Christian Helmuth
parent
8ad56a6c0e
commit
dc0bfd7008
@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
||||||
* \author Timo Wischer
|
* \author Timo Wischer
|
||||||
|
* \author Johannes Schlatow
|
||||||
* \date 2015-03-10
|
* \date 2015-03-10
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
* Copyright (C) 2015-2018 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
@ -25,15 +26,12 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const size_t BUFFER_DESC_SIZE = 0x08;
|
static const size_t BUFFER_DESC_SIZE = 0x08;
|
||||||
static const size_t MAX_PACKAGE_SIZE = 1600;
|
static const size_t BUFFER_SIZE = 1600;
|
||||||
static const size_t BUFFER_SIZE = BUFFER_DESC_SIZE + MAX_PACKAGE_SIZE;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const size_t _buffer_count;
|
size_t _buffer_count;
|
||||||
const size_t _buffer_offset;
|
size_t _head_idx { 0 };
|
||||||
|
size_t _tail_idx { 0 };
|
||||||
unsigned int _descriptor_index;
|
|
||||||
char* const _buffer;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -43,27 +41,49 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
|
|||||||
|
|
||||||
descriptor_t* const _descriptors;
|
descriptor_t* const _descriptors;
|
||||||
|
|
||||||
|
/* set the maximum descriptor index */
|
||||||
|
inline
|
||||||
|
void _max_index(size_t max_index) { _buffer_count = max_index+1; };
|
||||||
|
|
||||||
|
/* get the maximum descriptor index */
|
||||||
|
inline
|
||||||
|
unsigned _max_index() { return _buffer_count-1; }
|
||||||
|
|
||||||
void _increment_descriptor_index()
|
inline
|
||||||
|
void _advance_head()
|
||||||
{
|
{
|
||||||
_descriptor_index++;
|
_head_idx = (_head_idx+1) % _buffer_count;
|
||||||
_descriptor_index %= _buffer_count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
descriptor_t& _current_descriptor()
|
void _advance_tail()
|
||||||
{
|
{
|
||||||
return _descriptors[_descriptor_index];
|
_tail_idx = (_tail_idx+1) % (_buffer_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
char * _current_buffer()
|
descriptor_t& _head()
|
||||||
{
|
{
|
||||||
char * const buffer = &_buffer[MAX_PACKAGE_SIZE * _descriptor_index];
|
return _descriptors[_head_idx];
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
descriptor_t& _tail()
|
||||||
|
{
|
||||||
|
return _descriptors[_tail_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _queued() const
|
||||||
|
{
|
||||||
|
if (_head_idx >= _tail_idx)
|
||||||
|
return _head_idx - _tail_idx;
|
||||||
|
else
|
||||||
|
return _head_idx + _buffer_count - _tail_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _head_index() const { return _head_idx; }
|
||||||
|
size_t _tail_index() const { return _tail_idx; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -72,6 +92,7 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
|
|||||||
Buffer_descriptor(Buffer_descriptor const &);
|
Buffer_descriptor(Buffer_descriptor const &);
|
||||||
Buffer_descriptor &operator = (Buffer_descriptor const &);
|
Buffer_descriptor &operator = (Buffer_descriptor const &);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
* start of the ram spave contains all buffer descriptors
|
* start of the ram spave contains all buffer descriptors
|
||||||
@ -79,33 +100,13 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
|
|||||||
*/
|
*/
|
||||||
Buffer_descriptor(Genode::Env &env, const size_t buffer_count = 1)
|
Buffer_descriptor(Genode::Env &env, const size_t buffer_count = 1)
|
||||||
:
|
:
|
||||||
Attached_ram_dataspace(env.ram(), env.rm(), BUFFER_SIZE * buffer_count, UNCACHED),
|
Attached_ram_dataspace(env.ram(), env.rm(), BUFFER_DESC_SIZE * buffer_count, UNCACHED),
|
||||||
Genode::Mmio( reinterpret_cast<addr_t>(local_addr<void>()) ),
|
Genode::Mmio( reinterpret_cast<addr_t>(local_addr<void>()) ),
|
||||||
_buffer_count(buffer_count),
|
_buffer_count(buffer_count),
|
||||||
_buffer_offset(BUFFER_DESC_SIZE * buffer_count),
|
|
||||||
_descriptor_index(0),
|
|
||||||
_buffer(local_addr<char>() + _buffer_offset),
|
|
||||||
_descriptors(local_addr<descriptor_t>())
|
_descriptors(local_addr<descriptor_t>())
|
||||||
{
|
{ }
|
||||||
/*
|
|
||||||
* Save address of _buffer.
|
|
||||||
* Has to be the physical address and not the virtual addresse,
|
|
||||||
* because the dma controller of the nic will use it.
|
|
||||||
* Ignore lower two bits,
|
|
||||||
* because this are status bits.
|
|
||||||
*/
|
|
||||||
for (unsigned int i=0; i<buffer_count; i++) {
|
|
||||||
_descriptors[i].addr = (phys_addr_buffer(i)) & ~0x3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
addr_t phys_addr() { return Dataspace_client(cap()).phys_addr(); }
|
addr_t phys_addr() { return Dataspace_client(cap()).phys_addr(); }
|
||||||
|
|
||||||
addr_t phys_addr_buffer(const unsigned int index)
|
|
||||||
{
|
|
||||||
return phys_addr() + _buffer_offset + MAX_PACKAGE_SIZE * index;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _INCLUDE__DRIVERS__NIC__GEM__BUFFER_DESCRIPTOR_H_ */
|
#endif /* _INCLUDE__DRIVERS__NIC__GEM__BUFFER_DESCRIPTOR_H_ */
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
||||||
* \author Johannes Schlatow
|
|
||||||
* \author Timo Wischer
|
* \author Timo Wischer
|
||||||
|
* \author Johannes Schlatow
|
||||||
* \date 2015-03-10
|
* \date 2015-03-10
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
* Copyright (C) 2015-2018 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
@ -52,6 +52,7 @@ namespace Genode
|
|||||||
struct Mgmt_port_en : Bitfield<4, 1> {};
|
struct Mgmt_port_en : Bitfield<4, 1> {};
|
||||||
struct Clear_statistics : Bitfield<5, 1> {};
|
struct Clear_statistics : Bitfield<5, 1> {};
|
||||||
struct Start_tx : Bitfield<9, 1> {};
|
struct Start_tx : Bitfield<9, 1> {};
|
||||||
|
struct Tx_pause : Bitfield<11, 1> {};
|
||||||
|
|
||||||
static access_t init() {
|
static access_t init() {
|
||||||
return Mgmt_port_en::bits(1) |
|
return Mgmt_port_en::bits(1) |
|
||||||
@ -75,6 +76,7 @@ namespace Genode
|
|||||||
struct No_broadcast : Bitfield<5, 1> {};
|
struct No_broadcast : Bitfield<5, 1> {};
|
||||||
struct Multi_hash_en : Bitfield<6, 1> {};
|
struct Multi_hash_en : Bitfield<6, 1> {};
|
||||||
struct Gige_en : Bitfield<10, 1> {};
|
struct Gige_en : Bitfield<10, 1> {};
|
||||||
|
struct Pause_en : Bitfield<13, 1> {};
|
||||||
struct Fcs_remove : Bitfield<17, 1> {};
|
struct Fcs_remove : Bitfield<17, 1> {};
|
||||||
struct Mdc_clk_div : Bitfield<18, 3> {
|
struct Mdc_clk_div : Bitfield<18, 3> {
|
||||||
enum {
|
enum {
|
||||||
@ -82,6 +84,8 @@ namespace Genode
|
|||||||
DIV_224 = 0b111,
|
DIV_224 = 0b111,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
struct Dis_cp_pause : Bitfield<23, 1> {};
|
||||||
|
struct Rx_chksum_en : Bitfield<24, 1> {};
|
||||||
struct Ignore_rx_fcs : Bitfield<26, 1> {};
|
struct Ignore_rx_fcs : Bitfield<26, 1> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,6 +104,7 @@ namespace Genode
|
|||||||
*/
|
*/
|
||||||
struct Dma_config : Register<0x10, 32>
|
struct Dma_config : Register<0x10, 32>
|
||||||
{
|
{
|
||||||
|
struct Disc_when_no_ahb : Bitfield<24,1> {};
|
||||||
struct Rx_pktbuf_memsz_sel : Bitfield<8, 2> {
|
struct Rx_pktbuf_memsz_sel : Bitfield<8, 2> {
|
||||||
enum {
|
enum {
|
||||||
SPACE_8KB = 0x3,
|
SPACE_8KB = 0x3,
|
||||||
@ -115,15 +120,25 @@ namespace Genode
|
|||||||
BUFFER_1600B = 0x19,
|
BUFFER_1600B = 0x19,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
struct Csum_gen_en : Bitfield<11, 1> { };
|
||||||
|
struct Burst_len : Bitfield<0, 5> {
|
||||||
|
enum {
|
||||||
|
INCR16 = 0x10,
|
||||||
|
INCR8 = 0x08,
|
||||||
|
INCR4 = 0x04,
|
||||||
|
SINGLE = 0x01
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
static access_t init()
|
static access_t init()
|
||||||
{
|
{
|
||||||
return Ahb_mem_rx_buf_size::bits(Ahb_mem_rx_buf_size::BUFFER_1600B) |
|
return Ahb_mem_rx_buf_size::bits(Ahb_mem_rx_buf_size::BUFFER_1600B) |
|
||||||
Rx_pktbuf_memsz_sel::bits(Rx_pktbuf_memsz_sel::SPACE_8KB) |
|
Rx_pktbuf_memsz_sel::bits(Rx_pktbuf_memsz_sel::SPACE_8KB) |
|
||||||
Tx_pktbuf_memsz_sel::bits(Tx_pktbuf_memsz_sel::SPACE_4KB);
|
Tx_pktbuf_memsz_sel::bits(Tx_pktbuf_memsz_sel::SPACE_4KB) |
|
||||||
|
Disc_when_no_ahb::bits(1) |
|
||||||
|
Csum_gen_en::bits(1) |
|
||||||
|
Burst_len::bits(Burst_len::INCR16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO possibly enable transmition check sum offloading
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,36 +166,40 @@ namespace Genode
|
|||||||
struct Addr : Bitfield<0, 32> {};
|
struct Addr : Bitfield<0, 32> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive status register
|
* Receive status register
|
||||||
*/
|
*/
|
||||||
struct Rx_status : Register<0x20, 32>
|
struct Rx_status : Register<0x20, 32>
|
||||||
{
|
{
|
||||||
struct Frame_reveived : Bitfield<1, 1> {};
|
struct Rx_overrun : Bitfield<2, 1> {};
|
||||||
|
struct Frame_received : Bitfield<1, 1> {};
|
||||||
struct Buffer_not_available : Bitfield<0, 1> {};
|
struct Buffer_not_available : Bitfield<0, 1> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interrupt status register
|
* Interrupt status register
|
||||||
*/
|
*/
|
||||||
struct Interrupt_status : Register<0x24, 32>
|
struct Interrupt_status : Register<0x24, 32>
|
||||||
{
|
{
|
||||||
struct Rx_used_read : Bitfield<3, 1> {};
|
struct Rx_used_read : Bitfield<2, 1> {};
|
||||||
struct Rx_complete : Bitfield<1, 1> {};
|
struct Rx_complete : Bitfield<1, 1> {};
|
||||||
|
struct Pause_zero : Bitfield<13,1> {};
|
||||||
|
struct Pause_received : Bitfield<12,1> {};
|
||||||
|
struct Rx_overrun : Bitfield<10,1> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interrupt enable register
|
* Interrupt enable register
|
||||||
*/
|
*/
|
||||||
struct Interrupt_enable : Register<0x28, 32>
|
struct Interrupt_enable : Register<0x28, 32>
|
||||||
{
|
{
|
||||||
struct Rx_complete : Bitfield<1, 1> {};
|
struct Rx_used_read : Bitfield<2, 1> {};
|
||||||
|
struct Rx_complete : Bitfield<1, 1> {};
|
||||||
|
struct Pause_zero : Bitfield<13,1> {};
|
||||||
|
struct Pause_received : Bitfield<12,1> {};
|
||||||
|
struct Rx_overrun : Bitfield<10,1> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interrupt disable register
|
* Interrupt disable register
|
||||||
*/
|
*/
|
||||||
@ -189,7 +208,6 @@ namespace Genode
|
|||||||
struct Rx_complete : Bitfield<1, 1> {};
|
struct Rx_complete : Bitfield<1, 1> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PHY maintenance register
|
* PHY maintenance register
|
||||||
*/
|
*/
|
||||||
@ -210,7 +228,6 @@ namespace Genode
|
|||||||
struct Data : Bitfield<0, 16> {};
|
struct Data : Bitfield<0, 16> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAC hash register
|
* MAC hash register
|
||||||
*/
|
*/
|
||||||
@ -220,7 +237,6 @@ namespace Genode
|
|||||||
struct High_hash : Bitfield<32, 16> { };
|
struct High_hash : Bitfield<32, 16> { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAC Addresse
|
* MAC Addresse
|
||||||
*/
|
*/
|
||||||
@ -230,7 +246,6 @@ namespace Genode
|
|||||||
struct High_addr : Bitfield<32, 16> { };
|
struct High_addr : Bitfield<32, 16> { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counter for the successfully transmitted frames
|
* Counter for the successfully transmitted frames
|
||||||
*/
|
*/
|
||||||
@ -239,6 +254,13 @@ namespace Genode
|
|||||||
struct Counter : Bitfield<0, 32> { };
|
struct Counter : Bitfield<0, 32> { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for the transmitted pause frames
|
||||||
|
*/
|
||||||
|
struct Pause_transmitted : Register<0x114, 32>
|
||||||
|
{
|
||||||
|
struct Counter : Bitfield<0, 16> { };
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counter for the successfully received frames
|
* Counter for the successfully received frames
|
||||||
@ -248,15 +270,62 @@ namespace Genode
|
|||||||
struct Counter : Bitfield<0, 32> { };
|
struct Counter : Bitfield<0, 32> { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for resource error statistics
|
||||||
|
*/
|
||||||
|
struct Rx_resource_errors : Register<0x1A0, 32>
|
||||||
|
{
|
||||||
|
struct Counter : Bitfield<0, 18> { };
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counter for the successfully received frames
|
* Counter for overrun statistics
|
||||||
*/
|
*/
|
||||||
struct Rx_overrun_errors : Register<0x1A4, 32>
|
struct Rx_overrun_errors : Register<0x1A4, 32>
|
||||||
{
|
{
|
||||||
struct Counter : Bitfield<0, 10> { };
|
struct Counter : Bitfield<0, 10> { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for IP checksum errors
|
||||||
|
*/
|
||||||
|
struct Rx_ip_chksum_errors : Register<0x1A8, 32>
|
||||||
|
{
|
||||||
|
struct Counter : Bitfield<0, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for TCP checksum errors
|
||||||
|
*/
|
||||||
|
struct Rx_tcp_chksum_errors : Register<0x1AC, 32>
|
||||||
|
{
|
||||||
|
struct Counter : Bitfield<0, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for UDP checksum errors
|
||||||
|
*/
|
||||||
|
struct Rx_udp_chksum_errors : Register<0x1B0, 32>
|
||||||
|
{
|
||||||
|
struct Counter : Bitfield<0, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for FCS errors
|
||||||
|
*/
|
||||||
|
struct Rx_fcs_errors : Register<0x190, 32>
|
||||||
|
{
|
||||||
|
struct Counter : Bitfield<0, 10> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for pause frames received
|
||||||
|
*/
|
||||||
|
struct Pause_received : Register<0x164, 32>
|
||||||
|
{
|
||||||
|
struct Counter : Bitfield<0, 16> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class Phy_timeout_for_idle : public Genode::Exception {};
|
class Phy_timeout_for_idle : public Genode::Exception {};
|
||||||
class Unkown_ethernet_speed : public Genode::Exception {};
|
class Unkown_ethernet_speed : public Genode::Exception {};
|
||||||
@ -278,10 +347,14 @@ namespace Genode
|
|||||||
|
|
||||||
/* 1. Program the Network Configuration register (gem.net_cfg) */
|
/* 1. Program the Network Configuration register (gem.net_cfg) */
|
||||||
write<Config>(
|
write<Config>(
|
||||||
|
Config::Gige_en::bits(1) |
|
||||||
Config::Speed_100::bits(1) |
|
Config::Speed_100::bits(1) |
|
||||||
|
Config::Pause_en::bits(1) |
|
||||||
Config::Full_duplex::bits(1) |
|
Config::Full_duplex::bits(1) |
|
||||||
Config::Multi_hash_en::bits(1) |
|
Config::Multi_hash_en::bits(1) |
|
||||||
Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) |
|
Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) |
|
||||||
|
Config::Dis_cp_pause::bits(1) |
|
||||||
|
Config::Rx_chksum_en::bits(1) |
|
||||||
Config::Fcs_remove::bits(1)
|
Config::Fcs_remove::bits(1)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -312,12 +385,14 @@ namespace Genode
|
|||||||
write<Config::Gige_en>(1);
|
write<Config::Gige_en>(1);
|
||||||
rclk = (0 << 4) | (1 << 0);
|
rclk = (0 << 4) | (1 << 0);
|
||||||
clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
|
clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
|
||||||
|
log("Autonegotiation result: 1Gbit/s");
|
||||||
break;
|
break;
|
||||||
case SPEED_100:
|
case SPEED_100:
|
||||||
write<Config::Gige_en>(0);
|
write<Config::Gige_en>(0);
|
||||||
write<Config::Speed_100>(1);
|
write<Config::Speed_100>(1);
|
||||||
rclk = 1 << 0;
|
rclk = 1 << 0;
|
||||||
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
|
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
|
||||||
|
log("Autonegotiation result: 100Mbit/s");
|
||||||
break;
|
break;
|
||||||
case SPEED_10:
|
case SPEED_10:
|
||||||
write<Config::Gige_en>(0);
|
write<Config::Gige_en>(0);
|
||||||
@ -325,6 +400,7 @@ namespace Genode
|
|||||||
rclk = 1 << 0;
|
rclk = 1 << 0;
|
||||||
/* FIXME untested */
|
/* FIXME untested */
|
||||||
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
|
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
|
||||||
|
log("Autonegotiation result: 10Mbit/s");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw Unkown_ethernet_speed();
|
throw Unkown_ethernet_speed();
|
||||||
@ -333,7 +409,11 @@ namespace Genode
|
|||||||
|
|
||||||
|
|
||||||
/* 16.3.6 Configure Interrupts */
|
/* 16.3.6 Configure Interrupts */
|
||||||
write<Interrupt_enable>(Interrupt_enable::Rx_complete::bits(1));
|
write<Interrupt_enable>(Interrupt_enable::Rx_complete::bits(1) |
|
||||||
|
Interrupt_enable::Rx_overrun::bits(1) |
|
||||||
|
Interrupt_enable::Pause_received::bits(1) |
|
||||||
|
Interrupt_enable::Pause_zero::bits(1) |
|
||||||
|
Interrupt_enable::Rx_used_read::bits(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deinit()
|
void _deinit()
|
||||||
@ -394,6 +474,14 @@ namespace Genode
|
|||||||
_mdio_wait();
|
_mdio_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void _handle_acks()
|
||||||
|
{
|
||||||
|
while (_rx.source()->ack_avail()) {
|
||||||
|
Nic::Packet_descriptor p = _rx.source()->get_acked_packet();
|
||||||
|
_rx_buffer.reset_descriptor(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void _handle_irq()
|
virtual void _handle_irq()
|
||||||
{
|
{
|
||||||
/* 16.3.9 Receiving Frames */
|
/* 16.3.9 Receiving Frames */
|
||||||
@ -401,51 +489,82 @@ namespace Genode
|
|||||||
const Interrupt_status::access_t status = read<Interrupt_status>();
|
const Interrupt_status::access_t status = read<Interrupt_status>();
|
||||||
const Rx_status::access_t rxStatus = read<Rx_status>();
|
const Rx_status::access_t rxStatus = read<Rx_status>();
|
||||||
|
|
||||||
/* FIXME strangely, this handler is also called without any status bit set in Interrupt_status */
|
|
||||||
if ( Interrupt_status::Rx_complete::get(status) ) {
|
if ( Interrupt_status::Rx_complete::get(status) ) {
|
||||||
|
|
||||||
while (_rx_buffer.package_available()) {
|
while (_rx_buffer.next_packet()) {
|
||||||
// TODO use this buffer directly as the destination for the DMA controller
|
|
||||||
// to minimize the overrun errors
|
|
||||||
const size_t buffer_size = _rx_buffer.package_length();
|
|
||||||
|
|
||||||
/* allocate rx packet buffer */
|
_handle_acks();
|
||||||
Nic::Packet_descriptor p;
|
|
||||||
try {
|
|
||||||
p = _rx.source()->alloc_packet(buffer_size);
|
|
||||||
} catch (Session::Rx::Source::Packet_alloc_failed) { return; }
|
|
||||||
|
|
||||||
char *dst = (char *)_rx.source()->packet_content(p);
|
Nic::Packet_descriptor p = _rx_buffer.get_packet_descriptor();
|
||||||
|
if (_rx.source()->packet_valid(p))
|
||||||
/*
|
_rx.source()->submit_packet(p);
|
||||||
* copy data from rx buffer to new allocated buffer.
|
else
|
||||||
* Has to be copied,
|
Genode::error("invalid packet descriptor ", Genode::Hex(p.offset()),
|
||||||
* because the extern allocater possibly is using the cache.
|
" size ", Genode::Hex(p.size()));
|
||||||
*/
|
|
||||||
if ( _rx_buffer.get_package(dst, buffer_size) != buffer_size ) {
|
|
||||||
PWRN("Package not fully copiied. Package ignored.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clearing error flags */
|
|
||||||
write<Interrupt_status::Rx_used_read>(1);
|
|
||||||
write<Rx_status::Buffer_not_available>(1);
|
|
||||||
|
|
||||||
/* comit buffer to system services */
|
|
||||||
_rx.source()->submit_packet(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check, if there was lost some packages */
|
/* reset receive complete interrupt */
|
||||||
const uint16_t lost_packages = read<Rx_overrun_errors::Counter>();
|
write<Rx_status>(Rx_status::Frame_received::bits(1));
|
||||||
if (lost_packages > 0) {
|
|
||||||
PWRN("%d packages lost (%d packages successfully received)!",
|
|
||||||
lost_packages, read<Frames_received>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset reveive complete interrupt */
|
|
||||||
write<Rx_status>(Rx_status::Frame_reveived::bits(1));
|
|
||||||
write<Interrupt_status>(Interrupt_status::Rx_complete::bits(1));
|
write<Interrupt_status>(Interrupt_status::Rx_complete::bits(1));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
_handle_acks();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool print_stats = false;
|
||||||
|
if (Interrupt_status::Rx_overrun::get(status)) {
|
||||||
|
write<Control::Tx_pause>(1);
|
||||||
|
write<Interrupt_status>(Interrupt_status::Rx_overrun::bits(1));
|
||||||
|
write<Rx_status>(Rx_status::Rx_overrun::bits(1));
|
||||||
|
print_stats = true;
|
||||||
|
Genode::error("Rx overrun");
|
||||||
|
}
|
||||||
|
if (Interrupt_status::Rx_used_read::get(status)) {
|
||||||
|
/* tried to use buffer descriptor with used bit set */
|
||||||
|
/* we sent a pause frame because the buffer appears to
|
||||||
|
* be full
|
||||||
|
*/
|
||||||
|
write<Control::Tx_pause>(1);
|
||||||
|
write<Interrupt_status>(Interrupt_status::Rx_used_read::bits(1));
|
||||||
|
write<Rx_status>(Rx_status::Buffer_not_available::bits(1));
|
||||||
|
print_stats = true;
|
||||||
|
Genode::error("Rx used");
|
||||||
|
}
|
||||||
|
if (Interrupt_status::Pause_zero::get(status)) {
|
||||||
|
Genode::warning("Pause ended.");
|
||||||
|
write<Interrupt_status>(Interrupt_status::Pause_zero::bits(1));
|
||||||
|
print_stats = true;
|
||||||
|
}
|
||||||
|
if (Interrupt_status::Pause_received::get(status)) {
|
||||||
|
Genode::warning("Pause frame received.");
|
||||||
|
write<Interrupt_status>(Interrupt_status::Pause_received::bits(1));
|
||||||
|
print_stats = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (print_stats) {
|
||||||
|
/* check, if there was lost some packages */
|
||||||
|
const uint32_t received = read<Frames_received>();
|
||||||
|
const uint32_t pause_rx = read<Pause_received::Counter>();
|
||||||
|
const uint32_t res_err = read<Rx_resource_errors::Counter>();
|
||||||
|
const uint32_t overrun = read<Rx_overrun_errors::Counter>();
|
||||||
|
const uint32_t fcs_err = read<Rx_fcs_errors::Counter>();
|
||||||
|
const uint32_t ip_chk = read<Rx_ip_chksum_errors::Counter>();
|
||||||
|
const uint32_t udp_chk = read<Rx_udp_chksum_errors::Counter>();
|
||||||
|
const uint32_t tcp_chk = read<Rx_tcp_chksum_errors::Counter>();
|
||||||
|
const uint32_t transmit = read<Frames_transmitted>();
|
||||||
|
const uint32_t pause_tx = read<Pause_transmitted::Counter>();
|
||||||
|
|
||||||
|
Genode::warning("Received: ", received);
|
||||||
|
Genode::warning(" pause frames: ", pause_rx);
|
||||||
|
Genode::warning(" resource errors: ", res_err);
|
||||||
|
Genode::warning(" overrun errors: ", overrun);
|
||||||
|
Genode::warning(" FCS errors: ", fcs_err);
|
||||||
|
Genode::warning(" IP chk failed: ", ip_chk);
|
||||||
|
Genode::warning(" UDP chk failed: ", udp_chk);
|
||||||
|
Genode::warning(" TCP chk failed: ", tcp_chk);
|
||||||
|
Genode::warning("Transmitted: ", transmit);
|
||||||
|
Genode::warning(" pause frames: ", pause_tx);
|
||||||
|
}
|
||||||
|
|
||||||
_irq.ack_irq();
|
_irq.ack_irq();
|
||||||
}
|
}
|
||||||
@ -465,7 +584,9 @@ namespace Genode
|
|||||||
Genode::Attached_mmio(env, base, size),
|
Genode::Attached_mmio(env, base, size),
|
||||||
Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, env),
|
Session_component(tx_buf_size, rx_buf_size, rx_block_md_alloc, env),
|
||||||
_timer(env),
|
_timer(env),
|
||||||
_sys_ctrl(env, _timer), _tx_buffer(env, _timer), _rx_buffer(env),
|
_sys_ctrl(env, _timer),
|
||||||
|
_tx_buffer(env, *_tx.sink(), _timer),
|
||||||
|
_rx_buffer(env, *_rx.source()),
|
||||||
_irq(env, irq),
|
_irq(env, irq),
|
||||||
_irq_handler(env.ep(), *this, &Cadence_gem::_handle_irq),
|
_irq_handler(env.ep(), *this, &Cadence_gem::_handle_irq),
|
||||||
_phy(*this, _timer)
|
_phy(*this, _timer)
|
||||||
@ -511,6 +632,10 @@ namespace Genode
|
|||||||
|
|
||||||
bool _send()
|
bool _send()
|
||||||
{
|
{
|
||||||
|
/* first, see whether we can acknowledge any
|
||||||
|
* previously sent packet */
|
||||||
|
_tx_buffer.submit_acks(*_tx.sink());
|
||||||
|
|
||||||
if (!_tx.sink()->ready_to_ack())
|
if (!_tx.sink()->ready_to_ack())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -523,12 +648,14 @@ namespace Genode
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *src = (char *)_tx.sink()->packet_content(packet);
|
try {
|
||||||
|
_tx_buffer.add_to_queue(packet);
|
||||||
|
write<Control>(Control::start_tx());
|
||||||
|
} catch (Tx_buffer_descriptor::Package_send_timeout) {
|
||||||
|
Genode::warning("Package Tx timeout");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_tx_buffer.add_to_queue(src, packet.size());
|
|
||||||
write<Control>(Control::start_tx());
|
|
||||||
|
|
||||||
_tx.sink()->acknowledge_packet(packet);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,10 +684,9 @@ namespace Genode
|
|||||||
|
|
||||||
void _handle_packet_stream() override
|
void _handle_packet_stream() override
|
||||||
{
|
{
|
||||||
while (_rx.source()->ack_avail())
|
_handle_acks();
|
||||||
_rx.source()->release_packet(_rx.source()->get_acked_packet());
|
|
||||||
|
|
||||||
while (_send()) ;
|
while (_send());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,6 @@ class Server::Gem_session_component : public Cadence_gem
|
|||||||
try {
|
try {
|
||||||
Genode::Xml_node nic_config = _config_rom.xml().sub_node("nic");
|
Genode::Xml_node nic_config = _config_rom.xml().sub_node("nic");
|
||||||
nic_config.attribute("mac").value(&mac_addr);
|
nic_config.attribute("mac").value(&mac_addr);
|
||||||
Genode::log("Using configured MAC address ", mac_addr);
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
/* fall back to fake MAC address (unicast, locally managed) */
|
/* fall back to fake MAC address (unicast, locally managed) */
|
||||||
mac_addr.addr[0] = 0x02;
|
mac_addr.addr[0] = 0x02;
|
||||||
@ -70,6 +69,8 @@ class Server::Gem_session_component : public Cadence_gem
|
|||||||
mac_addr.addr[5] = 0x01;
|
mac_addr.addr[5] = 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Genode::log("Using MAC address ", mac_addr);
|
||||||
|
|
||||||
/* set mac address */
|
/* set mac address */
|
||||||
mac_address(mac_addr);
|
mac_address(mac_addr);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
||||||
* \author Timo Wischer
|
* \author Timo Wischer
|
||||||
|
* \author Johannes Schlatow
|
||||||
* \date 2015-03-10
|
* \date 2015-03-10
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
* Copyright (C) 2015-2018 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
@ -22,10 +23,11 @@ using namespace Genode;
|
|||||||
class Rx_buffer_descriptor : public Buffer_descriptor
|
class Rx_buffer_descriptor : public Buffer_descriptor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct Addr : Register<0x00, 32> {
|
struct Addr : Register<0x00, 32> {
|
||||||
struct Addr31to2 : Bitfield<2, 28> {};
|
struct Addr31to2 : Bitfield<2, 30> {};
|
||||||
struct Wrap : Bitfield<1, 1> {};
|
struct Wrap : Bitfield<1, 1> {};
|
||||||
struct Package_available : Bitfield<0, 1> {};
|
struct Used : Bitfield<0, 1> {};
|
||||||
};
|
};
|
||||||
struct Status : Register<0x04, 32> {
|
struct Status : Register<0x04, 32> {
|
||||||
struct Length : Bitfield<0, 13> {};
|
struct Length : Bitfield<0, 13> {};
|
||||||
@ -33,102 +35,101 @@ class Rx_buffer_descriptor : public Buffer_descriptor
|
|||||||
struct End_of_frame : Bitfield<15, 1> {};
|
struct End_of_frame : Bitfield<15, 1> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { BUFFER_COUNT = 16 };
|
enum { MAX_BUFFER_COUNT = 1024 };
|
||||||
|
|
||||||
|
addr_t const _phys_base;
|
||||||
|
|
||||||
/**
|
void _reset_descriptor(unsigned const i, addr_t phys_addr) {
|
||||||
* @brief _set_buffer_processed resets the available flag
|
if (i > _max_index())
|
||||||
* So the DMA controller can use this buffer for an received package.
|
return;
|
||||||
* The buffer index will be incremented, too.
|
|
||||||
* So the right sequenz of packages will be keeped.
|
/* clear status */
|
||||||
*/
|
_descriptors[i].status = 0;
|
||||||
void _set_package_processed()
|
|
||||||
{
|
/* set physical buffer address and set not used by SW
|
||||||
/* reset package available for new package */
|
* last descriptor must be marked by Wrap bit
|
||||||
_current_descriptor().addr &= ~Addr::Package_available::bits(1);
|
*/
|
||||||
/* use next buffer descriptor for next package */
|
_descriptors[i].addr =
|
||||||
_increment_descriptor_index();
|
(phys_addr & Addr::Addr31to2::reg_mask())
|
||||||
|
| Addr::Wrap::bits(i == _max_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool _head_available()
|
||||||
|
{
|
||||||
|
return Addr::Used::get(_head().addr)
|
||||||
|
&& Status::Length::get(_head().status);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Rx_buffer_descriptor(Genode::Env &env) : Buffer_descriptor(env, BUFFER_COUNT)
|
Rx_buffer_descriptor(Genode::Env &env,
|
||||||
|
Nic::Session::Tx::Source &source)
|
||||||
|
: Buffer_descriptor(env, MAX_BUFFER_COUNT),
|
||||||
|
_phys_base(Dataspace_client(source.dataspace()).phys_addr())
|
||||||
{
|
{
|
||||||
/*
|
for (size_t i=0; i <= _max_index(); i++) {
|
||||||
* mark the last buffer descriptor
|
try {
|
||||||
* so the dma will start at the beginning again
|
Nic::Packet_descriptor p = source.alloc_packet(BUFFER_SIZE);
|
||||||
*/
|
_reset_descriptor(i, _phys_base + p.offset());
|
||||||
_descriptors[BUFFER_COUNT-1].addr |= Addr::Wrap::bits(1);
|
} catch (Nic::Session::Rx::Source::Packet_alloc_failed) {
|
||||||
|
/* set new _buffer_count */
|
||||||
|
_max_index(i-1);
|
||||||
|
/* set wrap bit */
|
||||||
|
_descriptors[_max_index()].addr |= Addr::Wrap::bits(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::log("Initialised ", _max_index()+1, " RX buffer descriptors");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool reset_descriptor(Packet_descriptor pd)
|
||||||
bool package_available()
|
|
||||||
{
|
{
|
||||||
for (unsigned int i=0; i<BUFFER_COUNT; i++) {
|
addr_t const phys = _phys_base + pd.offset();
|
||||||
const bool available = Addr::Package_available::get(_current_descriptor().addr);
|
|
||||||
if (available) {
|
for (size_t i=0; i <= _max_index(); i++) {
|
||||||
|
_advance_tail();
|
||||||
|
if (Addr::Addr31to2::masked(_tail().addr) == phys) {
|
||||||
|
_reset_descriptor(_tail_index(), phys);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_increment_descriptor_index();
|
bool next_packet()
|
||||||
|
{
|
||||||
|
/* Find next available descriptor (head) holding a packet. */
|
||||||
|
for (unsigned int i=0; i < _max_index(); i++) {
|
||||||
|
if (_head_available())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_advance_head();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Nic::Packet_descriptor get_packet_descriptor()
|
||||||
size_t package_length()
|
|
||||||
{
|
{
|
||||||
if (!package_available())
|
if (!_head_available())
|
||||||
return 0;
|
return Nic::Packet_descriptor(0, 0);
|
||||||
|
|
||||||
return Status::Length::get(_current_descriptor().status);
|
const Status::access_t status = _head().status;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t get_package(char* const package, const size_t max_length)
|
|
||||||
{
|
|
||||||
if (!package_available())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const Status::access_t status = _current_descriptor().status;
|
|
||||||
if (!Status::Start_of_frame::get(status) || !Status::End_of_frame::get(status)) {
|
if (!Status::Start_of_frame::get(status) || !Status::End_of_frame::get(status)) {
|
||||||
warning("Package splitted over more than one descriptor. Package ignored!");
|
warning("Packet split over more than one descriptor. Packet ignored!");
|
||||||
|
|
||||||
_set_package_processed();
|
_reset_descriptor(_head_index(), _head().addr);
|
||||||
return 0;
|
return Nic::Packet_descriptor(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t length = Status::Length::get(status);
|
const size_t length = Status::Length::get(status);
|
||||||
if (length > max_length) {
|
|
||||||
warning("Buffer for received package to small. Package ignored!");
|
|
||||||
|
|
||||||
_set_package_processed();
|
/* reset status */
|
||||||
return 0;
|
_head().status = 0;
|
||||||
}
|
|
||||||
|
|
||||||
const char* const src_buffer = _current_buffer();
|
return Nic::Packet_descriptor((addr_t)Addr::Addr31to2::masked(_head().addr) - _phys_base, length);
|
||||||
memcpy(package, src_buffer, length);
|
|
||||||
|
|
||||||
_set_package_processed();
|
|
||||||
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_mem_diffs()
|
|
||||||
{
|
|
||||||
static unsigned int old_data[0x1F];
|
|
||||||
|
|
||||||
log("Rx buffer:");
|
|
||||||
const unsigned int* const cur_data = local_addr<unsigned int>();
|
|
||||||
for (unsigned i=0; i<sizeof(old_data)/sizeof(old_data[0]); i++) {
|
|
||||||
if (cur_data[i] != old_data[i]) {
|
|
||||||
log(i*4, ": ", Hex(old_data[i]), " -> ", Hex(cur_data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memcpy(old_data, cur_data, sizeof(old_data));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_ */
|
#endif /* _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_ */
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
|
||||||
* \author Timo Wischer
|
* \author Timo Wischer
|
||||||
|
* \author Johannes Schlatow
|
||||||
* \date 2015-03-10
|
* \date 2015-03-10
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
* Copyright (C) 2015-2018 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
@ -25,7 +26,7 @@ using namespace Genode;
|
|||||||
class Tx_buffer_descriptor : public Buffer_descriptor
|
class Tx_buffer_descriptor : public Buffer_descriptor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
enum { BUFFER_COUNT = 2 };
|
enum { BUFFER_COUNT = 1024 };
|
||||||
|
|
||||||
struct Addr : Register<0x00, 32> {};
|
struct Addr : Register<0x00, 32> {};
|
||||||
struct Status : Register<0x04, 32> {
|
struct Status : Register<0x04, 32> {
|
||||||
@ -33,51 +34,109 @@ class Tx_buffer_descriptor : public Buffer_descriptor
|
|||||||
struct Last_buffer : Bitfield<15, 1> {};
|
struct Last_buffer : Bitfield<15, 1> {};
|
||||||
struct Wrap : Bitfield<30, 1> {};
|
struct Wrap : Bitfield<30, 1> {};
|
||||||
struct Used : Bitfield<31, 1> {};
|
struct Used : Bitfield<31, 1> {};
|
||||||
|
struct Chksum_err : Bitfield<20, 2> {};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Package_send_timeout : public Genode::Exception {};
|
|
||||||
|
|
||||||
Timer::Connection &_timer;
|
Timer::Connection &_timer;
|
||||||
|
|
||||||
public:
|
addr_t const _phys_base;
|
||||||
Tx_buffer_descriptor(Genode::Env &env, Timer::Connection &timer)
|
|
||||||
: Buffer_descriptor(env, BUFFER_COUNT), _timer(timer)
|
void _reset_descriptor(unsigned const i, addr_t phys_addr) {
|
||||||
{
|
if (i > _max_index())
|
||||||
for (unsigned int i=0; i<BUFFER_COUNT; i++) {
|
return;
|
||||||
_descriptors[i].status = Status::Used::bits(1) | Status::Last_buffer::bits(1);
|
|
||||||
}
|
/* set physical buffer address */
|
||||||
_descriptors[BUFFER_COUNT-1].status |= Status::Wrap::bits(1);
|
_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:
|
||||||
|
|
||||||
void add_to_queue(const char* const packet, const size_t size)
|
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())
|
||||||
{
|
{
|
||||||
if (size > MAX_PACKAGE_SIZE) {
|
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!");
|
warning("Ethernet package to big. Not sent!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait until the used bit is set (timeout after 200ms) */
|
addr_t const packet_phys = _phys_base + p.offset();
|
||||||
uint32_t timeout = 200;
|
if (packet_phys & 0x1f) {
|
||||||
while ( !Status::Used::get(_current_descriptor().status) ) {
|
warning("Packet is not aligned properly.");
|
||||||
if (timeout <= 0) {
|
|
||||||
throw Package_send_timeout();
|
|
||||||
}
|
|
||||||
timeout--;
|
|
||||||
|
|
||||||
_timer.msleep(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
memcpy(_current_buffer(), packet, size);
|
/* TODO buffer is full, instead of sleeping we should
|
||||||
|
* therefore wait for tx_complete interrupt */
|
||||||
|
_timer.usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
_current_descriptor().status &= Status::Length::clear_mask();
|
_reset_descriptor(_head_index(), packet_phys);
|
||||||
_current_descriptor().status |= Status::Length::bits(size);
|
_head().status |= Status::Length::bits(p.size());
|
||||||
|
|
||||||
/* unset the unset bit */
|
/* unset the used bit */
|
||||||
_current_descriptor().status &= Status::Used::clear_mask();
|
_head().status &= Status::Used::clear_mask();
|
||||||
|
|
||||||
_increment_descriptor_index();
|
_advance_head();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user