aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/x300/lib
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/x300/lib')
-rw-r--r--firmware/x300/lib/CMakeLists.txt32
-rw-r--r--firmware/x300/lib/chinch.c310
-rw-r--r--firmware/x300/lib/ethernet.c639
-rw-r--r--firmware/x300/lib/link_state_route_proto.c441
-rw-r--r--firmware/x300/lib/mdelay.c36
-rw-r--r--firmware/x300/lib/print_addrs.c64
-rw-r--r--firmware/x300/lib/printf.c287
-rw-r--r--firmware/x300/lib/u3_net_stack.c589
-rw-r--r--firmware/x300/lib/udp_uart.c149
-rw-r--r--firmware/x300/lib/wb_i2c.c137
-rw-r--r--firmware/x300/lib/wb_pkt_iface64.c87
-rw-r--r--firmware/x300/lib/wb_uart.c34
12 files changed, 2805 insertions, 0 deletions
diff --git a/firmware/x300/lib/CMakeLists.txt b/firmware/x300/lib/CMakeLists.txt
new file mode 100644
index 000000000..ed5a0a7c0
--- /dev/null
+++ b/firmware/x300/lib/CMakeLists.txt
@@ -0,0 +1,32 @@
+#
+# Copyright 2010-2013 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+
+add_library(usrpfw STATIC
+ udp_uart.c
+ wb_uart.c
+ wb_i2c.c
+ printf.c
+ wb_pkt_iface64.c
+ u3_net_stack.c
+ ethernet.c
+ mdelay.c
+ chinch.c
+ print_addrs.c
+ link_state_route_proto.c
+)
diff --git a/firmware/x300/lib/chinch.c b/firmware/x300/lib/chinch.c
new file mode 100644
index 000000000..054845754
--- /dev/null
+++ b/firmware/x300/lib/chinch.c
@@ -0,0 +1,310 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "chinch.h"
+
+#define PCIE_MSG_REG_BASE 0xFB00
+#define PCIE_MSG_DATA_REG 0 //Write: Data register for outbound requests and responses
+ //Read: Latched data for inbound requests
+#define PCIE_MSG_CTRL_REG 1 //Write: Control register for outbound requests and responses (Initiates xfer)
+ //Read: Latched control word for inbound requests
+#define PCIE_MSG_RESP_REG 2 //Read: Latched response data for outbound requests
+#define PCIE_MSG_STATUS_REG 3 //Read: Status register for inbound and outbound transactions
+
+// Transaction Word Format
+//
+// -Control- --Data--
+// 63 32 31 0
+// | | | |
+// FXXA AAAA DDDD DDDD
+//
+// where:
+// D = Data/Payload
+// A = Address
+// X = Reserved
+// F = Flags: {Read_Response, Write_Request, Read_Request, Half_Word_Cycle}
+
+#define PCIE_CTRL_REG_READ_RESP (1<<31)
+#define PCIE_CTRL_REG_WRITE (1<<30)
+#define PCIE_CTRL_REG_READ (1<<29)
+#define PCIE_CTRL_REG_HALF_WORD (1<<28)
+#define PCIE_CTRL_REG_ADDR_MASK 0x000FFFFF
+
+#define PCIE_STATUS_REG_READ_PENDING (1<<0)
+#define PCIE_STATUS_REG_REQ_PENDING (1<<1)
+#define PCIE_STATUS_REG_RESP_PENDING (1<<2)
+#define PCIE_STATUS_REG_BUSY (1<<4)
+
+#define CHINCH_FPGA_CONFIG_REG 0x58
+#define CHINCH_FLASH_WINDOW_REG0 0xC0
+#define CHINCH_FLASH_WINDOW_REG1 0xE0
+#define CHINCH_FLASH_2AAA_REG 0x400
+#define CHINCH_FLASH_5555_REG 0x408
+#define CHINCH_FLASH_WINDOW_BASE 0x60000
+#define CHINCH_FLASH_WINDOW_SIZE 0x20000
+#define CHINCH_FLASH_WINDOW_CONF 0x91
+
+//-----------------------------------------------------
+// Peek-Poke interface
+//-----------------------------------------------------
+
+bool chinch_poke(
+ const uint32_t addr,
+ const uint32_t data,
+ bool half_word,
+ uint32_t timeout
+)
+{
+ //Build transaction control word
+ uint32_t ctrl_word = 0, i;
+ ctrl_word |= (addr & PCIE_CTRL_REG_ADDR_MASK);
+ if (half_word) ctrl_word |= PCIE_CTRL_REG_HALF_WORD;
+ ctrl_word |= PCIE_CTRL_REG_WRITE;
+
+ //Wait for space in the transaction queue or timeout
+ i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_BUSY) != 0) {
+ if (++i > timeout) return false;
+ }
+
+ //Flush transaction control and data registers
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_DATA_REG), data);
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG), ctrl_word);
+
+ return true;
+}
+
+bool chinch_peek(
+ const uint32_t addr,
+ uint32_t* data,
+ bool half_word,
+ uint32_t timeout
+)
+{
+ //Build transaction control word
+ uint32_t ctrl_word = 0, i;
+ ctrl_word |= (addr & PCIE_CTRL_REG_ADDR_MASK);
+ if (half_word) ctrl_word |= PCIE_CTRL_REG_HALF_WORD;
+ ctrl_word |= PCIE_CTRL_REG_READ;
+
+ //Wait for space in the transaction queue or timeout
+ i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_BUSY) != 0) {
+ if (++i > timeout) return false;
+ }
+
+ //Flush transaction control register
+ if (data) *data = 0;
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG), ctrl_word);
+
+ //Wait for read completion or timeout
+ i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_READ_PENDING) != 0) {
+ if (++i > timeout) return false;
+ }
+ //Read transaction data register
+ if (data) *data = wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_RESP_REG));
+ return true;
+}
+
+//-----------------------------------------------------
+// Flash access
+//-----------------------------------------------------
+
+uint32_t g_cached_win_reg0;
+uint32_t g_cached_win_reg1;
+
+bool chinch_flash_init()
+{
+ chinch_peek32(CHINCH_FLASH_WINDOW_REG0, &g_cached_win_reg0);
+ chinch_peek32(CHINCH_FLASH_WINDOW_REG1, &g_cached_win_reg1);
+
+ bool status = true, passed = true;
+ STATUS_MERGE(chinch_poke32(CHINCH_FLASH_WINDOW_REG0, CHINCH_FLASH_WINDOW_BASE | CHINCH_FLASH_WINDOW_CONF), status);
+
+ //Run a loopback test to ensure that we will not corrupt the flash.
+ STATUS_MERGE(chinch_poke32(0x200, 0xDEADBEEF), status);
+ STATUS_MERGE(chinch_poke16(0x204, 0x5678), status);
+ uint32_t reg_val;
+ STATUS_MERGE(chinch_peek16(0x0, &reg_val), status);
+ STATUS_MERGE(chinch_poke16(0x206, reg_val), status);
+ STATUS_MERGE(chinch_peek32(0x200, &reg_val), status);
+ passed &= (reg_val == 0xDEADBEEF);
+ STATUS_MERGE(chinch_peek32(0x204, &reg_val), status);
+ passed &= (reg_val == 0x7AD05678);
+
+ return status && passed;
+}
+
+void chinch_flash_cleanup()
+{
+ chinch_poke32(CHINCH_FLASH_WINDOW_REG0, g_cached_win_reg0);
+ chinch_poke32(CHINCH_FLASH_WINDOW_REG1, g_cached_win_reg1);
+}
+
+bool chinch_flash_select_sector(uint32_t sector)
+{
+ return chinch_poke32(CHINCH_FLASH_WINDOW_REG1, sector * CHINCH_FLASH_WINDOW_SIZE);
+}
+
+bool chinch_flash_erase_sector()
+{
+ bool status = true;
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x00AA), status); //Unlock #1
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_2AAA_REG, 0x0055), status); //Unlock #2
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x0080), status); //Setup
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x00AA), status); //Unlock #1
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_2AAA_REG, 0x0055), status); //Unlock #2
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, 0x0030), status); //Erase
+
+ if (status) {
+ uint32_t read_data;
+ while (true) {
+ status = chinch_peek16(CHINCH_FLASH_WINDOW_BASE, &read_data); //Wait for sector to erase
+ if (((read_data & 0xFFFF) == 0xFFFF) || !status) break;
+ }
+ }
+ return status;
+}
+
+bool chinch_flash_read_buf(uint32_t offset, uint16_t* buf, uint32_t size)
+{
+ bool status = true;
+ uint32_t base_addr = CHINCH_FLASH_WINDOW_BASE | (offset & 0x3FFFF);
+ for (uint32_t i = 0; (i < size) && status; i++) {
+ uint32_t word;
+ STATUS_CHAIN(chinch_peek16(base_addr + (i * 2), &word), status);
+ buf[i] = (uint16_t)word;
+ }
+ return status;
+}
+
+bool chinch_flash_write_buf(uint32_t offset, uint16_t* buf, uint32_t size)
+{
+ if (size > CHINCH_FLASH_MAX_BUF_WRITES || buf == 0) return false;
+ bool status = true;
+
+ //Setup buffered write
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_5555_REG, 0x00AA), status); //Unlock #1
+ STATUS_MERGE(chinch_poke16(CHINCH_FLASH_2AAA_REG, 0x0055), status); //Unlock #2
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, 0x0025), status); //Setup write
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, size - 1), status); //Num words
+
+ //Write the data
+ uint32_t base_addr = CHINCH_FLASH_WINDOW_BASE | (offset & 0x3FFFF);
+ for (uint32_t i = 0; i < size; i++) {
+ STATUS_CHAIN(chinch_poke16(base_addr + (i * 2), buf[i]), status);
+ }
+
+ //Commit write and poll until data is written
+ STATUS_CHAIN(chinch_poke16(CHINCH_FLASH_WINDOW_BASE, 0x0029), status);
+ if (status) {
+ uint16_t read_data;
+ while (true) {
+ STATUS_MERGE(chinch_flash_read(base_addr, &read_data), status); //Wait for write to finish
+ if ((read_data == buf[0]) || !status) break;
+ }
+ }
+ return status;
+}
+
+//-----------------------------------------------------
+// FPGA Configuration
+//-----------------------------------------------------
+void chinch_start_config()
+{
+ chinch_poke32(CHINCH_FPGA_CONFIG_REG, 0x1);
+}
+
+config_status_t chinch_get_config_status()
+{
+ bool status = true;
+ uint32_t read_data;
+ STATUS_MERGE(chinch_peek32(CHINCH_FPGA_CONFIG_REG, &read_data), status);
+ return status ? (config_status_t)read_data : CHINCH_CONFIG_ERROR;
+}
+
+//-----------------------------------------------------
+// Read-back interface for the user initiated
+// PCIe register transactions
+//-----------------------------------------------------
+pcie_register_xact_t g_pcie_reg_xact_info;
+uint32_t g_pcie_res_timeout;
+
+bool _respond_to_pcie_xact_request(uint32_t response, uint32_t timeout)
+{
+ //Wait for space in the transaction queue or timeout
+ uint32_t i = 0;
+ while ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_BUSY) != 0) {
+ if (++i > g_pcie_res_timeout) return false;
+ }
+
+ //First write data and then the control register to ensure coherency
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_DATA_REG), response);
+ wb_poke32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG), PCIE_CTRL_REG_READ_RESP);
+
+ return true;
+}
+
+bool check_pcie_user_regport(pcie_register_xact_t** xact_info_hdl)
+{
+ //Check for pending transaction requests
+ if ((wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_STATUS_REG)) & PCIE_STATUS_REG_REQ_PENDING) != 0) {
+ //Attach responder to transaction info
+ g_pcie_reg_xact_info.respond = _respond_to_pcie_xact_request;
+
+ //First read data and then the control register to ensure coherency
+ g_pcie_reg_xact_info.data = wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_DATA_REG));
+ uint32_t xact_control = wb_peek32(SR_ADDR(PCIE_MSG_REG_BASE, PCIE_MSG_CTRL_REG));
+
+ g_pcie_reg_xact_info.addr = xact_control & PCIE_CTRL_REG_ADDR_MASK;
+ g_pcie_reg_xact_info.size =
+ (xact_control & PCIE_CTRL_REG_HALF_WORD) == 0 ? PCIE_XACT_32_BIT : PCIE_XACT_16_BIT;
+ if ((xact_control & PCIE_CTRL_REG_READ) != 0)
+ g_pcie_reg_xact_info.type = PCIE_XACT_READ;
+ else if ((xact_control & PCIE_CTRL_REG_WRITE) != 0)
+ g_pcie_reg_xact_info.type = PCIE_XACT_WRITE;
+ else
+ g_pcie_reg_xact_info.type = PCIE_XACT_ERROR;
+
+ *xact_info_hdl = &g_pcie_reg_xact_info;
+ return true;
+ } else {
+ *xact_info_hdl = 0;
+ return false;
+ }
+}
+
+bool forward_pcie_user_xact_to_wb()
+{
+ pcie_register_xact_t* xact_info;
+ if (check_pcie_user_regport(&xact_info)) {
+ if (xact_info->size == PCIE_XACT_32_BIT) {
+ //Only respond to 32-bit transactions because that is all the LVFPGA interface can send
+ if (xact_info->type == PCIE_XACT_WRITE) {
+ wb_poke32(xact_info->addr, xact_info->data);
+ return true;
+ } else if (xact_info->type == PCIE_XACT_READ) {
+ return xact_info->respond(wb_peek32(xact_info->addr), CHINCH_DEFAULT_XACT_TIMEOUT);
+ }
+ }
+ }
+ return false;
+}
+
+
+
diff --git a/firmware/x300/lib/ethernet.c b/firmware/x300/lib/ethernet.c
new file mode 100644
index 000000000..806a3840d
--- /dev/null
+++ b/firmware/x300/lib/ethernet.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright 2007,2009 Free Software Foundation, Inc.
+ * Copyright 2009 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "../x300/x300_defs.h"
+#include "ethernet.h"
+#include "mdelay.h"
+#include "printf.h"
+#include "wb_i2c.h"
+#include "wb_utils.h"
+//#include "memory_map.h"
+//#include "eth_phy.h"
+//#include "pic.h"
+//#include "hal_io.h"
+//#include "nonstdio.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include "xge_phy.h"
+#include "xge_mac.h"
+#include <u3_net_stack.h>
+
+
+
+
+#define VERBOSE 0
+
+#define NETHS 2 // # of ethernet interfaces
+
+static bool links_up[NETHS] = {};
+
+////////////////////////////////////////////////////////////////////////
+//
+// 10 Gig Ethernet MAC.
+//
+typedef struct {
+ volatile uint32_t config; // WO
+ volatile uint32_t int_pending; // Clear-on-read
+ volatile uint32_t int_status; // RO
+ volatile uint32_t int_mask; // RW
+ volatile uint32_t mdio_data;
+ volatile uint32_t mdio_addr;
+ volatile uint32_t mdio_op;
+ volatile uint32_t mdio_control;
+ volatile uint32_t gpio;
+} xge_regs_t;
+
+#define xge_regs ((xge_regs_t *) base)
+
+#define SFPP_STATUS_MODABS_CHG (1 << 5) // Has MODABS changed since last read?
+#define SFPP_STATUS_TXFAULT_CHG (1 << 4) // Has TXFAULT changed since last read?
+#define SFPP_STATUS_RXLOS_CHG (1 << 3) // Has RXLOS changed since last read?
+#define SFPP_STATUS_MODABS (1 << 2) // MODABS state
+#define SFPP_STATUS_TXFAULT (1 << 1) // TXFAULT state
+#define SFPP_STATUS_RXLOS (1 << 0) // RXLOS state
+
+
+int
+ethernet_ninterfaces(void)
+{
+ return NETHS;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Clause 45 MDIO used for 10Gig Ethernet has two bus transactions to complete a transfer.
+// An initial transaction sets up the address, and a subsequent one transfers the read or write data.
+//
+static uint32_t
+xge_read_mdio(const uint32_t base, const uint32_t address, const uint32_t device, const uint32_t port)
+{
+ // Set register address each iteration
+ xge_regs->mdio_addr = address;
+ // Its a clause 45 device. We want to ADDRESS
+ xge_regs->mdio_op = XGE_MDIO_CLAUSE(CLAUSE45) | XGE_MDIO_OP(MDIO_ADDRESS) | XGE_MDIO_ADDR(port) | XGE_MDIO_MMD(device);
+ // Start MDIO bus transaction
+ xge_regs->mdio_control = 1;
+ // Wait until bus transaction complete
+ while (xge_regs->mdio_control == 1);
+ // Its a clause 45 device. We want to READ
+ xge_regs->mdio_op = XGE_MDIO_CLAUSE(CLAUSE45) | XGE_MDIO_OP(MDIO_READ) | XGE_MDIO_ADDR(port) | XGE_MDIO_MMD(device);
+ // Start MDIO bus transaction
+ xge_regs->mdio_control = 1;
+ // Wait until bus transaction complete
+ while (xge_regs->mdio_control == 1);
+ // Read MDIO data
+ return(xge_regs->mdio_data);
+}
+
+static void
+xge_write_mdio(const uint32_t base, const uint32_t address, const uint32_t device, const uint32_t port, const uint32_t data)
+{
+ // Set register address each iteration
+ xge_regs->mdio_addr = address;
+ // Its a clause 45 device. We want to ADDRESS
+ xge_regs->mdio_op = XGE_MDIO_CLAUSE(CLAUSE45) | XGE_MDIO_OP(MDIO_ADDRESS) | XGE_MDIO_ADDR(port) | XGE_MDIO_MMD(device);
+ // Start MDIO bus transaction
+ xge_regs->mdio_control = 1;
+ // Wait until bus transaction complete
+ while (xge_regs->mdio_control == 1);
+ // Write new value to mdio_write_data reg.
+ xge_regs->mdio_data = data;
+ // Its a clause 45 device. We want to WRITE
+ xge_regs->mdio_op = XGE_MDIO_CLAUSE(CLAUSE45) | XGE_MDIO_OP(MDIO_WRITE) | XGE_MDIO_ADDR(port) | XGE_MDIO_MMD(device);
+ // Start MDIO bus transaction
+ xge_regs->mdio_control = 1;
+ // Wait until bus transaction complete
+ while (xge_regs->mdio_control == 1);
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Clause 22 MDIO used for 1Gig Ethernet has one bus transaction to complete a transfer.
+//
+static uint32_t
+ge_read_mdio(const uint32_t base, const uint32_t address, const uint32_t port)
+{
+ // Its a clause 22 device. We want to READ
+ xge_regs->mdio_op = XGE_MDIO_CLAUSE(CLAUSE22) | XGE_MDIO_OP(MDIO_C22_READ) | XGE_MDIO_ADDR(port) | address;
+ // Start MDIO bus transaction
+ xge_regs->mdio_control = 1;
+ // Wait until bus transaction complete
+ while (xge_regs->mdio_control == 1);
+ // Read MDIO data
+ return(xge_regs->mdio_data);
+}
+
+static void
+ge_write_mdio(const uint32_t base, const uint32_t address, const uint32_t port, const uint32_t data)
+{
+ // Write new value to mdio_write_data reg.
+ xge_regs->mdio_data = data;
+ // Its a clause 22 device. We want to WRITE
+ xge_regs->mdio_op = XGE_MDIO_CLAUSE(CLAUSE22) | XGE_MDIO_OP(MDIO_C22_WRITE) | XGE_MDIO_ADDR(port) | address;
+ // Start MDIO bus transaction
+ xge_regs->mdio_control = 1;
+ // Wait until bus transaction complete
+ while (xge_regs->mdio_control == 1);
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Read and write MDIO independent of type
+//
+
+static uint32_t read_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port)
+{
+ const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1;
+ const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE;
+ if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)
+ {
+ return xge_read_mdio(base, address, device, port);
+ }
+ else
+ {
+ return ge_read_mdio(base, address, port);
+ }
+}
+
+static void write_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port, const uint32_t data)
+{
+ const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1;
+ const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE;
+ if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)
+ {
+ return xge_write_mdio(base, address, device, port, data);
+ }
+ else
+ {
+ return ge_write_mdio(base, address, port, data);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Read an 8-bit word from a device attached to the PHY's i2c bus.
+//
+static int
+xge_i2c_rd(const uint32_t base, const uint8_t i2c_dev_addr, const uint8_t i2c_word_addr)
+{
+ uint8_t buf;
+ // IJB. CHECK HERE FOR MODET. Bail immediately if no module
+
+ // SFF-8472 defines a hardcoded bus address of 0xA0, an 8bit internal address and a register map.
+ // Write the random access address to the SPF module
+ if (wb_i2c_write(base, i2c_dev_addr, &i2c_word_addr, 1) == false)
+ return(-1);
+
+ // Now read back a byte of data
+ if (wb_i2c_read(base, i2c_dev_addr, &buf, 1) == false)
+ return(-1);
+
+ return((int) buf);
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Read identity of SFP+ module for XGE PHY
+//
+// (base is i2c controller)
+static int
+xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)
+{
+ int x;
+ // Delay read of SFPP
+ if (delay_ms)
+ mdelay(delay_ms);
+ // Read ID code from SFP
+ x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);
+ // I2C Error?
+ if (x < 0) {
+ printf("DEBUG: I2C error in SFPP_TYPE.\n");
+ return x;
+ }
+ // Decode module type. These registers and values are defined in SFF-8472
+ if (x & 0x01) // Active 1X Infinband Copper
+ {
+ goto twinax;
+ }
+ if (x & 0x10)
+ {
+ printf("DEBUG: SFFP_TYPE_SR.\n");
+ return SFFP_TYPE_SR;
+ }
+ if (x & 0x20)
+ {
+ printf("DEBUG: SFFP_TYPE_LR.\n");
+ return SFFP_TYPE_LR;
+ }
+ if (x & 0x40)
+ {
+ printf("DEBUG: SFFP_TYPE_LRM.\n");
+ return SFFP_TYPE_LRM;
+ }
+ // Search for legacy 1000-Base SFP types
+ x = xge_i2c_rd(base, MODULE_DEV_ADDR, 0x6);
+ if (x < 0) {
+ printf("DEBUG: I2C error in SFPP_TYPE.\n");
+ return x;
+ }
+ if (x & 0x01) {
+ printf("DEBUG: SFFP_TYPE_1000BASE_SX.\n");
+ return SFFP_TYPE_1000BASE_SX;
+ }
+ if (x & 0x02) {
+ printf("DEBUG: SFFP_TYPE_1000BASE_LX.\n");
+ return SFFP_TYPE_1000BASE_LX;
+ }
+ if (x & 0x08) {
+ printf("DEBUG: SFFP_TYPE_1000BASE_T.\n");
+ return SFFP_TYPE_1000BASE_T;
+ }
+ // Not one of the standard optical types..now try to deduce if it's twinax aka 10GSFP+CU
+ // which is not covered explicitly in SFF-8472
+ x = xge_i2c_rd(base, MODULE_DEV_ADDR, 8);
+ if (x < 0) {
+ printf("DEBUG: I2C error in SFPP_TYPE.\n");
+ return x;
+ }
+ if ((x & 4) == 0) // Passive SFP+ cable type
+ goto unknown;
+// x = xge_i2c_rd(MODULE_DEV_ADDR, 6);
+// printf("SFP+ reg6 read as %x\n",x);
+// if (x < 0)
+// return x;
+// if (x != 0x04) // Returns 1000Base-CX as Compliance code
+// goto unknown;
+ x = xge_i2c_rd(base, MODULE_DEV_ADDR, 0xA);
+ if (x < 0) {
+ printf("DEBUG: I2C error in SFPP_TYPE.\n");
+ return x;
+ }
+ if (x & 0x80) {
+ twinax:
+ // Reports 1200 MBytes/sec fibre channel speed..close enough to 10G ethernet!
+ x = xge_i2c_rd(base, MODULE_DEV_ADDR, 0x12);
+
+ if (x < 0) {
+ printf("DEBUG: I2C error in SFPP_TYPE.\n");
+ return x;
+ }
+ printf("DEBUG: TwinAx.\n");
+ // If cable length support is greater than 10M then pick correct type
+ return x > 10 ? SFFP_TYPE_TWINAX_LONG : SFFP_TYPE_TWINAX;
+ }
+unknown:
+ printf("DEBUG: Unknown SFP+ type.\n");
+ // Not a supported Module type
+ return SFFP_TYPE_UNKNOWN;
+}
+
+
+// Pull reset line low for 100ms then release and wait 100ms
+static void
+xge_hard_phy_reset(const uint32_t base)
+{
+ wb_poke32(base, 1);
+ mdelay(100);
+ wb_poke32(base, 0);
+ mdelay(100);
+
+}
+
+static void
+xge_mac_init(const uint32_t base)
+{
+ printf("INFO: Begining XGE MAC init sequence.\n");
+ xge_regs->config = XGE_TX_ENABLE;
+}
+
+// base is pointer to XGE MAC on Wishbone.
+static void
+xge_phy_init(const uint8_t eth, const uint32_t mdio_port)
+{
+ int x;
+ // Read LASI Ctrl register to capture state.
+ //y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A);
+ printf("INFO: Begining XGE PHY init sequence.\n");
+ // Software reset
+ x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port);
+ x = x | (1 << 15);
+ write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x);
+ //FIXME uncomment lines below when 1gigE MDIO works
+ //while(x&(1<<15))
+ // x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port);
+}
+
+void
+xge_poll_sfpp_status(const uint32_t eth)
+{
+ uint32_t x;
+ // Has MODDET/MODAbS changed since we last looked?
+ x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 ));
+
+ if (x & SFPP_STATUS_RXLOS_CHG)
+ printf("DEBUG: eth%1d RXLOS changed state: %d\n", eth, x & SFPP_STATUS_RXLOS);
+ if (x & SFPP_STATUS_TXFAULT_CHG)
+ printf("DEBUG: eth%1d TXFAULT changed state: %d\n", eth,(x & SFPP_STATUS_TXFAULT) >> 1 );
+ if (x & SFPP_STATUS_MODABS_CHG)
+ printf("DEBUG: eth%1d MODABS changed state: %d\n", eth, (x & SFPP_STATUS_MODABS) >> 2);
+
+ if (x & (SFPP_STATUS_RXLOS_CHG|SFPP_STATUS_TXFAULT_CHG|SFPP_STATUS_MODABS_CHG))
+ if (( x & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) {
+ xge_ethernet_init(eth);
+ dump_mdio_regs((eth==0) ? XGE0_BASE : XGE1_BASE,MDIO_PORT);
+ mdelay(100);
+ dump_mdio_regs((eth==0) ? XGE0_BASE : XGE1_BASE,MDIO_PORT);
+ mdelay(100);
+ dump_mdio_regs((eth==0) ? XGE0_BASE : XGE1_BASE,MDIO_PORT);
+ }
+
+ if (x & SFPP_STATUS_MODABS_CHG) {
+ // MODDET has changed state since last checked
+ if (x & SFPP_STATUS_MODABS) {
+ // MODDET is high, module currently removed.
+ printf("INFO: An SFP+ module has been removed from eth port %d.\n", eth);
+ } else {
+ // MODDET is low, module currently inserted.
+ // Return status.
+ printf("INFO: A new SFP+ module has been inserted into eth port %d.\n", eth);
+ xge_read_sfpp_type((eth==0) ? I2C0_BASE : I2C2_BASE,1);
+ }
+ }
+
+ //update the link up status
+ const bool old_link_up = links_up[eth];
+ links_up[eth] = ((read_mdio(eth, XGE_MDIO_STATUS1,XGE_MDIO_DEVICE_PMA,MDIO_PORT)) & (1 << 2)) != 0;
+ //The link became up, send a GARP so everyone knows our mac/ip association
+ if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth));
+}
+
+
+void
+xge_ethernet_init(const uint32_t eth)
+{
+ xge_mac_init((eth==0) ? XGE0_BASE : XGE1_BASE);
+ //xge_hard_phy_reset();
+ xge_phy_init(eth ,MDIO_PORT);
+ uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 ));
+ printf(" eth%1d SFP initial state: RXLOS: %d TXFAULT: %d MODABS: %d\n",
+ eth,
+ x & SFPP_STATUS_RXLOS,
+ (x & SFPP_STATUS_TXFAULT) >> 1,
+ (x & SFPP_STATUS_MODABS) >> 2);
+}
+
+//
+// Debug code to verbosely read XGE MDIO registers below here.
+//
+
+
+void decode_reg(uint32_t address, uint32_t device, uint32_t data)
+{
+ int x;
+ printf("Device: ");
+ printf("%x",device);
+ printf(" ");
+ switch(address) {
+ case XGE_MDIO_CONTROL1:
+ printf("CONTROL1: ");
+ printf("%x",data); printf(" ");
+ for (x=15; x >= 0 ; x--)
+ if ((data & (1 << x)) != 0)
+ // Bits set.
+ switch(x) {
+ case 15: printf("Reset,"); break;
+ case 14: printf("Loopback,"); break;
+ case 11: printf("Low Power Mode,"); break;
+ case 5:case 4:case 3:case 2: printf("RESERVED speed value,"); break;
+ case 0: printf("PMA loopback,"); break;
+ } //else
+ // Bits clear.
+ //switch (x) {
+ //case 13: case 6: printf(" None 10Gb/s speed set!"); break;
+ //}
+ printf(" \n");
+ break;
+ case XGE_MDIO_STATUS1:
+ printf("STATUS1: ");
+ printf("%x",data); printf(" ");
+ for (x=15; x >= 0 ; x--)
+ if ((data & (1 << x)) != 0)
+ // Bits set.
+ switch(x) {
+ case 7: printf("Fault Detected,"); break;
+ case 2: printf("Link is Up,"); break;
+ case 1: printf("Supports Low Power,"); break;
+ } else
+ // Bits Clear
+ switch(x) {
+ case 2: printf("Link is Down,"); break;
+ }
+ printf(" \n");
+ break;
+ case XGE_MDIO_SPEED:
+ printf("SPEED ABILITY: ");
+ printf("%x",data); printf(" ");
+ for (x=15; x >= 0 ; x--)
+ if ((data & (1 << x)) != 0)
+ // Bits set.
+ switch(x) {
+ case 15:case 14:case 13:case 12:case 11:case 10:case 9:
+ case 8:case 7:case 6:case 5:case 4:case 3:case 2:case 1: printf("RESERVED bits set!,"); break;
+ case 0: printf("Capable of 10Gb/s,");
+ } else
+ // Bits clear.
+ switch(x) {
+ case 0: printf("Incapable of 10Gb/s,"); break;
+ }
+ printf(" \n");
+ break;
+ case XGE_MDIO_DEVICES1:
+ printf("DEVICES IN PACKAGE: ");
+ printf("%x",data); printf(" ");
+ for (x=15; x >= 0 ; x--)
+ if ((data & (1 << x)) != 0)
+ // Bits set.
+ switch(x) {
+ case 7: printf("Auto-Negotiation,"); break;
+ case 6: printf("TC,"); break;
+ case 5: printf("DTE XS,"); break;
+ case 4: printf("PHY XS,"); break;
+ case 3: printf("PCS,"); break;
+ case 2: printf("WIS,"); break;
+ case 1: printf("PMD/PMA,"); break;
+ case 0: printf("Clause 22 registers,"); break;
+ }
+ printf(" \n");
+ break;
+ case XGE_MDIO_DEVICES2:
+ printf("DEVICES IN PACKAGE (cont): ");
+ printf("%x",data); printf(" ");
+ for (x=15; x >= 0 ; x--)
+ if ((data & (1 << x)) != 0)
+ // Bits set.
+ switch(x) {
+ case 15: printf("Vendor device 2,"); break;
+ case 14: printf("Vendor device 1,"); break;
+ case 13: printf("Clause 22 extension,"); break;
+ }
+ printf(" \n");
+ break;
+ case XGE_MDIO_CONTROL2:
+ printf("CONTROL2: ");
+ printf("%x",data); printf(" ");
+ // PMA/PMD
+ if (device == XGE_MDIO_DEVICE_PMA)
+ switch((data & 0xf)) {
+ case 0xF: printf("10BASE-T,"); break;
+ case 0xE: printf("100BASE-TX,"); break;
+ case 0xD: printf("1000BASE-KX,"); break;
+ case 0xC: printf("1000BASE-T,"); break;
+ case 0xB: printf("10GBASE-KR,"); break;
+ case 0xA: printf("10GBASE-KX4,"); break;
+ case 0x9: printf("10GBASE-T,"); break;
+ case 0x8: printf("10GBASE-LRM,"); break;
+ case 0x7: printf("10GBASE-SR,"); break;
+ case 0x6: printf("10GBASE-LR,"); break;
+ case 0x5: printf("10GBASE-ER,"); break;
+ case 0x4: printf("10GBASE-LX4,"); break;
+ // case 0x3: printf("10GBASE-SW,"); break;
+ // case 0x2: printf("10GBASE-LW,"); break;
+ // case 0x1: printf("10GBASE-EW,"); break;
+ case 0x0: printf("10GBASE-CX4,"); break;
+ } else if (device == XGE_MDIO_DEVICE_PCS)
+ // PCS
+ switch((data & 0x3)) {
+ case 0x3: printf("10GBASE-T PCS,"); break;
+ case 0x2: printf("10GBASE-W PCS,"); break;
+ case 0x1: printf("10GBASE-X PCS,"); break;
+ case 0x0: printf("10GBASE-R PCS,"); break;
+ }
+ printf(" \n");
+ break;
+ case XGE_MDIO_STATUS2:
+ printf("STATUS2: ");
+ printf("%x",data); printf(" ");
+ for (x=15; x >= 0 ; x--)
+ if ((data & (1 << x)) != 0)
+ // Bits set.
+ switch(x) {
+ case 15: if ((data & (1 << 14)) == 0) printf("Device responding,"); break;
+ case 13: if (device == XGE_MDIO_DEVICE_PMA) printf("Able detect a Tx fault,"); break;
+ case 12: if (device == XGE_MDIO_DEVICE_PMA) printf("Able detect an Rx fault,"); break;
+ case 11: printf("Fault on Tx path,"); break;
+ case 10: printf("Fault on Rx path,"); break;
+ case 9: if (device == XGE_MDIO_DEVICE_PMA) printf("Extended abilities in Reg1.11,"); break;
+ case 8: if (device == XGE_MDIO_DEVICE_PMA) printf("Able to disable TX,"); break;
+ case 7: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-SR,"); break;
+ case 6: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-LR,"); break;
+ case 5: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-ER,"); break;
+ case 4: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-LX4,"); break;
+ case 3: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-SW,"); break;
+ case 2: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-LW,"); break;
+ case 1: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-EW,"); break;
+ case 0: if (device == XGE_MDIO_DEVICE_PMA) printf("loopback,"); break;
+ }
+ printf(" \n");
+ break;
+ case XGE_MDIO_LANESTATUS:
+ printf("LANE STATUS: ");
+ printf("%x",data); printf(" ");
+ for (x=15; x >= 0 ; x--)
+ if ((data & (1 << x)) != 0)
+ // Bits set.
+ switch(x) {
+ case 12: printf("Lanes aligned,"); break;
+ case 11: printf("Able to generate test patterns,"); break;
+ case 3: printf("Lane 3 synced,"); break;
+ case 2: printf("Lane 2 synced,"); break;
+ case 1: printf("Lane 1 synced,"); break;
+ case 0: printf("Lane 0 synced,"); break;
+ } else
+ // Bits clear
+ switch(x) {
+ case 3: printf("Lane 3 not synced,"); break;
+ case 2: printf("Lane 2 not synced,"); break;
+ case 1: printf("Lane 1 not synced,"); break;
+ case 0: printf("Lane 0 not synced,"); break;
+ }
+ printf(" \n");
+ break;
+ case XILINX_CORE_VERSION:
+ printf("XILINX CORE VERSION: %x ",data);
+ printf("Version: %d.%d ",(data&0xf000)>>12,(data&0xf00)>>8);
+ printf("Patch: %d ",(data&0xE)>>1);
+ if (data&0x1) printf("Evaluation Version of core");
+ printf("\n");
+ break;
+ default:
+ printf("Register @ address: ");
+ printf("%x",address);
+ printf(" has value: ");
+ printf("%x\n",data);
+ break;
+ }
+}
+
+void
+dump_mdio_regs(const uint8_t eth, uint32_t mdio_port)
+{
+ volatile unsigned int x;
+ int y;
+ unsigned int regs_a[9] = {0,1,4,5,6,7,8,32,33};
+ unsigned int regs_b[10] = {0,1,4,5,6,7,8,10,11,65535};
+
+ printf("\n");
+
+ for (y = 0; y < 10; y++)
+ {
+ // Read MDIO data
+ x = read_mdio(eth,regs_b[y],XGE_MDIO_DEVICE_PMA,mdio_port);
+ decode_reg(regs_b[y],XGE_MDIO_DEVICE_PMA,x);
+ }
+
+ for (y = 0; y < 9; y++)
+ {
+ // Read MDIO data
+ x = read_mdio(eth,regs_a[y],XGE_MDIO_DEVICE_PCS,mdio_port);
+ decode_reg(regs_a[y],XGE_MDIO_DEVICE_PCS,x);
+ }
+
+ printf("\n");
+
+ /* for (y = 0; y < 8; y++) */
+ /* { */
+ /* // Read MDIO data */
+ /* x = xge_read_mdio(base,regs_a[y],XGE_MDIO_DEVICE_PHY_XS,mdio_port); */
+ /* decode_reg(regs_a[y],XGE_MDIO_DEVICE_PHY_XS,x); */
+ /* } */
+
+ /* for (y = 0; y < 8; y++) */
+ /* { */
+ /* // Read MDIO data */
+ /* x = xge_read_mdio(base,regs_a[y],XGE_MDIO_DEVICE_DTE_XS,mdio_port); */
+ /* decode_reg(regs_a[y],XGE_MDIO_DEVICE_DTE_XS,x); */
+ /* } */
+}
+
+bool ethernet_get_link_up(const uint32_t eth)
+{
+ return links_up[eth];
+}
diff --git a/firmware/x300/lib/link_state_route_proto.c b/firmware/x300/lib/link_state_route_proto.c
new file mode 100644
index 000000000..30cfd73cb
--- /dev/null
+++ b/firmware/x300/lib/link_state_route_proto.c
@@ -0,0 +1,441 @@
+
+// Copyright 2013 Ettus Research LLC
+
+#include <link_state_route_proto.h>
+#include <u3_net_stack.h>
+#include <ethernet.h>
+#include <string.h>
+#include <printf.h>
+#include <print_addrs.h>
+
+#define lengthof(a) (sizeof(a)/sizeof(*(a)))
+
+/***********************************************************************
+ * global constants
+ **********************************************************************/
+#define LS_PROTO_VERSION 6
+
+//shift the proto version into the ID so only matching fw responds
+#define LS_ID_DISCOVER (0 | (8 << LS_PROTO_VERSION))
+#define LS_ID_INFORM (1 | (8 << LS_PROTO_VERSION))
+
+#define LS_PAYLOAD_MTU 1024
+#define LS_NUM_NBOR_ENTRIES 16
+#define LS_NUM_NODE_ENTRIES 64
+#define LS_NUM_MAP_ENTRIES 128
+
+#define NETHS 4 //max eths supported in this file
+
+/***********************************************************************
+ * wire format for table communication
+ **********************************************************************/
+typedef struct
+{
+ uint32_t num_nbors; //number of entries in neighbors list
+ uint32_t num_ports; //first few neighbors are local ports
+ struct ip_addr node;
+ struct ip_addr nbors[];
+} ls_data_t;
+
+static inline size_t sizeof_ls_data(const ls_data_t *ls_data)
+{
+ return 0
+ + sizeof(uint32_t)/*num neighbors*/
+ + sizeof(uint32_t)/*num ports*/
+ + sizeof(struct ip_addr)/*source node*/
+ + sizeof(struct ip_addr)*ls_data->num_nbors;
+}
+
+/***********************************************************************
+ * sequence and tick counter monitor
+ **********************************************************************/
+static uint16_t ticker = 0;
+
+void link_state_route_proto_tick(void)
+{
+ ticker++;
+}
+
+static inline bool is_tick_expired(const uint16_t tick)
+{
+ const uint16_t delta = ticker - tick;
+ return delta > 2; //have not talked in a while, you are deaf to me
+}
+
+static uint16_t current_seq = 0;
+
+static inline bool is_seq_newer(const uint16_t seq, const uint16_t entry_seq)
+{
+ if (seq == entry_seq) return false; //not newer if equal
+ const uint16_t delta = seq - entry_seq;
+ return (delta & (1 << 15)) == 0; //newer when subtraction did not overflow
+}
+
+/***********************************************************************
+ * node entry api
+ **********************************************************************/
+typedef struct
+{
+ uint16_t seq;
+ uint16_t tick;
+ uint8_t ethno;
+ struct ip_addr ip_addr;
+} ls_node_entry_t;
+
+static bool ls_node_entry_valid(const ls_node_entry_t *entry)
+{
+ return entry->ip_addr.addr != 0 && !is_tick_expired(entry->tick);
+}
+
+static void ls_node_entry_update(ls_node_entry_t *entry, const int8_t ethno, const uint16_t seq, const struct ip_addr *ip_addr)
+{
+ entry->seq = seq;
+ entry->tick = ticker;
+ entry->ethno = ethno;
+ entry->ip_addr.addr = ip_addr->addr;
+}
+
+static bool ls_node_entries_update(
+ ls_node_entry_t *entries, const size_t num_entries,
+ const int8_t ethno, const uint16_t seq, const struct ip_addr *ip_addr
+)
+{
+ for (size_t i = 0; i < num_entries; i++)
+ {
+ if (!ls_node_entry_valid(&entries[i]))
+ {
+ ls_node_entry_update(entries+i, ethno, seq, ip_addr);
+ return true;
+ }
+
+ if (entries[i].ip_addr.addr == ip_addr->addr && entries[i].ethno == ethno)
+ {
+ if (is_seq_newer(seq, entries[i].seq))
+ {
+ ls_node_entry_update(entries+i, ethno, seq, ip_addr);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ //no space, shift the table down and take entry 0
+ memmove(entries+1, entries, (num_entries-1)*sizeof(ls_node_entry_t));
+ ls_node_entry_update(entries+0, ethno, seq, ip_addr);
+ return true;
+}
+
+/***********************************************************************
+ * storage for nodes in the network
+ **********************************************************************/
+static ls_node_entry_t ls_nbors[LS_NUM_NBOR_ENTRIES];
+static ls_node_entry_t ls_nodes[LS_NUM_NODE_ENTRIES];
+
+/***********************************************************************
+ * node table
+ **********************************************************************/
+static ls_node_mapping_t ls_node_maps[LS_NUM_MAP_ENTRIES];
+
+const ls_node_mapping_t *link_state_route_get_node_mapping(size_t *length)
+{
+ *length = lengthof(ls_node_maps);
+ return ls_node_maps;
+}
+
+static void add_node_mapping(const struct ip_addr *node, const struct ip_addr *nbor)
+{
+ //printf("add_node_mapping: %s -> %s\n", ip_addr_to_str(node), ip_addr_to_str(nbor));
+
+ //write into the first available slot
+ for (size_t i = 0; i < lengthof(ls_node_maps); i++)
+ {
+ if (ls_node_maps[i].node.addr == 0)
+ {
+ ls_node_maps[i].node.addr = node->addr;
+ ls_node_maps[i].nbor.addr = nbor->addr;
+ return;
+ }
+ }
+
+ //otherwise, shift down the table and take slot0
+ memmove(ls_node_maps+1, ls_node_maps, sizeof(ls_node_maps) - sizeof(ls_node_mapping_t));
+ ls_node_maps[0].node.addr = node->addr;
+ ls_node_maps[0].nbor.addr = nbor->addr;
+}
+
+static void remove_node_matches(const struct ip_addr *node)
+{
+ //printf("remove_node_matches: %s\n", ip_addr_to_str(node));
+
+ for (size_t j = 0; j < lengthof(ls_node_maps); j++)
+ {
+ //if the address is a match, clear the entry
+ if (ls_node_maps[j].node.addr == node->addr)
+ {
+ ls_node_maps[j].node.addr = 0;
+ ls_node_maps[j].nbor.addr = 0;
+ }
+ }
+}
+
+static void update_node_mappings(const ls_data_t *ls_data)
+{
+ //printf("update_node_mappings: %s\n", ip_addr_to_str(&ls_data->node));
+
+ //remove any expired entries
+ for (size_t i = 0; i < lengthof(ls_nodes); i++)
+ {
+ if (ls_nodes[i].ip_addr.addr != 0 && is_tick_expired(ls_nodes[i].tick))
+ {
+ remove_node_matches(&ls_nodes[i].ip_addr);
+ }
+ }
+
+ //remove any matches for the current node
+ remove_node_matches(&ls_data->node);
+
+ //is this a local packet?
+ bool is_local = false;
+ for (size_t e = 0; e < ethernet_ninterfaces(); e++)
+ {
+ if (ls_data->node.addr == u3_net_stack_get_ip_addr(e)->addr) is_local = true;
+ }
+
+ //load entries from ls data into array
+ for (size_t i = 0; i < ls_data->num_nbors; i++)
+ {
+ if (is_local && i < ls_data->num_ports) continue; //ignore local ports
+ add_node_mapping(&ls_data->node, &ls_data->nbors[i]);
+ }
+}
+
+/***********************************************************************
+ * forward link state data onto all neighbors on the given port
+ **********************************************************************/
+static void send_link_state_data_to_all_neighbors(
+ const uint8_t ethno, const uint16_t seq, const ls_data_t *ls_data
+){
+ //exit and dont forward if the information is stale
+ if (!ls_node_entries_update(ls_nodes, lengthof(ls_nodes), ethno, seq, &ls_data->node)) return;
+
+ //update the mappings with new info
+ update_node_mappings(ls_data);
+
+ //forward to all neighbors
+ for (size_t i = 0; i < lengthof(ls_nbors); i++)
+ {
+ if (ls_nbors[i].ip_addr.addr == ls_data->node.addr) continue; //dont forward to sender
+ if (ls_node_entry_valid(&ls_nbors[i]))
+ {
+ if (ethernet_get_link_up(ls_nbors[i].ethno)) u3_net_stack_send_icmp_pkt(
+ ls_nbors[i].ethno, ICMP_IRQ, 0,
+ LS_ID_INFORM, seq,
+ &(ls_nbors[i].ip_addr), ls_data, sizeof_ls_data(ls_data)
+ );
+ }
+ }
+
+ //a change may have occured, update the cache
+ link_state_route_proto_update_cycle_cache(ethno);
+}
+
+/***********************************************************************
+ * handler for information reply
+ **********************************************************************/
+static void handle_icmp_ir(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t id, const uint16_t seq,
+ const void *buff, const size_t num_bytes
+){
+ switch (id)
+ {
+ //received a reply directly from the neighbor, add to neighbor list
+ case LS_ID_DISCOVER:
+ //printf("GOT LS_ID_DISCOVER REPLY - ID 0x%x - IP%u: %s\n", id, (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno)));
+ if (ls_node_entries_update(ls_nbors, lengthof(ls_nbors), ethno, seq, src)) link_state_route_proto_flood(ethno);
+ break;
+ }
+}
+
+/***********************************************************************
+ * handler for information request
+ **********************************************************************/
+static void handle_icmp_irq(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t id, const uint16_t seq,
+ const void *buff, const size_t num_bytes
+){
+ switch (id)
+ {
+ //replies to discovery packets
+ case LS_ID_DISCOVER:
+ //printf("GOT LS_ID_DISCOVER REQ - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno)));
+ //printf("SEND LS_ID_DISCOVER REPLY - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno)));
+ u3_net_stack_send_icmp_pkt(ethno, ICMP_IR, 0, id, seq, src, buff, num_bytes);
+ break;
+
+ //handle and forward information
+ case LS_ID_INFORM:
+ //printf("GOT LS_ID_INFORM REQ - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno)));
+ send_link_state_data_to_all_neighbors(ethno, seq, (const ls_data_t *)buff);
+ break;
+ };
+}
+
+/***********************************************************************
+ * initiate a periodic update to the table
+ **********************************************************************/
+void link_state_route_proto_update(const uint8_t ethno)
+{
+ //send a discovery packet
+ //printf("SEND LS_ID_DISCOVER REQ - IP%u: %s\n", (int)ethno, ip_addr_to_str(u3_net_stack_get_ip_addr(ethno)));
+ u3_net_stack_send_icmp_pkt(
+ ethno, ICMP_IRQ, 0,
+ LS_ID_DISCOVER, current_seq++,
+ u3_net_stack_get_bcast(ethno), NULL, 0
+ );
+}
+
+void link_state_route_proto_flood(const uint8_t ethno)
+{
+ for (size_t e = 0; e < ethernet_ninterfaces(); e++)
+ {
+ //fill link state data buffer
+ uint8_t buff[LS_PAYLOAD_MTU] = {};
+ ls_data_t *ls_data = (ls_data_t *)buff;
+ ls_data->node.addr = u3_net_stack_get_ip_addr(e)->addr;
+ ls_data->num_nbors = 0;
+ ls_data->num_ports = 0;
+
+ //first the local port links
+ for (size_t ej = 0; ej < ethernet_ninterfaces(); ej++)
+ {
+ if (e == ej) continue; //dont include our own port
+ ls_data->nbors[ls_data->num_nbors++].addr = u3_net_stack_get_ip_addr(ej)->addr;
+ ls_data->num_ports++;
+ }
+
+ //now list the neighbors
+ for (size_t i = 0; i < lengthof(ls_nbors); i++)
+ {
+ if ((sizeof_ls_data(ls_data) + 4) >= LS_PAYLOAD_MTU) break;
+ if (ls_node_entry_valid(&ls_nbors[i]) && ls_nbors[i].ethno == e)
+ {
+ ls_data->nbors[ls_data->num_nbors++].addr = ls_nbors[i].ip_addr.addr;
+ }
+ }
+
+ //send this data to all neighbors
+ send_link_state_data_to_all_neighbors(ethno, current_seq++, ls_data);
+ }
+}
+
+/***********************************************************************
+ * cycle detection logic
+ **********************************************************************/
+static void follow_links(const size_t current, struct ip_addr *nodes, bool *visited, const size_t num_nodes)
+{
+ if (visited[current]) return; //end the recursion
+ visited[current] = true;
+
+ //follow all links where current node is the source
+ for (size_t i = 0; i < lengthof(ls_node_maps); i++)
+ {
+ if (ls_node_maps[i].node.addr != nodes[current].addr) continue;
+
+ //find the index of the neighbor in the node list to recurse
+ for (size_t j = 0; j < num_nodes; j++)
+ {
+ if (nodes[j].addr != ls_node_maps[i].nbor.addr) continue;
+ follow_links(j, nodes, visited, num_nodes);
+ }
+ }
+}
+
+bool link_state_route_proto_causes_cycle(const struct ip_addr *src, const struct ip_addr *dst)
+{
+ //printf("is there a cycle? %s -> %s: \n", ip_addr_to_str(src), ip_addr_to_str(dst));
+
+ //make a set of all nodes
+ size_t num_nodes = 0;
+ struct ip_addr nodes[LS_NUM_MAP_ENTRIES];
+ for (size_t i = 0; i < lengthof(ls_node_maps); i++)
+ {
+ if (ls_node_maps[i].node.addr == 0 || ls_node_maps[i].nbor.addr == 0) continue;
+ //printf(" Link %s -> %s\n", ip_addr_to_str(&ls_node_maps[i].node), ip_addr_to_str(&ls_node_maps[i].nbor));
+ const struct ip_addr *node = &ls_node_maps[i].node;
+
+ //check if we have an entry
+ for (size_t j = 0; j < num_nodes; j++)
+ {
+ if (nodes[j].addr == node->addr) goto skip_add;
+ }
+
+ //otherwise, we add the node
+ nodes[num_nodes++].addr = node->addr;
+ //printf(" Add to node set: %s\n", ip_addr_to_str(node));
+ skip_add: continue;
+ }
+
+ //and stateful tracking info for each node
+ bool visited[LS_NUM_MAP_ENTRIES];
+ for (size_t i = 0; i < num_nodes; i++) visited[i] = false;
+
+ //find our src node in the set and follow
+ for (size_t i = 0; i < num_nodes; i++)
+ {
+ if (nodes[i].addr == src->addr) follow_links(i, nodes, visited, num_nodes);
+ }
+
+ //did we visit the destination? if so, there is a cycle
+ for (size_t i = 0; i < num_nodes; i++)
+ {
+ if (nodes[i].addr == dst->addr && visited[i])
+ {
+ //printf("CAUSES CYCLE!\n");
+ return true;
+ }
+ }
+
+ //printf("no cycle found.\n");
+ return false;
+}
+
+static bool ls_causes_cycle[NETHS][NETHS];
+
+void link_state_route_proto_update_cycle_cache(const uint8_t eth_src)
+{
+ for (size_t eth_dst = 0; eth_dst < ethernet_ninterfaces(); eth_dst++)
+ {
+ if (eth_src == eth_dst) continue;
+ ls_causes_cycle[eth_src][eth_dst] = link_state_route_proto_causes_cycle(
+ u3_net_stack_get_ip_addr(eth_src),
+ u3_net_stack_get_ip_addr(eth_dst)
+ );
+ }
+}
+
+bool link_state_route_proto_causes_cycle_cached(const uint8_t eth_src, const uint8_t eth_dst)
+{
+ return ls_causes_cycle[eth_src][eth_dst];
+}
+
+/***********************************************************************
+ * init and registration code
+ **********************************************************************/
+void link_state_route_proto_init(void)
+{
+ u3_net_stack_register_icmp_handler(ICMP_IRQ, 0, &handle_icmp_irq);
+ u3_net_stack_register_icmp_handler(ICMP_IR, 0, &handle_icmp_ir);
+
+ //default to causing a cycle, let the algorithm set this correctly
+ for (size_t i = 0; i < NETHS; i++)
+ {
+ for (size_t j = 0; j < NETHS; j++)
+ {
+ ls_causes_cycle[i][j] = true;
+ }
+ }
+}
diff --git a/firmware/x300/lib/mdelay.c b/firmware/x300/lib/mdelay.c
new file mode 100644
index 000000000..6d2742206
--- /dev/null
+++ b/firmware/x300/lib/mdelay.c
@@ -0,0 +1,36 @@
+/* -*- c -*- */
+/*
+ * Copyright 2007 Free Software Foundation, Inc.
+ * Copyright 2009 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mdelay.h"
+#include "wb_utils.h"
+#include "printf.h"
+#include <stdint.h>
+//IJB FIXME.
+#include "../x300/x300_defs.h"
+
+void mdelay(int ms){
+ for(int i = 0; i < ms; i++){
+ static const uint32_t num_ticks = CPU_CLOCK/1000;
+ const uint32_t ticks_begin = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
+ // printf("DEBUG: Counter is %d\n",ticks_begin);
+ while((wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)) - ticks_begin) < num_ticks) {
+ /*NOP*/
+ }
+ }
+}
diff --git a/firmware/x300/lib/print_addrs.c b/firmware/x300/lib/print_addrs.c
new file mode 100644
index 000000000..6a710f75c
--- /dev/null
+++ b/firmware/x300/lib/print_addrs.c
@@ -0,0 +1,64 @@
+// Copyright 2013 Ettus Research LLC
+
+#include <print_addrs.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <printf.h>
+
+#define MAX_MAC_CHARS 24
+#define MAX_IP_CHARS 16
+
+static const char hex[16] = "0123456789ABCDEF";
+
+char *mac_addr_to_str_r(const void *addr, char *str)
+{
+ uint8_t *p = (uint8_t *)addr;
+ size_t j = 0;
+ for(size_t i = 0; i < 6; i++)
+ {
+ if (i) str[j++] = ':';
+ str[j++] = hex[(p[i] >> 4) & 0xf];
+ str[j++] = hex[p[i] & 0xf];
+ }
+ str[j++] = '\0';
+ return str;
+}
+
+char *ip_addr_to_str_r(const void *addr, char *str)
+{
+ uint8_t *p = (uint8_t *)addr;
+ sprintf(str, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ return str;
+}
+
+char *mac_addr_to_str(const void *addr)
+{
+ static size_t index = 0;
+ index = (index + 1) % 4;
+ static char str[4][MAX_MAC_CHARS];
+ return mac_addr_to_str_r(addr, str[index]);
+}
+
+char *ip_addr_to_str(const void *addr)
+{
+ static size_t index = 0;
+ index = (index + 1) % 4;
+ static char str[4][MAX_IP_CHARS];
+ return ip_addr_to_str_r(addr, str[index]);
+}
+
+/*
+void print_mac_addr(const void *addr)
+{
+ char str[MAX_MAC_CHARS];
+ mac_addr_to_str_r(addr, str);
+ printf("%s", str);
+}
+
+void print_ip_addr(const void *addr)
+{
+ char str[MAX_IP_CHARS];
+ ip_addr_to_str_r(addr, str);
+ printf("%s", str);
+}
+*/
diff --git a/firmware/x300/lib/printf.c b/firmware/x300/lib/printf.c
new file mode 100644
index 000000000..6d6a3e712
--- /dev/null
+++ b/firmware/x300/lib/printf.c
@@ -0,0 +1,287 @@
+/*
+File: printf.c
+
+Copyright (C) 2004 Kustaa Nyholm
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#include "printf.h"
+
+typedef void (*putcf) (void*,char);
+static putcf stdout_putf;
+static void* stdout_putp;
+
+#ifdef PRINTF_LONG_LONG_SUPPORT
+
+static void ulli2a(unsigned long long int num, unsigned int base, int uc,char * bf)
+ {
+ int n=0;
+ unsigned int d=1;
+ while (num/d >= base)
+ d*=base;
+ while (d!=0) {
+ int dgt = num / d;
+ num%=d;
+ d/=base;
+ if (n || dgt>0|| d==0) {
+ *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10);
+ ++n;
+ }
+ }
+ *bf=0;
+ }
+
+static void lli2a (long long num, char * bf)
+ {
+ if (num<0) {
+ num=-num;
+ *bf++ = '-';
+ }
+ ulli2a(num,10,0,bf);
+ }
+
+#endif
+
+#ifdef PRINTF_LONG_SUPPORT
+
+static void uli2a(unsigned long int num, unsigned int base, int uc,char * bf)
+ {
+ int n=0;
+ unsigned int d=1;
+ while (num/d >= base)
+ d*=base;
+ while (d!=0) {
+ int dgt = num / d;
+ num%=d;
+ d/=base;
+ if (n || dgt>0|| d==0) {
+ *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10);
+ ++n;
+ }
+ }
+ *bf=0;
+ }
+
+static void li2a (long num, char * bf)
+ {
+ if (num<0) {
+ num=-num;
+ *bf++ = '-';
+ }
+ uli2a(num,10,0,bf);
+ }
+
+#endif
+
+static void ui2a(unsigned int num, unsigned int base, int uc,char * bf)
+ {
+ int n=0;
+ unsigned int d=1;
+ while (num/d >= base)
+ d*=base;
+ while (d!=0) {
+ int dgt = num / d;
+ num%= d;
+ d/=base;
+ if (n || dgt>0 || d==0) {
+ *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10);
+ ++n;
+ }
+ }
+ *bf=0;
+ }
+
+static void i2a (int num, char * bf)
+ {
+ if (num<0) {
+ num=-num;
+ *bf++ = '-';
+ }
+ ui2a(num,10,0,bf);
+ }
+
+static int a2d(char ch)
+ {
+ if (ch>='0' && ch<='9')
+ return ch-'0';
+ else if (ch>='a' && ch<='f')
+ return ch-'a'+10;
+ else if (ch>='A' && ch<='F')
+ return ch-'A'+10;
+ else return -1;
+ }
+
+static char a2i(char ch, char** src,int base,int* nump)
+ {
+ char* p= *src;
+ int num=0;
+ int digit;
+ while ((digit=a2d(ch))>=0) {
+ if (digit>base) break;
+ num=num*base+digit;
+ ch=*p++;
+ }
+ *src=p;
+ *nump=num;
+ return ch;
+ }
+
+static void putchw(void* putp,putcf putf,int n, char z, char* bf)
+ {
+ char fc=z? '0' : ' ';
+ char ch;
+ char* p=bf;
+ while (*p++ && n > 0)
+ n--;
+ while (n-- > 0)
+ putf(putp,fc);
+ while ((ch= *bf++))
+ putf(putp,ch);
+ }
+
+void tfp_format(void* putp,putcf putf,char *fmt, va_list va)
+ {
+ char bf[12];
+
+ char ch;
+
+
+ while ((ch=*(fmt++))) {
+ if (ch!='%')
+ putf(putp,ch);
+ else {
+ char lz=0;
+#ifdef PRINTF_LONG_SUPPORT
+ char lng=0;
+#endif
+#ifdef PRINTF_LONG_LONG_SUPPORT
+ char lnglng=0;
+#endif
+ int w=0;
+ ch=*(fmt++);
+ if (ch=='0') {
+ ch=*(fmt++);
+ lz=1;
+ }
+ if (ch>='0' && ch<='9') {
+ ch=a2i(ch,&fmt,10,&w);
+ }
+#ifdef PRINTF_LONG_SUPPORT
+ if (ch=='l') {
+ ch=*(fmt++);
+ lng=1;
+ }
+#endif
+#ifdef PRINTF_LONG_LONG_SUPPORT
+ if ((ch=='l')&&(lng==1)) {
+ ch=*(fmt++);
+ lnglng=1;
+ }
+#endif
+ switch (ch) {
+ case 0:
+ goto abort;
+ case 'u' : {
+#ifdef PRINTF_LONG_LONG_SUPPORT
+ if (lnglng)
+ ulli2a(va_arg(va, unsigned long long int),10,0,bf);
+ else
+#endif
+#ifdef PRINTF_LONG_SUPPORT
+ if (lng)
+ uli2a(va_arg(va, unsigned long int),10,0,bf);
+ else
+#endif
+ ui2a(va_arg(va, unsigned int),10,0,bf);
+ putchw(putp,putf,w,lz,bf);
+ break;
+ }
+ case 'd' : {
+#ifdef PRINTF_LONG_LONG_SUPPORT
+ if (lnglng)
+ lli2a(va_arg(va, long long int),bf);
+ else
+#endif
+#ifdef PRINTF_LONG_SUPPORT
+ if (lng)
+ li2a(va_arg(va, long int),bf);
+ else
+#endif
+ i2a(va_arg(va, int),bf);
+ putchw(putp,putf,w,lz,bf);
+ break;
+ }
+ case 'x': case 'X' :
+#ifdef PRINTF_LONG_LONG_SUPPORT
+ if (lnglng)
+ ulli2a(va_arg(va, unsigned long long int),16,(ch=='X'),bf);
+ else
+#endif
+#ifdef PRINTF_LONG_SUPPORT
+ if (lng)
+ uli2a(va_arg(va, unsigned long int),16,(ch=='X'),bf);
+ else
+#endif
+ ui2a(va_arg(va, unsigned int),16,(ch=='X'),bf);
+ putchw(putp,putf,w,lz,bf);
+ break;
+ case 'c' :
+ putf(putp,(char)(va_arg(va, int)));
+ break;
+ case 's' :
+ putchw(putp,putf,w,0,va_arg(va, char*));
+ break;
+ case '%' :
+ putf(putp,ch);
+ default:
+ break;
+ }
+ }
+ }
+ abort:;
+ }
+
+
+void init_printf(void* putp,void (*putf) (void*,char))
+ {
+ stdout_putf=putf;
+ stdout_putp=putp;
+ }
+
+void tfp_printf(char *fmt, ...)
+ {
+ va_list va;
+ va_start(va,fmt);
+ tfp_format(stdout_putp,stdout_putf,fmt,va);
+ va_end(va);
+ }
+
+static void putcp(void* p,char c)
+ {
+ *(*((char**)p))++ = c;
+ }
+
+
+
+void tfp_sprintf(char* s,char *fmt, ...)
+ {
+ va_list va;
+ va_start(va,fmt);
+ tfp_format(&s,putcp,fmt,va);
+ putcp(&s,0);
+ va_end(va);
+ }
diff --git a/firmware/x300/lib/u3_net_stack.c b/firmware/x300/lib/u3_net_stack.c
new file mode 100644
index 000000000..6b8ef096c
--- /dev/null
+++ b/firmware/x300/lib/u3_net_stack.c
@@ -0,0 +1,589 @@
+
+// Copyright 2012-2013 Ettus Research LLC
+
+#include <u3_net_stack.h>
+#include <string.h> //memcmp
+#include <printf.h>
+
+#define MAX_NETHS 4
+
+typedef struct
+{
+ padded_eth_hdr_t eth;
+ struct arp_eth_ipv4 arp;
+} padded_arp_t;
+
+typedef struct
+{
+ padded_eth_hdr_t eth;
+ struct ip_hdr ip;
+ struct icmp_echo_hdr icmp;
+} padded_icmp_t;
+
+typedef struct
+{
+ padded_eth_hdr_t eth;
+ struct ip_hdr ip;
+ struct udp_hdr udp;
+} padded_udp_t;
+
+/***********************************************************************
+ * declares for internal handlers
+ **********************************************************************/
+static void handle_icmp_echo_packet(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t id, const uint16_t seq,
+ const void *buff, const size_t num_bytes
+){
+ u3_net_stack_send_icmp_pkt(ethno, ICMP_ER, 0, id, seq, src, buff, num_bytes);
+}
+
+static void handle_icmp_dur_packet(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t id, const uint16_t seq,
+ const void *buff, const size_t num_bytes
+);
+
+/***********************************************************************
+ * 16-bit one's complement sum
+ **********************************************************************/
+static uint32_t chksum_buffer(
+ uint16_t *buf, size_t nshorts,
+ uint32_t initial_chksum
+){
+ uint32_t chksum = initial_chksum;
+ for (size_t i = 0; i < nshorts; i++) chksum += buf[i];
+
+ while (chksum >> 16) chksum = (chksum & 0xffff) + (chksum >> 16);
+
+ return chksum;
+}
+
+/***********************************************************************
+ * ARP Cache implementation
+ **********************************************************************/
+#define ARP_CACHE_NENTRIES 32
+
+static size_t arp_cache_wr_index;
+
+static struct ip_addr arp_cache_ips[ARP_CACHE_NENTRIES];
+static eth_mac_addr_t arp_cache_macs[ARP_CACHE_NENTRIES];
+static uint8_t arp_cache_eths[ARP_CACHE_NENTRIES];
+
+void u3_net_stack_arp_cache_update(const struct ip_addr *ip_addr, const eth_mac_addr_t *mac_addr, const uint8_t ethno)
+{
+ for (size_t i = 0; i < ARP_CACHE_NENTRIES; i++)
+ {
+ if (memcmp(ip_addr, arp_cache_ips+i, sizeof(struct ip_addr)) == 0)
+ {
+ memcpy(arp_cache_macs+i, mac_addr, sizeof(eth_mac_addr_t));
+ arp_cache_eths[i] = ethno;
+ return;
+ }
+ }
+ if (arp_cache_wr_index >= ARP_CACHE_NENTRIES) arp_cache_wr_index = 0;
+ memcpy(arp_cache_ips+arp_cache_wr_index, ip_addr, sizeof(struct ip_addr));
+ memcpy(arp_cache_macs+arp_cache_wr_index, mac_addr, sizeof(eth_mac_addr_t));
+ arp_cache_eths[arp_cache_wr_index] = ethno;
+ arp_cache_wr_index++;
+}
+
+const eth_mac_addr_t *u3_net_stack_arp_cache_lookup(const struct ip_addr *ip_addr)
+{
+ //do a local look up on our own ports
+ for (size_t e = 0; e < MAX_NETHS; e++)
+ {
+ if (memcmp(ip_addr, u3_net_stack_get_ip_addr(e), sizeof(struct ip_addr)) == 0)
+ {
+ return u3_net_stack_get_mac_addr(e);
+ }
+ }
+ //now check the arp cache
+ for (size_t i = 0; i < ARP_CACHE_NENTRIES; i++)
+ {
+ if (memcmp(ip_addr, arp_cache_ips+i, sizeof(struct ip_addr)) == 0)
+ {
+ return &arp_cache_macs[i];
+ }
+ }
+ return NULL;
+}
+
+bool resolve_ip(const struct ip_addr *ip_addr, eth_mac_addr_t *mac_addr)
+{
+ for (size_t e = 0; e < MAX_NETHS; e++)
+ {
+ if (memcmp(u3_net_stack_get_bcast(e), ip_addr, sizeof(struct ip_addr)) == 0)
+ {
+ memset(mac_addr, 0xff, sizeof(eth_mac_addr_t));
+ return true;
+ }
+ }
+ const eth_mac_addr_t *r = u3_net_stack_arp_cache_lookup(ip_addr);
+ if (r != NULL)
+ {
+ memcpy(mac_addr, r, sizeof(eth_mac_addr_t));
+ return true;
+ }
+ return false;
+}
+
+/***********************************************************************
+ * Net stack config
+ **********************************************************************/
+static wb_pkt_iface64_config_t *pkt_iface_config = NULL;
+
+void u3_net_stack_init(wb_pkt_iface64_config_t *config)
+{
+ pkt_iface_config = config;
+ u3_net_stack_register_icmp_handler(ICMP_ECHO, 0, &handle_icmp_echo_packet);
+ u3_net_stack_register_icmp_handler(ICMP_DUR, ICMP_DUR_PORT, &handle_icmp_dur_packet);
+}
+
+static struct ip_addr net_conf_ips[MAX_NETHS];
+static eth_mac_addr_t net_conf_macs[MAX_NETHS];
+static struct ip_addr net_conf_subnets[MAX_NETHS];
+static struct ip_addr net_conf_bcasts[MAX_NETHS];
+static uint32_t net_stat_counts[MAX_NETHS];
+
+void u3_net_stack_init_eth(
+ const uint8_t ethno,
+ const eth_mac_addr_t *mac,
+ const struct ip_addr *ip,
+ const struct ip_addr *subnet
+)
+{
+ memcpy(&net_conf_macs[ethno], mac, sizeof(eth_mac_addr_t));
+ memcpy(&net_conf_ips[ethno], ip, sizeof(struct ip_addr));
+ memcpy(&net_conf_subnets[ethno], subnet, sizeof(struct ip_addr));
+ net_stat_counts[ethno] = 0;
+}
+
+const struct ip_addr *u3_net_stack_get_ip_addr(const uint8_t ethno)
+{
+ return &net_conf_ips[ethno];
+}
+
+const struct ip_addr *u3_net_stack_get_subnet(const uint8_t ethno)
+{
+ return &net_conf_subnets[ethno];
+}
+
+const struct ip_addr *u3_net_stack_get_bcast(const uint8_t ethno)
+{
+ const uint32_t ip = u3_net_stack_get_ip_addr(ethno)->addr;
+ const uint32_t subnet = u3_net_stack_get_subnet(ethno)->addr;
+ net_conf_bcasts[ethno].addr = ip | (~subnet);
+ return &net_conf_bcasts[ethno];
+}
+
+const eth_mac_addr_t *u3_net_stack_get_mac_addr(const uint8_t ethno)
+{
+ return &net_conf_macs[ethno];
+}
+
+/***********************************************************************
+ * Ethernet activity stats
+ **********************************************************************/
+uint32_t u3_net_stack_get_stat_counts(const uint8_t ethno)
+{
+ return net_stat_counts[ethno];
+}
+
+static void incr_stat_counts(const void *p)
+{
+ const padded_eth_hdr_t *eth = (const padded_eth_hdr_t *)p;
+ if (eth->ethno < MAX_NETHS) net_stat_counts[eth->ethno]++;
+}
+
+/***********************************************************************
+ * Ethernet handlers - send packet w/ payload
+ **********************************************************************/
+static void send_eth_pkt(
+ const void *p0, const size_t l0,
+ const void *p1, const size_t l1,
+ const void *p2, const size_t l2
+)
+{
+ incr_stat_counts(p0);
+ void *ptr = wb_pkt_iface64_tx_claim(pkt_iface_config);
+ size_t buff_i = 0;
+
+ uint32_t *buff32 = (uint32_t *)ptr;
+ for (size_t i = 0; i < (l0+3)/4; i++)
+ {
+ buff32[buff_i++] = ((const uint32_t *)p0)[i];
+ }
+ for (size_t i = 0; i < (l1+3)/4; i++)
+ {
+ buff32[buff_i++] = ((const uint32_t *)p1)[i];
+ }
+ for (size_t i = 0; i < (l2+3)/4; i++)
+ {
+ buff32[buff_i++] = ((const uint32_t *)p2)[i];
+ }
+
+ // Fixes issue where we don't write an even number of 32bit words leaving last data stranded in H/W.
+ if ((buff_i%2) == 1) buff32[buff_i++] = 0;
+
+ wb_pkt_iface64_tx_submit(pkt_iface_config, l0 + l1 + l2);
+}
+
+/***********************************************************************
+ * ARP handlers
+ **********************************************************************/
+static void send_arp_reply(
+ const uint8_t ethno,
+ const struct arp_eth_ipv4 *req,
+ const eth_mac_addr_t *our_mac
+){
+ padded_arp_t reply;
+ reply.eth.ethno = ethno;
+ memcpy(&reply.eth.dst, (eth_mac_addr_t *)req->ar_sha, sizeof(eth_mac_addr_t));
+ memcpy(&reply.eth.src, u3_net_stack_get_mac_addr(ethno), sizeof(eth_mac_addr_t));
+ reply.eth.ethertype = ETHERTYPE_ARP;
+
+ reply.arp.ar_hrd = req->ar_hrd;
+ reply.arp.ar_pro = req->ar_pro;
+ reply.arp.ar_hln = req->ar_hln;
+ reply.arp.ar_pln = req->ar_pln;
+ reply.arp.ar_op = ARPOP_REPLY;
+ memcpy(reply.arp.ar_sha, our_mac, sizeof(eth_mac_addr_t));
+ memcpy(reply.arp.ar_sip, req->ar_tip, sizeof(struct ip_addr));
+ memcpy(reply.arp.ar_tha, req->ar_sha, sizeof(eth_mac_addr_t));
+ memcpy(reply.arp.ar_tip, req->ar_sip, sizeof(struct ip_addr));
+
+ send_eth_pkt(&reply, sizeof(reply), NULL, 0, NULL, 0);
+}
+
+void u3_net_stack_send_arp_request(const uint8_t ethno, const struct ip_addr *addr)
+{
+ padded_arp_t req;
+ req.eth.ethno = ethno;
+ memset(&req.eth.dst, 0xff, sizeof(eth_mac_addr_t)); //bcast
+ memcpy(&req.eth.src, u3_net_stack_get_mac_addr(ethno), sizeof(eth_mac_addr_t));
+ req.eth.ethertype = ETHERTYPE_ARP;
+
+ req.arp.ar_hrd = ARPHRD_ETHER;
+ req.arp.ar_pro = ETHERTYPE_IPV4;
+ req.arp.ar_hln = sizeof(eth_mac_addr_t);
+ req.arp.ar_pln = sizeof(struct ip_addr);
+ req.arp.ar_op = ARPOP_REQUEST;
+ memcpy(req.arp.ar_sha, u3_net_stack_get_mac_addr(ethno), sizeof(eth_mac_addr_t));
+ memcpy(req.arp.ar_sip, u3_net_stack_get_ip_addr(ethno), sizeof(struct ip_addr));
+ memset(req.arp.ar_tha, 0x00, sizeof(eth_mac_addr_t));
+ memcpy(req.arp.ar_tip, addr, sizeof(struct ip_addr));
+
+ send_eth_pkt(&req, sizeof(req), NULL, 0, NULL, 0);
+}
+
+static void handle_arp_packet(const uint8_t ethno, const struct arp_eth_ipv4 *p)
+{
+ //printf("handle_arp_packet\n");
+ if (p->ar_hrd != ARPHRD_ETHER
+ || p->ar_pro != ETHERTYPE_IPV4
+ || p->ar_hln != sizeof(eth_mac_addr_t)
+ || p->ar_pln != sizeof(struct ip_addr))
+ return;
+
+ //got an arp reply -- injest it into the arp cache
+ if (p->ar_op == ARPOP_REPLY)
+ {
+ //printf("ARPOP_REPLY\n");
+ struct ip_addr ip_addr;
+ memcpy(&ip_addr, p->ar_sip, sizeof(ip_addr));
+ eth_mac_addr_t mac_addr;
+ memcpy(&mac_addr, p->ar_sha, sizeof(mac_addr));
+ u3_net_stack_arp_cache_update(&ip_addr, &mac_addr, ethno);
+ }
+
+ //got an arp request -- reply if its for our address
+ if (p->ar_op == ARPOP_REQUEST)
+ {
+ //printf("ARPOP_REQUEST\n");
+ if (memcmp(p->ar_tip, u3_net_stack_get_ip_addr(ethno), sizeof(struct ip_addr)) == 0)
+ {
+ send_arp_reply(ethno, p, u3_net_stack_get_mac_addr(ethno));
+ }
+ }
+}
+
+/***********************************************************************
+ * UDP handlers
+ **********************************************************************/
+#define UDP_NHANDLERS 16
+
+static uint16_t udp_handler_ports[UDP_NHANDLERS];
+static u3_net_stack_udp_handler_t udp_handlers[UDP_NHANDLERS];
+static size_t udp_handlers_index = 0;
+
+void u3_net_stack_register_udp_handler(
+ const uint16_t port,
+ const u3_net_stack_udp_handler_t handler
+)
+{
+ if (udp_handlers_index < UDP_NHANDLERS)
+ {
+ udp_handler_ports[udp_handlers_index] = port;
+ udp_handlers[udp_handlers_index] = handler;
+ udp_handlers_index++;
+ }
+}
+
+void u3_net_stack_send_udp_pkt(
+ const uint8_t ethno,
+ const struct ip_addr *dst,
+ const uint16_t src_port,
+ const uint16_t dst_port,
+ const void *buff,
+ const size_t num_bytes
+)
+{
+ eth_mac_addr_t dst_mac_addr;
+ if (!resolve_ip(dst, &dst_mac_addr))
+ {
+ printf("u3_net_stack_send_udp_pkt arp_cache_lookup fail\n");
+ return;
+ }
+
+ padded_udp_t pkt;
+
+ pkt.eth.ethno = ethno;
+ memcpy(&pkt.eth.dst, &dst_mac_addr, sizeof(eth_mac_addr_t));
+ memcpy(&pkt.eth.src, u3_net_stack_get_mac_addr(ethno), sizeof(eth_mac_addr_t));
+ pkt.eth.ethertype = ETHERTYPE_IPV4;
+
+ IPH_VHLTOS_SET(&pkt.ip, 4, 5, 0);
+ IPH_LEN_SET(&pkt.ip, IP_HLEN + UDP_HLEN + num_bytes);
+ IPH_ID_SET(&pkt.ip, 0);
+ IPH_OFFSET_SET(&pkt.ip, IP_DF); /* don't fragment */
+ IPH_TTL_SET(&pkt.ip, 32);
+ IPH_PROTO_SET(&pkt.ip, IP_PROTO_UDP);
+ IPH_CHKSUM_SET(&pkt.ip, 0);
+ memcpy(&pkt.ip.src, u3_net_stack_get_ip_addr(ethno), sizeof(struct ip_addr));
+ memcpy(&pkt.ip.dest, dst, sizeof(struct ip_addr));
+
+ IPH_CHKSUM_SET(&pkt.ip, ~chksum_buffer(
+ (unsigned short *) &pkt.ip, sizeof(pkt.ip)/sizeof(short), 0
+ ));
+
+ pkt.udp.src = src_port;
+ pkt.udp.dest = dst_port;
+ pkt.udp.len = UDP_HLEN + num_bytes;
+ pkt.udp.chksum = 0;
+
+ send_eth_pkt(&pkt, sizeof(pkt), buff, num_bytes, NULL, 0);
+}
+
+static void handle_udp_packet(
+ const uint8_t ethno,
+ const struct ip_addr *src,
+ const struct ip_addr *dst,
+ const struct udp_hdr *udp,
+ const size_t num_bytes
+){
+ for (size_t i = 0; i < udp_handlers_index; i++)
+ {
+ if (udp_handler_ports[i] == udp->dest)
+ {
+ udp_handlers[i](
+ ethno, src, u3_net_stack_get_ip_addr(ethno), udp->src, udp->dest,
+ ((const uint8_t *)udp) + sizeof(struct udp_hdr),
+ num_bytes - UDP_HLEN
+ );
+ return;
+ }
+ }
+ printf("Unhandled UDP packet src=%u, dest=%u\n", udp->src, udp->dest);
+ //TODO send destination unreachable
+}
+
+/***********************************************************************
+ * ICMP handlers
+ **********************************************************************/
+#define ICMP_NHANDLERS 8
+
+static uint8_t icmp_handler_types[ICMP_NHANDLERS];
+static uint8_t icmp_handler_codes[ICMP_NHANDLERS];
+static u3_net_stack_icmp_handler_t icmp_handlers[ICMP_NHANDLERS];
+static size_t icmp_handlers_index = 0;
+
+void u3_net_stack_register_icmp_handler(
+ const uint8_t type,
+ const uint8_t code,
+ const u3_net_stack_icmp_handler_t handler
+)
+{
+ if (icmp_handlers_index < ICMP_NHANDLERS)
+ {
+ icmp_handler_types[icmp_handlers_index] = type;
+ icmp_handler_codes[icmp_handlers_index] = code;
+ icmp_handlers[icmp_handlers_index] = handler;
+ icmp_handlers_index++;
+ }
+}
+
+static void handle_icmp_packet(
+ const uint8_t ethno,
+ const struct ip_addr *src,
+ const struct ip_addr *dst,
+ const struct icmp_echo_hdr *icmp,
+ const size_t num_bytes
+){
+
+ for (size_t i = 0; i < icmp_handlers_index; i++)
+ {
+ if (icmp_handler_types[i] == icmp->type && icmp_handler_codes[i] == icmp->code)
+ {
+ icmp_handlers[i](
+ ethno, src, u3_net_stack_get_ip_addr(ethno), icmp->id, icmp->seqno,
+ ((const uint8_t *)icmp) + sizeof(struct icmp_echo_hdr),
+ num_bytes - sizeof(struct icmp_echo_hdr)
+ );
+ return;
+ }
+ }
+ printf("Unhandled ICMP packet type=%u\n", icmp->type);
+}
+
+static void handle_icmp_dur_packet(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t id, const uint16_t seq,
+ const void *buff, const size_t num_bytes
+){
+ struct ip_hdr *ip = (struct ip_hdr *)buff;
+ struct udp_hdr *udp = (struct udp_hdr *)(((char *)ip) + IP_HLEN);
+ if (IPH_PROTO(ip) != IP_PROTO_UDP) return;
+ for (size_t i = 0; i < udp_handlers_index; i++)
+ {
+ if (udp_handler_ports[i] == udp->src)
+ {
+ udp_handlers[i](ethno,
+ src, u3_net_stack_get_ip_addr(ethno),
+ udp->src, udp->dest, NULL, 0
+ );
+ return;
+ }
+ }
+}
+
+void u3_net_stack_send_icmp_pkt(
+ const uint8_t ethno,
+ const uint8_t type,
+ const uint8_t code,
+ const uint16_t id,
+ const uint16_t seq,
+ const struct ip_addr *dst,
+ const void *buff,
+ const size_t num_bytes
+)
+{
+ eth_mac_addr_t dst_mac_addr;
+ if (!resolve_ip(dst, &dst_mac_addr))
+ {
+ printf("u3_net_stack_send_echo_request arp_cache_lookup fail\n");
+ return;
+ }
+
+ padded_icmp_t pkt;
+
+ pkt.eth.ethno = ethno;
+ memcpy(&pkt.eth.dst, &dst_mac_addr, sizeof(eth_mac_addr_t));
+ memcpy(&pkt.eth.src, u3_net_stack_get_mac_addr(ethno), sizeof(eth_mac_addr_t));
+ pkt.eth.ethertype = ETHERTYPE_IPV4;
+
+ IPH_VHLTOS_SET(&pkt.ip, 4, 5, 0);
+ IPH_LEN_SET(&pkt.ip, IP_HLEN + sizeof(pkt.icmp) + num_bytes);
+ IPH_ID_SET(&pkt.ip, 0);
+ IPH_OFFSET_SET(&pkt.ip, IP_DF); /* don't fragment */
+ IPH_TTL_SET(&pkt.ip, 32);
+ IPH_PROTO_SET(&pkt.ip, IP_PROTO_ICMP);
+ IPH_CHKSUM_SET(&pkt.ip, 0);
+ memcpy(&pkt.ip.src, u3_net_stack_get_ip_addr(ethno), sizeof(struct ip_addr));
+ memcpy(&pkt.ip.dest, dst, sizeof(struct ip_addr));
+
+ IPH_CHKSUM_SET(&pkt.ip, ~chksum_buffer(
+ (unsigned short *) &pkt.ip, sizeof(pkt.ip)/sizeof(short), 0
+ ));
+
+ pkt.icmp.type = type;
+ pkt.icmp.code = code;
+ pkt.icmp.chksum = 0;
+ pkt.icmp.id = id;
+ pkt.icmp.seqno = seq;
+ pkt.icmp.chksum = ~chksum_buffer( //data checksum
+ (unsigned short *)buff,
+ num_bytes/sizeof(short),
+ chksum_buffer( //header checksum
+ (unsigned short *)&pkt.icmp,
+ sizeof(pkt.icmp)/sizeof(short),
+ 0)
+ );
+
+ send_eth_pkt(&pkt, sizeof(pkt), buff, num_bytes, NULL, 0);
+}
+
+/***********************************************************************
+ * Ethernet handlers
+ **********************************************************************/
+static void handle_eth_packet(const void *buff, const size_t num_bytes)
+{
+ const padded_eth_hdr_t *eth_hdr = (padded_eth_hdr_t *)buff;
+ const uint8_t *eth_body = ((const uint8_t *)buff) + sizeof(padded_eth_hdr_t);
+ //printf("handle_eth_packet got ethertype 0x%x\n", (unsigned)eth_hdr->ethertype);
+
+ if (eth_hdr->ethertype == ETHERTYPE_ARP)
+ {
+ //printf("eth_hdr->ethertype == ETHERTYPE_ARP\n");
+ const struct arp_eth_ipv4 *arp = (const struct arp_eth_ipv4 *)eth_body;
+ handle_arp_packet(eth_hdr->ethno, arp);
+ }
+ else if (eth_hdr->ethertype == ETHERTYPE_IPV4)
+ {
+ //printf("eth_hdr->ethertype == ETHERTYPE_IPV4\n");
+ const struct ip_hdr *ip = (const struct ip_hdr *)eth_body;
+ const uint8_t *ip_body = eth_body + IP_HLEN;
+
+ if (IPH_V(ip) != 4 || IPH_HL(ip) != 5) return;// ignore pkts w/ bad version or options
+ if (IPH_OFFSET(ip) & (IP_MF | IP_OFFMASK)) return;// ignore fragmented packets
+
+ //TODO -- only handle when the mac is bcast or IP is for us
+
+ u3_net_stack_arp_cache_update(&ip->src, &eth_hdr->src, eth_hdr->ethno);
+
+ if (IPH_PROTO(ip) == IP_PROTO_UDP)
+ {
+ handle_udp_packet(
+ eth_hdr->ethno, &ip->src, &ip->dest,
+ (const struct udp_hdr *)ip_body,
+ IPH_LEN(ip) - IP_HLEN
+ );
+ }
+
+ if (IPH_PROTO(ip) == IP_PROTO_ICMP)
+ {
+ handle_icmp_packet(
+ eth_hdr->ethno, &ip->src, &ip->dest,
+ (const struct icmp_echo_hdr *)ip_body,
+ IPH_LEN(ip) - IP_HLEN
+ );
+ }
+ }
+ else return; // Not ARP or IPV4, ignore
+}
+
+void u3_net_stack_handle_one(void)
+{
+ size_t num_bytes = 0;
+ const void *ptr = wb_pkt_iface64_rx_try_claim(pkt_iface_config, &num_bytes);
+ if (ptr != NULL)
+ {
+ //printf("u3_net_stack_handle_one got %u bytes\n", (unsigned)num_bytes);
+ incr_stat_counts(ptr);
+ handle_eth_packet(ptr, num_bytes);
+ wb_pkt_iface64_rx_release(pkt_iface_config);
+ }
+}
diff --git a/firmware/x300/lib/udp_uart.c b/firmware/x300/lib/udp_uart.c
new file mode 100644
index 000000000..d078aa2c3
--- /dev/null
+++ b/firmware/x300/lib/udp_uart.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2011-2013 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_uart.h>
+#include <udp_uart.h>
+#include <u3_net_stack.h>
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+#define MAX_NUM_UARTS 4
+
+static const size_t num_idle_cyc_b4_flush = 22;
+
+/***********************************************************************
+ * Globals
+ **********************************************************************/
+typedef struct
+{
+ uint32_t uart_base;
+ struct ip_addr host_addr;
+ int host_ethno;
+ uint16_t host_port;
+ uint16_t local_port;
+ __attribute__ ((aligned (16))) uint8_t buf[256];
+ size_t len; //length of buffer
+ size_t cyc; //idle cycle count
+} udp_uart_state_t;
+
+static udp_uart_state_t _states[MAX_NUM_UARTS];
+
+static int udp_uart_lookup(const uint16_t port)
+{
+ for (size_t i = 0; i < MAX_NUM_UARTS; i++)
+ {
+ if (_states[i].local_port == port) return i;
+ }
+ return -1;
+}
+
+/***********************************************************************
+ * UDP handler for UARTs
+ **********************************************************************/
+static void handle_uart_data_packet(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes
+){
+ //handle ICMP destination unreachable
+ if (buff == NULL)
+ {
+ const size_t which = udp_uart_lookup(src_port);
+ if (which == -1) return;
+ _states[which].host_port = 0;
+ }
+
+ //handle a regular blocking UART write
+ else
+ {
+ const size_t which = udp_uart_lookup(dst_port);
+ if (which == -1) return;
+ _states[which].host_ethno = ethno;
+ _states[which].host_addr = *src;
+ _states[which].host_port = src_port;
+ for (size_t i = 0; i < num_bytes; i++)
+ {
+ const char ch = ((const char *)buff)[i];
+ if (ch == '\n') wb_uart_putc(_states[which].uart_base, (int)'\r');
+ wb_uart_putc(_states[which].uart_base, (int)ch);
+ udp_uart_poll();
+ }
+ }
+}
+
+/***********************************************************************
+ * Public init function
+ **********************************************************************/
+void udp_uart_init(const uint32_t uart_base, const uint16_t udp_port)
+{
+ for (size_t i = 0; i < MAX_NUM_UARTS; i++)
+ {
+ if (_states[i].uart_base != 0) continue;
+ _states[i].uart_base = uart_base;
+ _states[i].local_port = udp_port;
+ _states[i].host_port = 0; //reset to null port
+ _states[i].len = 0;
+ _states[i].cyc = 0;
+ u3_net_stack_register_udp_handler(udp_port, &handle_uart_data_packet);
+ return;
+ }
+}
+
+/***********************************************************************
+ * Public poll function
+ **********************************************************************/
+void udp_uart_poll(void)
+{
+ for (size_t i = 0; i < MAX_NUM_UARTS; i++)
+ {
+ if (_states[i].uart_base == 0) continue;
+
+ bool newline = false;
+ udp_uart_state_t *state = &_states[i];
+
+ //read all characters we can without blocking
+ for (size_t j = state->len; j < sizeof(state->buf); j++)
+ {
+ int ret = wb_uart_getc(state->uart_base);
+ if (ret == -1) break;
+ char ch = (char) ret;
+ if (ch == '\n' || ch == '\r') newline = true;
+ state->buf[j] = ch;
+ state->len++;
+ state->cyc = 0; //reset idle cycles
+ }
+
+ //nothing in buffer, continue to next uart
+ if (state->len == 0) continue;
+
+ //send out a message if newline or forced flush
+ if (newline || state->cyc++ > num_idle_cyc_b4_flush)
+ {
+ if (state->host_port != 0) u3_net_stack_send_udp_pkt(
+ state->host_ethno,
+ &state->host_addr,
+ state->local_port,
+ state->host_port,
+ state->buf, state->len
+ );
+ state->len = 0;
+ state->cyc = 0;
+ }
+ }
+}
diff --git a/firmware/x300/lib/wb_i2c.c b/firmware/x300/lib/wb_i2c.c
new file mode 100644
index 000000000..611908e18
--- /dev/null
+++ b/firmware/x300/lib/wb_i2c.c
@@ -0,0 +1,137 @@
+
+// Copyright 2012 Ettus Research LLC
+/*
+ * Copyright 2007 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// NOTE: this driver left shifts a "7bit" I2C address by one bit and puts a R/W bit as bit 0.
+// Some devices may specify there I2C address assuming this R/W bit is already in place.
+
+#include <wb_i2c.h>
+
+typedef struct {
+ volatile uint32_t prescaler_lo; // r/w
+ volatile uint32_t prescaler_hi; // r/w
+ volatile uint32_t ctrl; // r/w
+ volatile uint32_t data; // wr = transmit reg; rd = receive reg
+ volatile uint32_t cmd_status; // wr = command reg; rd = status reg
+} i2c_regs_t;
+
+#define i2c_regs ((i2c_regs_t *) base)
+
+#define I2C_CTRL_EN (1 << 7) // core enable
+#define I2C_CTRL_IE (1 << 6) // interrupt enable
+
+//
+// STA, STO, RD, WR, and IACK bits are cleared automatically
+//
+#define I2C_CMD_START (1 << 7) // generate (repeated) start condition
+#define I2C_CMD_STOP (1 << 6) // generate stop condition
+#define I2C_CMD_RD (1 << 5) // read from slave
+#define I2C_CMD_WR (1 << 4) // write to slave
+#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1)
+#define I2C_CMD_RSVD_2 (1 << 2) // reserved
+#define I2C_CMD_RSVD_1 (1 << 1) // reserved
+#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt
+
+#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK)
+#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected
+#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration
+#define I2C_ST_RSVD_4 (1 << 4) // reserved
+#define I2C_ST_RSVD_3 (1 << 3) // reserved
+#define I2C_ST_RSVD_2 (1 << 2) // reserved
+#define I2C_ST_TIP (1 << 1) // Transfer-in-progress
+#define I2C_ST_IP (1 << 0) // Interrupt pending
+
+void wb_i2c_init(const uint32_t base, const size_t clk_rate)
+{
+ // prescaler divisor values for 100 kHz I2C [uses 5 * SCLK internally]
+ const uint16_t prescaler = (clk_rate/(5 * 400000)) - 1;
+ i2c_regs->prescaler_lo = prescaler & 0xff;
+ i2c_regs->prescaler_hi = (prescaler >> 8) & 0xff;
+
+ i2c_regs->ctrl = I2C_CTRL_EN; //| I2C_CTRL_IE; // enable core
+}
+
+static inline void
+wait_for_xfer(const uint32_t base)
+{
+ while (i2c_regs->cmd_status & I2C_ST_TIP) // wait for xfer to complete
+ ;
+}
+
+static inline bool
+wait_chk_ack(const uint32_t base)
+{
+ wait_for_xfer(base);
+
+ if ((i2c_regs->cmd_status & I2C_ST_RXACK) != 0){ // target NAK'd
+ return false;
+ }
+ return true;
+}
+
+bool wb_i2c_read(const uint32_t base, const uint8_t i2c_addr, uint8_t *buf, size_t len)
+{
+ if (len == 0) // reading zero bytes always works
+ return true;
+
+ while (i2c_regs->cmd_status & I2C_ST_BUSY)
+ ;
+
+ i2c_regs->data = (i2c_addr << 1) | 1; // 7 bit address and read bit (1)
+ // generate START and write addr
+ i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START;
+ if (!wait_chk_ack(base))
+ goto fail;
+
+ for (; len > 0; buf++, len--){
+ i2c_regs->cmd_status = I2C_CMD_RD | (len == 1 ? (I2C_CMD_NACK | I2C_CMD_STOP) : 0);
+ wait_for_xfer(base);
+ *buf = i2c_regs->data;
+ }
+ return true;
+
+ fail:
+ i2c_regs->cmd_status = I2C_CMD_STOP; // generate STOP
+ return false;
+}
+
+bool wb_i2c_write(const uint32_t base, const uint8_t i2c_addr, const uint8_t *buf, size_t len)
+{
+ while (i2c_regs->cmd_status & I2C_ST_BUSY)
+ ;
+
+ i2c_regs->data = (i2c_addr << 1) | 0; // 7 bit address and write bit (0)
+
+ // generate START and write addr (and maybe STOP)
+ i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START | (len == 0 ? I2C_CMD_STOP : 0);
+ if (!wait_chk_ack(base))
+ goto fail;
+
+ for (; len > 0; buf++, len--){
+ i2c_regs->data = *buf;
+ i2c_regs->cmd_status = I2C_CMD_WR | (len == 1 ? I2C_CMD_STOP : 0);
+ if (!wait_chk_ack(base))
+ goto fail;
+ }
+ return true;
+
+ fail:
+ i2c_regs->cmd_status = I2C_CMD_STOP; // generate STOP
+ return false;
+}
+
diff --git a/firmware/x300/lib/wb_pkt_iface64.c b/firmware/x300/lib/wb_pkt_iface64.c
new file mode 100644
index 000000000..3c0774bdd
--- /dev/null
+++ b/firmware/x300/lib/wb_pkt_iface64.c
@@ -0,0 +1,87 @@
+// Copyright 2012 Ettus Research LLC
+
+#include <wb_pkt_iface64.h>
+#include <wb_utils.h>
+#include <printf.h>
+
+#define NUM_BYTES_MASK 0x1fff
+
+static uint32_t get_status(wb_pkt_iface64_config_t *config)
+{
+ return wb_peek32(config->config_addr);
+}
+
+static void set_control(wb_pkt_iface64_config_t *config)
+{
+ wb_poke32(config->config_addr, config->ctrl);
+}
+
+wb_pkt_iface64_config_t wb_pkt_iface64_init(const uint32_t base, const size_t ctrl_offset)
+{
+ wb_pkt_iface64_config_t config;
+ config.base = base;
+ config.ctrl = 0;
+ config.config_addr = base + ctrl_offset;
+ set_control(&config);
+ wb_pkt_iface64_rx_release(&config); //always release, in case left in a filled state
+ return config;
+}
+
+const void *wb_pkt_iface64_rx_try_claim(wb_pkt_iface64_config_t *config, size_t *num_bytes)
+{
+ const uint32_t status = get_status(config);
+ const uint32_t rx_state_flag = (status >> 31) & 0x1;
+ *num_bytes = (status & NUM_BYTES_MASK);
+ //if (*num_bytes & 0x7) *num_bytes -= 8; //adjust for tuser
+ if (rx_state_flag == 0) return NULL;
+ return (void *)config->base;
+}
+
+void wb_pkt_iface64_rx_release(wb_pkt_iface64_config_t *config)
+{
+ config->ctrl |= 1ul << 31; //does a release
+ set_control(config);
+ while (true)
+ {
+ const uint32_t status = get_status(config);
+ const uint32_t rx_state_flag = (status >> 31) & 0x1;
+ if (rx_state_flag == 0)
+ {
+ config->ctrl &= ~(1ul << 31); //allows for next claim
+ set_control(config);
+ return;
+ }
+ }
+}
+
+void *wb_pkt_iface64_tx_claim(wb_pkt_iface64_config_t *config)
+{
+ while (true)
+ {
+ const uint32_t status = get_status(config);
+ const uint32_t tx_state_flag = (status >> 30) & 0x1;
+ if (tx_state_flag == 1) break;
+ }
+ return (void *)config->base;
+}
+
+void wb_pkt_iface64_tx_submit(wb_pkt_iface64_config_t *config, size_t num_bytes)
+{
+ config->ctrl |= (1ul << 30); //allows for next claim
+ config->ctrl &= ~(NUM_BYTES_MASK); //clear num bytes
+ if (num_bytes & 0x7) num_bytes += 8; //adjust for tuser
+ config->ctrl |= num_bytes & NUM_BYTES_MASK; //set num bytes
+ set_control(config);
+
+ //wait for state machine to transition
+ while (true)
+ {
+ const uint32_t status = get_status(config);
+ const uint32_t tx_state_flag = (status >> 30) & 0x1;
+ if (tx_state_flag == 0) break;
+ }
+
+ config->ctrl &= ~(1ul << 30); //release
+ set_control(config);
+
+}
diff --git a/firmware/x300/lib/wb_uart.c b/firmware/x300/lib/wb_uart.c
new file mode 100644
index 000000000..368d0e150
--- /dev/null
+++ b/firmware/x300/lib/wb_uart.c
@@ -0,0 +1,34 @@
+// Copyright 2012 Ettus Research LLC
+
+#include <wb_uart.h>
+#include <wb_utils.h>
+
+localparam SUART_CLKDIV = 0;
+localparam SUART_TXLEVEL = 1;
+localparam SUART_RXLEVEL = 2;
+localparam SUART_TXCHAR = 3;
+localparam SUART_RXCHAR = 4;
+
+void wb_uart_init(const uint32_t base, const size_t div)
+{
+ wb_poke32(base + SUART_CLKDIV*4, div);
+}
+
+void wb_uart_putc(const uint32_t base, const int ch)
+{
+ while (wb_peek32(base + SUART_TXLEVEL*4) == 0);
+ wb_poke32(base + SUART_TXCHAR*4, ch);
+}
+
+bool wb_uart_try_putc(const uint32_t base, const int ch)
+{
+ if (wb_peek32(base + SUART_TXLEVEL*4) == 0) return false;
+ wb_poke32(base + SUART_TXCHAR*4, ch);
+ return true;
+}
+
+int wb_uart_getc(const uint32_t base)
+{
+ if (wb_peek32(base + SUART_RXLEVEL*4) == 0) return -1;
+ return wb_peek32(base + SUART_RXCHAR*4);
+}