summaryrefslogtreecommitdiffstats
path: root/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic
diff options
context:
space:
mode:
Diffstat (limited to 'meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic')
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile13
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README10
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh103
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h73
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c245
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c254
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h31
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c717
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h161
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c225
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h32
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c192
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c487
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h44
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h143
15 files changed, 2730 insertions, 0 deletions
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile
new file mode 100644
index 0000000..0c9f49f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile
@@ -0,0 +1,13 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: oob-nic i2craw
+
+oob-nic: main.o nic.o intf.o ll_map.o libnetlink.o
+ $(CC) -o $@ $^ $(LDFLAGS) -lwedge_eeprom
+
+i2craw: i2craw.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o oob-nic i2craw
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README
new file mode 100644
index 0000000..f46971f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README
@@ -0,0 +1,10 @@
+
+TODO:
+
+1. Currently, we poll the SMbus instead of rely on the ALERT. The kernel does not handle the ALERT either other than just a print out.
+
+2. Maximum fragment is 32
+
+3. We use libnetlink for bring interface up and setting MAC. That increases the binary by about 50k and also we copied about 5 files from iproute2 here for that. We might be able to get away this by using some non-iproute2 API
+
+4. The dependency in the Makefile does not consider .h
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh
new file mode 100644
index 0000000..35e1a2a
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh
@@ -0,0 +1,103 @@
+#! /bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file 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; version 2 of the License.
+#
+# 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 in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: oob-nic
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop: 0 6
+# Short-Description: One of the first scripts to be executed. Starts or stops
+# the OOB NIC.
+#
+### END INIT INFO
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/oob-nic
+NAME=oob-nic
+DESC="OOB NIC Driver"
+
+# source function library
+. /etc/init.d/functions
+
+test -f $DAEMON || exit 0
+
+# enable the isolation buffer
+. /usr/local/fbpackages/utils/ast-functions
+wedge_iso_buf_enable
+
+fix_etc_interfaces() {
+ local intf_conf rev
+ intf_conf="/etc/network/interfaces"
+ rev=$(wedge_board_rev)
+ if [ $rev -lt 3 ]; then
+ if ! grep oob $intf_conf > /dev/null 2>&1; then
+ echo >> $intf_conf
+ echo "auto oob" >> $intf_conf
+ echo "iface oob inet dhcp" >> $intf_conf
+ fi
+ fi
+}
+
+STOPPER=
+ACTION="$1"
+
+case "$ACTION" in
+ start)
+ echo -n "Starting $DESC: "
+ if [ ! -d /dev/net ]
+ then
+ mkdir /dev/net
+ fi
+ if [ ! -f /dev/net/tun ]
+ then
+ mknod /dev/net/tun c 10 200
+ chmod 666 /dev/net/tun
+ fi
+ fix_etc_interfaces
+ $DAEMON > /dev/null 2>&1 &
+ echo "$NAME."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ start-stop-daemon --stop --quiet --exec $DAEMON
+ echo "$NAME."
+ ;;
+ restart|force-reload)
+ echo -n "Restarting $DESC: "
+ start-stop-daemon --stop --quiet --exec $DAEMON
+ sleep 1
+ fix_etc_interfaces
+ $DAEMON > /dev/null 2>&1 &
+ echo "$NAME."
+ ;;
+ status)
+ status $DAEMON
+ exit $?
+ ;;
+ *)
+ N=${0##*/}
+ N=${N#[SK]??}
+ echo "Usage: $N {start|stop|status|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h
new file mode 100644
index 0000000..5e89765
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h
@@ -0,0 +1,73 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __HLIST_H__
+#define __HLIST_H__ 1
+/* Hash list stuff from kernel */
+
+#include <stddef.h>
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos ; pos = pos->next)
+
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+#define hlist_entry_safe(ptr, type, member) \
+ ({ typeof(ptr) ____ptr = (ptr); \
+ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+ })
+
+#define hlist_for_each_entry(pos, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+#endif /* __HLIST_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c
new file mode 100644
index 0000000..f9d499b
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "facebook/i2c-dev.h"
+#include "facebook/log.h"
+
+void usage(const char *prog) {
+ printf("Usage: %s [options] <bus number> <slave address>\n", prog);
+ printf("\n Options:\n"
+ "\n\t-w 'bytes to write':\n"
+ "\t\t i2c write\n"
+ "\n\t-r <number of bytes to read>:\n"
+ "\t\t if 0 is provided, the first byte of read is used to determine\n"
+ "\t\t how many bytes more to read\n"
+ "\n\t-p:\n"
+ "\t\t Use PEC\n"
+ "\n\t-h:\n"
+ "\t\t Print this help\n"
+ "\n Note: if both '-w' and '-r' are specified, write will be"
+ "\n performed first, followed by read\n");
+}
+
+#define MAX_BYTES 255
+
+int g_use_pec = 0;
+int g_has_write = 0;
+int g_n_write = 0;
+uint8_t g_write_bytes[MAX_BYTES];
+int g_has_read = 0;
+int g_n_read = -1;
+uint8_t g_read_bytes[MAX_BYTES];
+uint8_t g_bus = -1;
+uint8_t g_slave_addr = 0xff;
+
+static int parse_byte_string(const char *str) {
+ const char *startptr = str;
+ char *endptr;
+ int total = 0;
+ unsigned long val;
+
+ do {
+ val = strtoul(startptr, &endptr, 0);
+ if (startptr == endptr) {
+ printf("'%s' is invalid\n", str);
+ return -1;
+ }
+ if (val > MAX_BYTES) {
+ printf("'%s' is invalid\n", str);
+ return -1;
+ }
+ g_write_bytes[total++] = val;
+ if (*endptr == '\0') {
+ break;
+ }
+ if (total >= MAX_BYTES) {
+ printf("'%s' is invalid\n", str);
+ return -1;
+ }
+ startptr = endptr;
+ } while(1);
+
+ return total;
+}
+
+static int i2c_open() {
+ int fd;
+ char fn[32];
+ int rc;
+
+ snprintf(fn, sizeof(fn), "/dev/i2c-%d", g_bus);
+ fd = open(fn, O_RDWR);
+ if (fd == -1) {
+ LOG_ERR(errno, "Failed to open i2c device %s", fn);
+ return -1;
+ }
+
+ rc = ioctl(fd, I2C_SLAVE, g_slave_addr);
+ if (rc < 0) {
+ LOG_ERR(errno, "Failed to open slave @ address 0x%x", g_slave_addr);
+ close(fd);
+ }
+
+ return fd;
+}
+
+static int i2c_io(int fd) {
+ struct i2c_rdwr_ioctl_data data;
+ struct i2c_msg msg[2];
+ int n_msg = 0;
+ int rc;
+
+ memset(&msg, 0, sizeof(msg));
+
+ if (g_has_write) {
+ msg[n_msg].addr = g_slave_addr;
+ msg[n_msg].flags = (g_use_pec) ? I2C_CLIENT_PEC : 0;
+ msg[n_msg].len = g_n_write;
+ msg[n_msg].buf = g_write_bytes;
+ n_msg++;
+ }
+
+ if (g_has_read) {
+ msg[n_msg].addr = g_slave_addr;
+ msg[n_msg].flags = I2C_M_RD
+ | ((g_use_pec) ? I2C_CLIENT_PEC : 0)
+ | ((g_n_read == 0) ? I2C_M_RECV_LEN : 0);
+ /*
+ * In case of g_n_read is 0, block length will be added by
+ * the underlying bus driver.
+ */
+ msg[n_msg].len = (g_n_read) ? g_n_read : 256;
+ msg[n_msg].buf = g_read_bytes;
+ if (g_n_read == 0) {
+ /* If we're using variable length block reads, we have to set the
+ * first byte of the buffer to at least one or the kernel complains.
+ */
+ g_read_bytes[0] = 1;
+ }
+ n_msg++;
+ }
+
+ data.msgs = msg;
+ data.nmsgs = n_msg;
+
+ rc = ioctl(fd, I2C_RDWR, &data);
+ if (rc < 0) {
+ LOG_ERR(errno, "Failed to do raw io");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char * const argv[]) {
+ int i;
+ int fd;
+ int opt;
+ while ((opt = getopt(argc, argv, "hpw:r:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ case 'p':
+ g_use_pec = 1;
+ break;
+ case 'w':
+ g_has_write = 1;
+ if ((g_n_write = parse_byte_string(optarg)) <= 0) {
+ usage(argv[0]);
+ return -1;
+ }
+ break;
+ case 'r':
+ g_has_read = 1;
+ g_n_read = atoi(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ return -1;
+ }
+ }
+
+ /* make sure we still have arguments for bus and slave address */
+ if (optind + 2 != argc) {
+ printf("Bus or slave address is missing\n");
+ usage(argv[0]);
+ return -1;
+ }
+
+ g_bus = atoi(argv[optind]);
+ g_slave_addr = strtoul(argv[optind + 1], NULL, 0);
+ if ((g_slave_addr & 0x80)) {
+ printf("Slave address must be 7-bit\n");
+ return -1;
+ }
+
+ if (!g_has_write && !g_has_read) {
+ /* by default, read, first byte read is the length */
+ g_has_read = 1;
+ g_n_read = 0;
+ }
+
+ printf("Bus: %d\nDevice address: 0x%x\n", g_bus, g_slave_addr);
+ if (g_has_write) {
+ printf("To write %d bytes:", g_n_write);
+ for (i = 0; i < g_n_write; i++) {
+ printf(" 0x%x", g_write_bytes[i]);
+ }
+ printf("\n");
+ }
+ if (g_has_read) {
+ if (g_n_read) {
+ printf("To read %d bytes.\n", g_n_read);
+ } else {
+ printf("To read data.\n");
+ }
+ }
+
+ fd = i2c_open();
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (i2c_io(fd) < 0) {
+ return -1;
+ }
+
+ if (g_has_read) {
+ printf("Received:\n ");
+ if (g_n_read == 0) {
+ g_n_read = g_read_bytes[0] + 1;
+ }
+ for (i = 0; i < g_n_read; i++) {
+ printf(" 0x%x", g_read_bytes[i]);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c
new file mode 100644
index 0000000..5bf8480
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "intf.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/fib_rules.h>
+
+#include "facebook/log.h"
+#include "libnetlink.h"
+#include "ll_map.h"
+
+struct oob_intf_t {
+ char oi_name[32];
+ int oi_fd;
+ int oi_ifidx;
+ uint8_t oi_mac[6];
+ struct rtnl_handle oi_rth;
+};
+
+#define TUN_DEVICE "/dev/net/tun"
+
+static int oob_intf_set_mac(oob_intf *intf, const uint8_t mac[6]) {
+ int rc;
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg ifi;
+ char buf[256];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_type = RTM_NEWLINK;
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.ifi.ifi_family = AF_UNSPEC;
+ req.ifi.ifi_index = intf->oi_ifidx;
+ memcpy(intf->oi_mac, mac, sizeof(intf->oi_mac));
+ addattr_l(&req.n, sizeof(req), IFLA_ADDRESS,
+ intf->oi_mac, sizeof(intf->oi_mac));
+ rc = rtnl_talk(&intf->oi_rth, &req.n, 0, 0, NULL);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set mac to interface %s @ index %d",
+ intf->oi_name, intf->oi_ifidx);
+ return -rc;
+ }
+
+ LOG_INFO("Set interface %s @ index %d mac to %x:%x:%x:%x:%x:%x",
+ intf->oi_name, intf->oi_ifidx,
+ intf->oi_mac[0], intf->oi_mac[1], intf->oi_mac[2],
+ intf->oi_mac[3], intf->oi_mac[4], intf->oi_mac[5]);
+
+ return 0;
+}
+
+static int oob_intf_bring_up(oob_intf *intf) {
+ int rc;
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg ifi;
+ char buf[256];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_type = RTM_NEWLINK;
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.ifi.ifi_family = AF_UNSPEC;
+ req.ifi.ifi_change |= IFF_UP;
+ req.ifi.ifi_flags |= IFF_UP;
+ req.ifi.ifi_index = intf->oi_ifidx;
+ rc = rtnl_talk(&intf->oi_rth, &req.n, 0, 0, NULL);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to bring up interface %s @ index %d",
+ intf->oi_name, intf->oi_ifidx);
+ return -rc;
+ }
+
+ LOG_INFO("Brought up interface %s @ index %d", intf->oi_name, intf->oi_ifidx);
+
+ return 0;
+}
+
+oob_intf* oob_intf_create(const char *name, const uint8_t mac[6]) {
+
+ int rc;
+ int flags;
+ struct ifreq ifr;
+ oob_intf *intf = NULL;
+
+#define _CHECK_RC(fmt, ...) do { \
+ if (rc < 0) { \
+ rc = errno; \
+ LOG_ERR(rc, fmt, ##__VA_ARGS__); \
+ goto err_out; \
+ } \
+} while(0)
+
+ intf = malloc(sizeof(*intf));
+ if (!intf) {
+ rc = ENOMEM;
+ LOG_ERR(rc, "Failed to allocate memory for interface");
+ goto err_out;
+ }
+ memset(intf, 0, sizeof(*intf));
+ strncpy(intf->oi_name, name, sizeof(intf->oi_name));
+ intf->oi_name[sizeof(intf->oi_name) - 1] = '\0';
+ intf->oi_fd = -1;
+
+ rc = rtnl_open(&intf->oi_rth, 0);
+ _CHECK_RC("Failed to open rth_handler");
+
+ rc = open(TUN_DEVICE, O_RDWR);
+ _CHECK_RC("Failed to open %s", TUN_DEVICE);
+ intf->oi_fd = rc;
+
+ memset(&ifr, 0, sizeof(ifr));
+ /*
+ * IFF_TAP: TAP interface
+ * IFF_NO_PI: Do not provide pracket information
+ */
+ ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
+
+ rc = ioctl(intf->oi_fd, TUNSETIFF, (void *) &ifr);
+ _CHECK_RC("Failed to create tap interface %s", ifr.ifr_name);
+
+ /* make fd non-blocking */
+ rc = fcntl(intf->oi_fd, F_GETFL);
+ _CHECK_RC("Failed to get flags from fd ", intf->oi_fd);
+ flags = rc | O_NONBLOCK;
+ rc = fcntl(intf->oi_fd, F_SETFL, rc);
+ _CHECK_RC("Failed to set non-blocking flags ", flags,
+ " to fd ", intf->oi_fd);
+
+ /* set CLOEXEC */
+ rc = fcntl(intf->oi_fd, F_GETFD);
+ _CHECK_RC("Failed to get flags from fd ", intf->oi_fd);
+ flags = rc | FD_CLOEXEC;
+ rc = fcntl(intf->oi_fd, F_SETFD, flags);
+ _CHECK_RC("Failed to set close-on-exec flags ", flags,
+ " to fd ", intf->oi_fd);
+
+ // TODO: if needed, we can adjust send buffer size, TUNSETSNDBUF
+ intf->oi_ifidx = ll_name_to_index(intf->oi_name);
+
+ /* now set the mac address */
+ oob_intf_set_mac(intf, mac);
+
+#if 0
+ /* make it persistent */
+ rc = ioctl(intf->oi_fd, TUNSETPERSIST, 0);
+ _CHECK_RC("Failed to make the tap interface %s persistent", intf->oi_name);
+#endif
+
+ LOG_INFO("Create/attach to tap interface %s @ fd %d, index %d",
+ intf->oi_name, intf->oi_fd, intf->oi_ifidx);
+
+ //oob_intf_bring_up(intf);
+
+ return intf;
+
+ err_out:
+ if (intf) {
+ rtnl_close(&intf->oi_rth);
+ if (intf->oi_fd != -1) {
+ close(intf->oi_fd);
+ }
+ free(intf);
+ }
+
+ return NULL;
+}
+
+int oob_intf_get_fd(const oob_intf *intf) {
+ return intf->oi_fd;
+}
+
+int oob_intf_receive(const oob_intf *intf, char *buf, int len) {
+ int rc;
+ do {
+ rc = read(intf->oi_fd, buf, len);
+ } while (rc == -1 && errno == EINTR);
+ if (rc < 0) {
+ rc = errno;
+ if (rc != EAGAIN) {
+ LOG_ERR(rc, "Failed to read on interface fd %d", intf->oi_fd);
+ return -rc;
+ } else {
+ /* nothing is available */
+ return 0;
+ }
+ } else if (rc == 0) {
+ // Nothing to read. It shall not happen as the fd is non-blocking.
+ // Just add this case to be safe.
+ return 0;
+ } else if (rc > len) {
+ // The pkt is larger than the buffer. We don't have complete packet.
+ // It shall not happen unless the MTU is mis-match. Drop the packet.
+ LOG_ERR(ENOSPC, "Received a too large packet (%d bytes > %d) from the "
+ "tap interface. Drop it...", rc, len);
+ return -ENOSPC;
+ } else {
+ LOG_VER("Recv a packet of %d bytes from %s", rc, intf->oi_name);
+ return rc;
+ }
+}
+
+int oob_intf_send(const oob_intf *intf, const char *buf, int len) {
+ int rc;
+ do {
+ rc = write(intf->oi_fd, buf, len);
+ } while (rc == -1 && errno == EINTR);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to send on interface fd %d", intf->oi_fd);
+ return -rc;
+ } else if (rc < len) {
+ LOG_ERR(EIO, "Failed to send the full packet (%d bytes > %d) for fd %d",
+ len, rc, intf->oi_fd);
+ return -EIO;
+ } else {
+ LOG_VER("Sent a packet of %d bytes to %s", rc, intf->oi_name);
+ return rc;
+ }
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h
new file mode 100644
index 0000000..6ea7af1
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef INTF_H
+#define INTF_H
+
+#include <stdint.h>
+
+typedef struct oob_intf_t oob_intf;
+
+oob_intf* oob_intf_create(const char *name, const uint8_t mac[6]);
+int oob_intf_get_fd(const oob_intf *intf);
+
+int oob_intf_receive(const oob_intf *intf, char *buf, int len);
+int oob_intf_send(const oob_intf *intf, const char *buf, int len);
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c
new file mode 100644
index 0000000..019e2c8
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c
@@ -0,0 +1,717 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+int rcvbuf = 1024 * 1024;
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+
+ memset(rth, 0, sizeof(*rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
+ if (rth->fd < 0) {
+ perror("Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+ perror("SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ perror("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ perror("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ fprintf(stderr, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ return rtnl_wilddump_req_filter(rth, family, type, RTEXT_FILTER_VF);
+}
+
+int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type,
+ __u32 filt_mask)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct ifinfomsg ifm;
+ /* attribute has to be NLMSG aligned */
+ struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO)));
+ __u32 ext_filter_mask;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.ifm.ifi_family = family;
+
+ req.ext_req.rta_type = IFLA_EXT_MASK;
+ req.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));
+ req.ext_filter_mask = filt_mask;
+
+ return send(rth->fd, (void*)&req, sizeof(req), 0);
+}
+
+int rtnl_send(struct rtnl_handle *rth, const void *buf, int len)
+{
+ return send(rth->fd, buf, len, 0);
+}
+
+int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int len)
+{
+ struct nlmsghdr *h;
+ int status;
+ char resp[1024];
+
+ status = send(rth->fd, buf, len, 0);
+ if (status < 0)
+ return status;
+
+ /* Check for immediate errors */
+ status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
+ if (status < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ }
+
+ for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status);
+ h = NLMSG_NEXT(h, status)) {
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+ fprintf(stderr, "ERROR truncated\n");
+ else
+ errno = -err->error;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+ { .iov_base = req, .iov_len = len }
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+ int dump_intr = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ const struct rtnl_dump_filter_arg *a;
+ int found_done = 0;
+ int msglen = 0;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ for (a = arg; a->filter; a++) {
+ struct nlmsghdr *h = (struct nlmsghdr*)buf;
+ msglen = status;
+
+ while (NLMSG_OK(h, msglen)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump)
+ goto skip_it;
+
+ if (h->nlmsg_flags & NLM_F_DUMP_INTR)
+ dump_intr = 1;
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ found_done = 1;
+ break; /* process next filter */
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr,
+ "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = a->filter(&nladdr, h, a->arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, msglen);
+ }
+ }
+
+ if (found_done) {
+ if (dump_intr)
+ fprintf(stderr,
+ "Dump was interrupted and may be inconsistent.\n");
+ return 0;
+ }
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (msglen) {
+ fprintf(stderr, "!!!Remnant of size %d\n", msglen);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1)
+{
+ const struct rtnl_dump_filter_arg a[2] = {
+ { .filter = filter, .arg1 = arg1, },
+ { .filter = NULL, .arg1 = NULL, },
+ };
+
+ return rtnl_dump_filter_l(rth, a);
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void*) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ perror("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l < 0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ if (nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < sizeof(struct nlmsgerr)) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ if (!err->error) {
+ if (answer)
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "RTNETLINK answers: %s\n", strerror(-err->error));
+ errno = -err->error;
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ if (errno == ENOBUFS)
+ continue;
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ l = len - sizeof(*h);
+
+ if (l<0 || len>sizeof(buf)) {
+ fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ fprintf(stderr, "rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr(struct nlmsghdr *n, int maxlen, int type)
+{
+ return addattr_l(n, maxlen, type, NULL, 0);
+}
+
+int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u8));
+}
+
+int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u16));
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u32));
+}
+
+int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u64));
+}
+
+int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *str)
+{
+ return addattr_l(n, maxlen, type, str, strlen(str)+1);
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
+{
+ struct rtattr *nest = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, NULL, 0);
+ return nest;
+}
+
+int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+ nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest;
+ return n->nlmsg_len;
+}
+
+struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
+ const void *data, int len)
+{
+ struct rtattr *start = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, data, len);
+ addattr_nest(n, maxlen, type);
+ return start;
+}
+
+int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
+{
+ struct rtattr *nest = (void *)start + NLMSG_ALIGN(start->rta_len);
+
+ start->rta_len = (void *)NLMSG_TAIL(n) - (void *)start;
+ addattr_nest_end(n, nest);
+ return n->nlmsg_len;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ return parse_rtattr_flags(tb, max, rta, len, 0);
+}
+
+int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len, unsigned short flags)
+{
+ unsigned short type;
+
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ type = rta->rta_type & ~flags;
+ if ((type <= max) && (!tb[type]))
+ tb[type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return i;
+}
+
+int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len)
+{
+ if (RTA_PAYLOAD(rta) < len)
+ return -1;
+ if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
+ rta = RTA_DATA(rta) + RTA_ALIGN(len);
+ return parse_rtattr_nested(tb, max, rta);
+ }
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h
new file mode 100644
index 0000000..9e72692
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h
@@ -0,0 +1,161 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <stdio.h>
+#include <string.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+#include <linux/if_addr.h>
+#include <linux/neighbour.h>
+#include <linux/netconf.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rcvbuf;
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int fam, int type,
+ __u32 filt_mask);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+ struct nlmsghdr *n, void *);
+
+struct rtnl_dump_filter_arg
+{
+ rtnl_filter_t filter;
+ void *arg1;
+};
+
+extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer);
+extern int rtnl_send(struct rtnl_handle *rth, const void *buf, int);
+extern int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int);
+
+extern int addattr(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data);
+extern int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data);
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data);
+extern int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *data);
+
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
+extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
+extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len, unsigned short flags);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+ ({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+ __parse_rtattr_nested_compat(tb, max, rta, len); })
+
+static inline __u8 rta_getattr_u8(const struct rtattr *rta)
+{
+ return *(__u8 *)RTA_DATA(rta);
+}
+static inline __u16 rta_getattr_u16(const struct rtattr *rta)
+{
+ return *(__u16 *)RTA_DATA(rta);
+}
+static inline __u32 rta_getattr_u32(const struct rtattr *rta)
+{
+ return *(__u32 *)RTA_DATA(rta);
+}
+static inline __u64 rta_getattr_u64(const struct rtattr *rta)
+{
+ __u64 tmp;
+ memcpy(&tmp, RTA_DATA(rta), sizeof(__u64));
+ return tmp;
+}
+static inline const char *rta_getattr_str(const struct rtattr *rta)
+{
+ return (const char *)RTA_DATA(rta);
+}
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+ void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+ void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c
new file mode 100644
index 0000000..64e5069
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c
@@ -0,0 +1,225 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * ll_map.c
+ *
+ * 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
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/if.h>
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "hlist.h"
+
+struct ll_cache {
+ struct hlist_node idx_hash;
+ struct hlist_node name_hash;
+ unsigned flags;
+ int index;
+ unsigned short type;
+ char name[IFNAMSIZ];
+};
+
+#define IDXMAP_SIZE 1024
+static struct hlist_head idx_head[IDXMAP_SIZE];
+static struct hlist_head name_head[IDXMAP_SIZE];
+
+static struct ll_cache *ll_get_by_index(unsigned index)
+{
+ struct hlist_node *n;
+ unsigned h = index & (IDXMAP_SIZE - 1);
+
+ hlist_for_each(n, &idx_head[h]) {
+ struct ll_cache *im
+ = container_of(n, struct ll_cache, idx_hash);
+ if (im->index == index)
+ return im;
+ }
+
+ return NULL;
+}
+
+static unsigned namehash(const char *str)
+{
+ unsigned hash = 5381;
+
+ while (*str)
+ hash = ((hash << 5) + hash) + *str++; /* hash * 33 + c */
+
+ return hash;
+}
+
+static struct ll_cache *ll_get_by_name(const char *name)
+{
+ struct hlist_node *n;
+ unsigned h = namehash(name) & (IDXMAP_SIZE - 1);
+
+ hlist_for_each(n, &name_head[h]) {
+ struct ll_cache *im
+ = container_of(n, struct ll_cache, name_hash);
+
+ if (strncmp(im->name, name, IFNAMSIZ) == 0)
+ return im;
+ }
+
+ return NULL;
+}
+
+int ll_remember_index(const struct sockaddr_nl *who,
+ struct nlmsghdr *n, void *arg)
+{
+ unsigned int h;
+ const char *ifname;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct ll_cache *im;
+ struct rtattr *tb[IFLA_MAX+1];
+
+ if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+ return 0;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+ return -1;
+
+ im = ll_get_by_index(ifi->ifi_index);
+ if (n->nlmsg_type == RTM_DELLINK) {
+ if (im) {
+ hlist_del(&im->name_hash);
+ hlist_del(&im->idx_hash);
+ free(im);
+ }
+ return 0;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ ifname = rta_getattr_str(tb[IFLA_IFNAME]);
+ if (ifname == NULL)
+ return 0;
+
+ if (im) {
+ /* change to existing entry */
+ if (strcmp(im->name, ifname) != 0) {
+ hlist_del(&im->name_hash);
+ h = namehash(ifname) & (IDXMAP_SIZE - 1);
+ hlist_add_head(&im->name_hash, &name_head[h]);
+ }
+
+ im->flags = ifi->ifi_flags;
+ return 0;
+ }
+
+ im = malloc(sizeof(*im));
+ if (im == NULL)
+ return 0;
+ im->index = ifi->ifi_index;
+ strcpy(im->name, ifname);
+ im->type = ifi->ifi_type;
+ im->flags = ifi->ifi_flags;
+
+ h = ifi->ifi_index & (IDXMAP_SIZE - 1);
+ hlist_add_head(&im->idx_hash, &idx_head[h]);
+
+ h = namehash(ifname) & (IDXMAP_SIZE - 1);
+ hlist_add_head(&im->name_hash, &name_head[h]);
+
+ return 0;
+}
+
+const char *ll_idx_n2a(unsigned idx, char *buf)
+{
+ const struct ll_cache *im;
+
+ if (idx == 0)
+ return "*";
+
+ im = ll_get_by_index(idx);
+ if (im)
+ return im->name;
+
+ if (if_indextoname(idx, buf) == NULL)
+ snprintf(buf, IFNAMSIZ, "if%d", idx);
+
+ return buf;
+}
+
+const char *ll_index_to_name(unsigned idx)
+{
+ static char nbuf[IFNAMSIZ];
+
+ return ll_idx_n2a(idx, nbuf);
+}
+
+int ll_index_to_type(unsigned idx)
+{
+ const struct ll_cache *im;
+
+ if (idx == 0)
+ return -1;
+
+ im = ll_get_by_index(idx);
+ return im ? im->type : -1;
+}
+
+unsigned ll_index_to_flags(unsigned idx)
+{
+ const struct ll_cache *im;
+
+ if (idx == 0)
+ return 0;
+
+ im = ll_get_by_index(idx);
+ return im ? im->flags : -1;
+}
+
+unsigned ll_name_to_index(const char *name)
+{
+ const struct ll_cache *im;
+ unsigned idx;
+
+ if (name == NULL)
+ return 0;
+
+ im = ll_get_by_name(name);
+ if (im)
+ return im->index;
+
+ idx = if_nametoindex(name);
+ if (idx == 0)
+ sscanf(name, "if%u", &idx);
+ return idx;
+}
+
+void ll_init_map(struct rtnl_handle *rth)
+{
+ static int initialized;
+
+ if (initialized)
+ return;
+
+ if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+ perror("Cannot send dump request");
+ exit(1);
+ }
+
+ if (rtnl_dump_filter(rth, ll_remember_index, NULL) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ exit(1);
+ }
+
+ initialized = 1;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h
new file mode 100644
index 0000000..d74a46f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h
@@ -0,0 +1,32 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LL_MAP_H__
+#define __LL_MAP_H__ 1
+
+extern int ll_remember_index(const struct sockaddr_nl *who,
+ struct nlmsghdr *n, void *arg);
+
+extern void ll_init_map(struct rtnl_handle *rth);
+extern unsigned ll_name_to_index(const char *name);
+extern const char *ll_index_to_name(unsigned idx);
+extern const char *ll_idx_n2a(unsigned idx, char *buf);
+extern int ll_index_to_type(unsigned idx);
+extern unsigned ll_index_to_flags(unsigned idx);
+
+#endif /* __LL_MAP_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c
new file mode 100644
index 0000000..4312402
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include "nic.h"
+#include "intf.h"
+
+#include "facebook/log.h"
+#include "facebook/wedge_eeprom.h"
+
+#define WAIT4PACKET_TIMEOUT 10000 /* 10ms */
+#define NO_RCV_CHECK_THRESHOLD 100 /* if not receiving pkt for 100 times (1s),
+ * check the NIC status
+ */
+
+static void io_loop(oob_nic *nic, oob_intf *intf, const uint8_t mac[6]) {
+
+ fd_set rfds;
+ int fd = oob_intf_get_fd(intf);
+ struct timeval timeout;
+ int rc;
+ int n_fds;
+ int n_io;
+ char buf[NIC_PKT_SIZE_MAX];
+ int no_rcv = 0;
+ struct oob_nic_status_t sts;
+
+ while (1) {
+ memset(&timeout, 0, sizeof(timeout));
+ timeout.tv_sec = 0;
+ timeout.tv_usec = WAIT4PACKET_TIMEOUT;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n_fds = select(fd + 1, &rfds, NULL, NULL, &timeout);
+ if (n_fds < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to select");
+ continue;
+ }
+
+ /*
+ * no matter what, receive packet from nic first, as the nic
+ * has small amount of memory. Without read, the sending could
+ * fail due to OOM.
+ *
+ * TODO: We might want to do something smart here to prevent attack or
+ * just rx flooding. Disable the Receive Enable first, drain the buffer
+ * with oob_nic_receive(), then Tx, and enable Receive Enable after.
+ */
+ for (n_io = 0; n_io < 16; n_io++) {
+ rc = oob_nic_receive(nic, buf, sizeof(buf));
+ if (rc <= 0) {
+ no_rcv++;
+ break;
+ }
+ oob_intf_send(intf, buf, rc);
+ no_rcv = 0;
+ }
+
+ /*
+ * if we didn't receive any packet for NO_RCV_CHECK_THRESHOLD times,
+ * check the nic status
+ */
+ if (no_rcv >= NO_RCV_CHECK_THRESHOLD) {
+ while(oob_nic_get_status(nic, &sts)) {
+ usleep(1000);
+ }
+ LOG_INFO("Failed to receive packets for %d times. NIC status is "
+ "%x.%x", NO_RCV_CHECK_THRESHOLD, sts.ons_byte1, sts.ons_byte2);
+ /*
+ * if the NIC went through initialization, or not set force up, need to
+ * re-program the filters by calling oob_nic_start().
+ */
+ if ((sts.ons_byte1 & NIC_STATUS_D1_INIT)
+ || !(sts.ons_byte1 & NIC_STATUS_D1_FORCE_UP)) {
+ while(oob_nic_start(nic, mac)) {
+ usleep(1000);
+ }
+ }
+ no_rcv = 0;
+ }
+
+ if (n_fds > 0 && FD_ISSET(fd, &rfds)) {
+ for (n_io = 0; n_io < 1; n_io++) {
+ rc = oob_intf_receive(intf, buf, sizeof(buf));
+ if (rc <= 0) {
+ break;
+ }
+ oob_nic_send(nic, buf, rc);
+ }
+ }
+ }
+}
+
+int main(int argc, const char **argv) {
+
+ uint8_t mac[6];
+ oob_nic *nic;
+ oob_intf *intf;
+ struct wedge_eeprom_st eeprom;
+ int rc;
+ int from_eeprom = 0;
+
+ nic = oob_nic_open(0, 0x49);
+ if (!nic) {
+ return -1;
+ }
+
+ /* read EEPROM for the MAC */
+ if (wedge_eeprom_parse(NULL, &eeprom) == 0) {
+ if (eeprom.fbw_mac_size <= 0) {
+ LOG_ERR(EFAULT, "Invalid extended MAC size: %d", eeprom.fbw_mac_size);
+ } else {
+ uint16_t carry;
+ int pos;
+ /* use the last MAC address from the extended MAC range */
+ memcpy(mac, eeprom.fbw_mac_base, sizeof(mac));
+ if (eeprom.fbw_mac_size > 128) {
+ LOG_ERR(EFAULT, "Extended MAC size (%d) is too large.",
+ eeprom.fbw_mac_size);
+ carry = 127;
+ } else {
+ /*
+ * hack around bug device which have the same MAC address on
+ * left and right.
+ */
+ if (strncmp(eeprom.fbw_location, "LEFT", FBW_EEPROM_F_LOCATION) == 0) {
+ carry = eeprom.fbw_mac_size - 2;
+ } else {
+ carry = eeprom.fbw_mac_size - 1;
+ }
+ }
+ for (pos = sizeof(mac) - 1; pos >= 0 && carry; pos--) {
+ uint16_t tmp = mac[pos] + carry;
+ mac[pos] = tmp & 0xFF;
+ carry = tmp >> 8;
+ }
+ from_eeprom = 1;
+ }
+ }
+
+ if (!from_eeprom) {
+ while (oob_nic_get_mac(nic, mac)) {
+ usleep(1000);
+ }
+ /*
+ * increase the last byte of the mac by 1 and turn on the
+ * local administered bit to use it as the oob nic mac
+ */
+ mac[0] |= 0x2;
+ mac[5]++;
+ }
+
+ LOG_INFO("Retrieve MAC %x:%x:%x:%x:%x:%x from %s",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ (from_eeprom) ? "EEPROM" : "NIC");
+
+ /* create the tap interface */
+ intf = oob_intf_create("oob", mac);
+ if (!intf) {
+ return -1;
+ }
+
+ while (oob_nic_start(nic, mac)) {
+ usleep(1000);
+ }
+
+ io_loop(nic, intf, mac);
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c
new file mode 100644
index 0000000..a4dc071
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "nic.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "facebook/i2c-dev.h"
+#include "facebook/log.h"
+
+struct oob_nic_t {
+ int on_bus;
+ uint8_t on_addr;
+ int on_file; /* the file descriptor */
+ uint8_t on_mac[6]; /* the mac address assigned to this NIC */
+};
+
+oob_nic* oob_nic_open(int bus, uint8_t addr) {
+ oob_nic *dev = NULL;
+ char fn[32];
+ int rc;
+
+ /* address must be 7 bits maximum */
+ if ((addr & 0x80)) {
+ LOG_ERR(EINVAL, "Address 0x%x has the 8th bit", addr);
+ return NULL;
+ }
+
+ dev = calloc(1, sizeof(*dev));
+ if (!dev) {
+ return NULL;
+ }
+ dev->on_bus = bus;
+ dev->on_addr = addr;
+
+ /* construct the device file name */
+ snprintf(fn, sizeof(fn), "/dev/i2c-%d", bus);
+ dev->on_file = open(fn, O_RDWR);
+ if (dev->on_file == -1) {
+ LOG_ERR(errno, "Failed to open i2c device %s", fn);
+ goto err_out;
+ }
+
+ /* assign the device address */
+ rc = ioctl(dev->on_file, I2C_SLAVE, dev->on_addr);
+ if (rc < 0) {
+ LOG_ERR(errno, "Failed to open slave @ address 0x%x", dev->on_addr);
+ goto err_out;
+ }
+
+ return dev;
+
+ err_out:
+ oob_nic_close(dev);
+ return NULL;
+}
+
+void oob_nic_close(oob_nic *dev) {
+ if (!dev) {
+ return;
+ }
+ if (dev->on_file != -1) {
+ close(dev->on_file);
+ }
+ free(dev);
+}
+
+int oob_nic_get_mac(oob_nic *dev, uint8_t mac[6]) {
+ int rc;
+ uint8_t buf[64];
+
+ rc = i2c_smbus_read_block_data(dev->on_file, NIC_READ_MAC_CMD, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to get MAC on %d-%x",
+ dev->on_bus, dev->on_addr);
+ return -rc;
+ }
+
+ if (rc != NIC_READ_MAC_RES_LEN) {
+ LOG_ERR(EFAULT, "Unexpected response len (%d) for get MAC on %d-%x",
+ rc, dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ if (buf[0] != NIC_READ_MAC_RES_OPT) {
+ LOG_ERR(EFAULT, "Unexpected response opt code (0x%x) get MAC on %d-%x",
+ buf[0], dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ memcpy(mac, &buf[1], 6);
+
+ LOG_DBG("Get MAC on %d-%x: %x:%x:%x:%x:%x:%x", dev->on_bus, dev->on_addr,
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ return 0;
+}
+
+int oob_nic_get_status(oob_nic *dev, oob_nic_status *status) {
+ int rc;
+ uint8_t buf[64];
+
+ rc = i2c_smbus_read_block_data(dev->on_file, NIC_READ_STATUS_CMD, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to get status on %d-%x",
+ dev->on_bus, dev->on_addr);
+ return -rc;
+ }
+
+ if (rc != NIC_READ_STATUS_RES_LEN) {
+ LOG_ERR(EFAULT, "Unexpected response len (%d) for get status on %d-%x",
+ rc, dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ if (buf[0] != NIC_READ_STATUS_RES_OPT) {
+ LOG_ERR(EFAULT, "Unexpected response opt code (0x%x) get status on %d-%x",
+ buf[0], dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ memset(status, 0, sizeof(*status));
+ status->ons_byte1 = buf[1];
+ status->ons_byte2 = buf[2];
+
+ LOG_VER("Get status on %d-%x: byte1:0x%x byte2:0x%x",
+ dev->on_bus, dev->on_addr,
+ status->ons_byte1, status->ons_byte2);
+ return 0;
+}
+
+int oob_nic_receive(oob_nic *dev, uint8_t *buf, int len) {
+
+ int rc = 0;
+ uint8_t pkt[I2C_SMBUS_BLOCK_LARGE_MAX];
+ uint8_t opt;
+ int copied = 0;
+ int to_copy;
+ int expect_first = 1;
+ int n_frags = 0;
+
+#define _COPY_DATA(n, data) do { \
+ int to_copy; \
+ if (copied >= len) { \
+ break; \
+ } \
+ to_copy = (n < len - copied) ? n : len - copied; \
+ if (to_copy) { \
+ memcpy(buf + copied, data, to_copy); \
+ } \
+ copied += to_copy; \
+} while(0)
+
+ do {
+ rc = i2c_smbus_read_block_large_data(dev->on_file, NIC_READ_PKT_CMD, pkt);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to get packet on %d-%x",
+ dev->on_bus, dev->on_addr);
+ goto err_out;
+ }
+ if (rc > I2C_SMBUS_BLOCK_LARGE_MAX) {
+ LOG_ERR(EFAULT, "Too large i2c block (%d) received on %d-%x",
+ rc, dev->on_bus, dev->on_addr);
+ rc = EFAULT;
+ goto err_out;
+ }
+ opt = pkt[0];
+ switch (opt) {
+ case NIC_READ_PKT_RES_FIRST_OPT:
+ if (!expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received more than one buffer with FIRST set");
+ goto err_out;
+ }
+ expect_first = 0;
+ n_frags++;
+ _COPY_DATA(rc - 1, &pkt[1]);
+ break;
+ case NIC_READ_PKT_RES_MIDDLE_OPT:
+ if (expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received MIDDLE before getting FIRST");
+ goto err_out;
+ }
+ _COPY_DATA(rc - 1, &pkt[1]);
+ n_frags++;
+ break;
+ case NIC_READ_PKT_RES_LAST_OPT:
+ if (expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received LAST before getting FIRST");
+ goto err_out;
+ }
+ if (rc != NIC_READ_PKT_RES_LAST_LEN) {
+ LOG_ERR(EFAULT, "Expect %d bytes (got %d) for LAST segement",
+ NIC_READ_PKT_RES_LAST_LEN, rc);
+ rc = EFAULT;
+ goto err_out;
+ }
+ /* TODO: pkt status???? */
+ break;
+ case NIC_READ_STATUS_RES_OPT:
+ /* that means no pkt available */
+ if (!expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received STATUS in the middle of packet");
+ goto err_out;
+ }
+ //LOG_VER("Received STATUS when receiving the packet");
+ return 0;
+ default:
+ rc = EFAULT;
+ LOG_ERR(rc, "Unexpected opt code 0x%x", opt);
+ goto err_out;
+ }
+ } while (opt != NIC_READ_PKT_RES_LAST_OPT);
+
+ LOG_VER("Received a packet with %d bytes in %d fragments", copied, n_frags);
+ return copied;
+
+ err_out:
+ return -rc;
+#undef _COPY_DATA
+}
+
+int oob_nic_send(oob_nic *dev, const uint8_t *data, int len) {
+
+ int rc;
+ uint8_t to_send;
+ int has_sent = 0;
+ int is_first = 1;
+ uint8_t cmd;
+ int n_frags = 0;
+
+ if (len <= 0 || len > NIC_PKT_SIZE_MAX) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Invalid packet length %d", len);
+ return -rc;
+ }
+
+ while (len) {
+ to_send = (len < OOB_NIC_PKT_FRAGMENT_SIZE)
+ ? len : OOB_NIC_PKT_FRAGMENT_SIZE;
+
+ if (is_first) {
+ if (to_send >= len) {
+ /* this is the last pkt also */
+ cmd = NIC_WRITE_PKT_SINGLE_CMD;
+ } else {
+ cmd = NIC_WRITE_PKT_FIRST_CMD;
+ }
+ is_first = 0;
+ } else {
+ if (to_send >= len) {
+ /* this is the last pkt */
+ cmd = NIC_WRITE_PKT_LAST_CMD;
+ } else {
+ cmd = NIC_WRITE_PKT_MIDDLE_CMD;
+ }
+ }
+
+ rc = i2c_smbus_write_block_large_data(dev->on_file, cmd,
+ to_send, data + has_sent);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to sent packet with cmd 0x%x, has_sent=%d "
+ "to_send=%d", cmd, has_sent, to_send);
+ return -rc;
+ }
+
+ has_sent += to_send;
+ len -= to_send;
+ n_frags++;
+ }
+
+ LOG_VER("Sent a packet with %d bytes in %d fragments", has_sent, n_frags);
+
+ return has_sent;
+}
+
+static int oob_nic_set_mng_ctrl(oob_nic *dev, const uint8_t *data, int len) {
+ int rc;
+
+ if (len <= 0) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Invalid data length: %d", len);
+ return -rc;
+ }
+
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_MNG_CTRL_CMD,
+ len, data);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to send management control command for parameter # %d",
+ data[0]);
+ return -rc;
+ }
+
+ return 0;
+}
+
+static int oob_nic_set_force_up(oob_nic *dev, int enable) {
+ uint8_t cmd[2];
+
+ cmd[0] = NIC_MNG_CTRL_KEEP_LINK_UP_NUM;
+ cmd[1] = enable
+ ? NIC_MNG_CTRL_KEEP_LINK_UP_ENABLE : NIC_MNG_CTRL_KEEP_LINK_UP_DISABLE;
+
+ LOG_DBG("Turn %s link force up", enable ? "on" : "off");
+ return oob_nic_set_mng_ctrl(dev, cmd, sizeof(cmd));
+}
+
+static int oob_nic_setup_filters(oob_nic *dev, const uint8_t mac[6]) {
+ int rc;
+ int i;
+ uint32_t cmd32;
+ uint8_t buf[32];
+ uint8_t *cmd;
+
+ /*
+ * Command to set MAC filter
+ * Seven bytes are required to load the MAC address filters.
+ * Data 2—MAC address filters pair number (3:0).
+ * Data 3—MSB of MAC address.
+ * ...
+ * Data 8: LSB of MAC address.
+ */
+ /* set MAC filter to pair 0 */
+ cmd = buf;
+ *cmd++ = NIC_FILTER_MAC_NUM;
+ *cmd++ = NIC_FILTER_MAC_PAIR0; /* pair 0 */
+ for (i = 0; i < 6; i++) {
+ *cmd++ = mac[i];
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set MAC filter");
+ return -rc;
+ }
+
+ /*
+ * Command to enable filter
+ *
+ * 9 bytes to load the extended decision filters (MDEF_EXT & MDEF)
+ * Data 2—MDEF filter index (valid values are 0...6)
+ * Data 3—MSB of MDEF_EXT (DecisionFilter0)
+ * ....
+ * Data 6—LSB of MDEF_EXT (DecisionFilter0)
+ * Data 7—MSB of MDEF (DecisionFilter0)
+ * ....
+ * Data 10—LSB of MDEF (DecisionFilter0)
+ */
+
+ /* enable MAC filter pair 0 on filter 0 */
+ cmd = buf;
+ *cmd++ = NIC_FILTER_DECISION_EXT_NUM;
+ *cmd++ = NIC_FILTER_MDEF0;
+ /* enable filter for traffic from network and host */
+ cmd32 = NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_NET_EN_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_HOST_EN_OFFSET);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ /* enable mac pair 0 */
+ cmd32 = NIC_FILTER_MDEF_BIT_VAL(NIC_FILTER_MDEF_MAC_AND_OFFSET,
+ NIC_FILTER_MAC_PAIR0);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set MAC filter to MDEF 0");
+ return -rc;
+ }
+ /* enable ARP and ND on filter 1*/
+ cmd = buf;
+ *cmd++ = NIC_FILTER_DECISION_EXT_NUM;
+ *cmd++ = NIC_FILTER_MDEF1;
+ /* enable filter for traffic from network and host */
+ cmd32 = NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_NET_EN_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_HOST_EN_OFFSET);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ /* enable ARP and ND */
+ cmd32 = NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_ARP_REQ_OR_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_ARP_RES_OR_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_NBG_OR_OFFSET);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set ARP and ND filter to MDEF 1");
+ return -rc;
+ }
+
+ /* make filter 0, matching MAC, to be mng only */
+ cmd = buf;
+ *cmd++ = NIC_FILTER_MNG_ONLY_NUM;
+ cmd32 = NIC_FILTER_MNG_ONLY_FILTER0;
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to enabled management only filter");
+ return -rc;
+ }
+
+ return 0;
+}
+
+int oob_nic_start(oob_nic *dev, const uint8_t mac[6]) {
+ int rc;
+ uint8_t cmd;
+
+ /* force the link up, no matter what the status of the main link */
+ rc = oob_nic_set_force_up(dev, 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ oob_nic_setup_filters(dev, mac);
+
+ /* first byte is the control */
+ cmd = NIC_WRITE_RECV_ENABLE_EN
+ | NIC_WRITE_RECV_ENABLE_STA
+ | NIC_WRITE_RECV_ENABLE_NM_UNSUPP /* TODO, to support ALERT */
+ | NIC_WRITE_RECV_ENABLE_RESERVED;
+
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_RECV_ENABLE_CMD,
+ 1, &cmd);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to start receive function");
+ return -rc;
+ }
+ LOG_DBG("Started receive function");
+ return 0;
+}
+
+int oob_nic_stop(oob_nic *dev) {
+ int rc;
+ uint8_t ctrl;
+ /* don't set any enable bits, which turns off the receive func */
+ ctrl = NIC_WRITE_RECV_ENABLE_RESERVED;
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_RECV_ENABLE_CMD,
+ 1, &ctrl);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to stop receive function");
+ return -rc;
+ }
+ LOG_DBG("Stopped receive function");
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h
new file mode 100644
index 0000000..1ac7ff8
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef NIC_H
+#define NIC_H
+
+#include <stdint.h>
+
+#include "nic_defs.h"
+
+typedef struct oob_nic_t oob_nic;
+
+oob_nic* oob_nic_open(int bus, uint8_t addr);
+void oob_nic_close(oob_nic* dev);
+
+/* MAC */
+int oob_nic_get_mac(oob_nic *dev, uint8_t mac[6]);
+
+/* Status */
+typedef struct oob_nic_status_t oob_nic_status;
+int oob_nic_get_status(oob_nic *dev, oob_nic_status *status);
+
+int oob_nic_start(oob_nic *dev, const uint8_t mac[6]);
+int oob_nic_stop(oob_nic *dev);
+
+int oob_nic_send(oob_nic *dev, const uint8_t *data, int len);
+
+int oob_nic_receive(oob_nic *dev, uint8_t *buf, int len);
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h
new file mode 100644
index 0000000..1ae8721
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef NIC_DEFS_H
+#define NIC_DEFS_H
+
+#include <stdint.h>
+
+#define OOB_NIC_PKT_FRAGMENT_SIZE 240
+#define NIC_PKT_SIZE_MAX 1536
+
+/** Get System MAC Address */
+#define NIC_READ_MAC_CMD 0xD4
+#define NIC_READ_MAC_RES_OPT 0xD4
+#define NIC_READ_MAC_RES_LEN 7
+
+/** Read Status */
+#define NIC_READ_STATUS_CMD 0xDE
+#define NIC_READ_STATUS_RES_OPT 0xDD
+#define NIC_READ_STATUS_RES_LEN 3
+
+struct oob_nic_status_t {
+ uint8_t ons_byte1;
+ uint8_t ons_byte2;
+};
+
+#define NIC_STATUS_D1_POWER_DR 0x00
+#define NIC_STATUS_D1_POWER_D0U 0x01
+#define NIC_STATUS_D1_POWER_D0 0x10
+#define NIC_STATUS_D1_POWER_D3 0x11
+#define NIC_STATUS_D1_PORT_MSB (0x1 << 2)
+#define NIC_STATUS_D1_INIT (0x1 << 3)
+#define NIC_STATUS_D1_FORCE_UP (0x1 << 4)
+#define NIC_STATUS_D1_LINK (0x1 << 5)
+#define NIC_STATUS_D1_TCO_CMD_ABORT (0x1 << 6)
+#define NIC_STATUS_D1_PORT_LSB (0x1 << 7)
+
+#define NIC_STATUS_D2_ICR (0x1 << 1)
+#define NIC_STATUS_D2_IPI (0x1 << 2)
+#define NIC_STATUS_D2_DRV_VALID (0x1 << 3)
+
+/** Receive TCO Packet */
+#define NIC_READ_PKT_CMD 0xC0
+#define NIC_READ_PKT_RES_FIRST_OPT 0x90
+#define NIC_READ_PKT_RES_MIDDLE_OPT 0x10
+#define NIC_READ_PKT_RES_LAST_OPT 0x50
+#define NIC_READ_PKT_RES_LAST_LEN 17
+
+/** Transmit Packet */
+#define NIC_WRITE_PKT_SINGLE_CMD 0xC4
+#define NIC_WRITE_PKT_FIRST_CMD 0x84
+#define NIC_WRITE_PKT_MIDDLE_CMD 0x04
+#define NIC_WRITE_PKT_LAST_CMD 0x44
+
+/** Management Control */
+#define NIC_WRITE_MNG_CTRL_CMD 0xC1
+
+#define NIC_MNG_CTRL_KEEP_LINK_UP_NUM 0x00
+#define NIC_MNG_CTRL_KEEP_LINK_UP_ENABLE 0x01
+#define NIC_MNG_CTRL_KEEP_LINK_UP_DISABLE 0x00
+
+/** Update MNG RCV Filter Parameters */
+#define NIC_WRITE_FILTER_CMD 0xCC
+
+#define NIC_FILTER_MAC_NUM 0x66
+#define NIC_FILTER_MAC_PAIR0 0
+#define NIC_FILTER_MAC_PAIR1 1
+#define NIC_FILTER_MAC_PAIR2 2
+#define NIC_FILTER_MAC_PAIR3 3
+
+#define NIC_FILTER_MNG_ONLY_NUM 0xF
+#define NIC_FILTER_MNG_ONLY_FILTER0 (0x1)
+#define NIC_FILTER_MNG_ONLY_FILTER1 (0x1 << 1)
+#define NIC_FILTER_MNG_ONLY_FILTER2 (0x1 << 2)
+#define NIC_FILTER_MNG_ONLY_FILTER3 (0x1 << 3)
+#define NIC_FILTER_MNG_ONLY_FILTER4 (0x1 << 4)
+
+#define NIC_FILTER_DECISION_EXT_NUM 0x68
+#define NIC_FILTER_MDEF0 0 /* index 0 */
+#define NIC_FILTER_MDEF1 1 /* index 1 */
+#define NIC_FILTER_MDEF2 2 /* index 2 */
+#define NIC_FILTER_MDEF3 3 /* index 3 */
+#define NIC_FILTER_MDEF4 4 /* index 4 */
+#define NIC_FILTER_MDEF5 5 /* index 5 */
+#define NIC_FILTER_MDEF6 6 /* index 6 */
+#define NIC_FILTER_MDEF7 7 /* index 7 */
+
+#define NIC_FILTER_MDEF_MAC_AND_OFFSET 0
+#define NIC_FILTER_MDEF_BCAST_AND_OFFSET 4
+#define NIC_FILTER_MDEF_VLAN_AND_OFFSET 5
+#define NIC_FILTER_MDEF_IPV4_AND_OFFSET 13
+#define NIC_FILTER_MDEF_IPV6_AND_OFFSET 17
+#define NIC_FILTER_MDEF_MAC_OR_OFFSET 21
+#define NIC_FILTER_MDEF_BCAST_OR_OFFSET 25
+#define NIC_FILTER_MDEF_MCAST_AND_OFFSET 26
+#define NIC_FILTER_MDEF_ARP_REQ_OR_OFFSET 27
+#define NIC_FILTER_MDEF_ARP_RES_OR_OFFSET 28
+#define NIC_FILTER_MDEF_NBG_OR_OFFSET 29
+#define NIC_FILTER_MDEF_PORT298_OR_OFFSET 30
+#define NIC_FILTER_MDEF_PORT26F_OR_OFFSET 31
+
+#define NIC_FILTER_MDEF_EXT_ETHTYPE_AND_OFFSET 0
+#define NIC_FILTER_MDEF_EXT_ETHTYPE_OR_OFFSET 8
+#define NIC_FILTER_MDEF_EXT_FLEX_PORT_OR_OFFSET 16
+#define NIC_FILTER_MDEF_EXT_FLEX_TCO_OR_OFFSET 24
+#define NIC_FILTER_MDEF_EXT_NCSI_DISABLE_OFFSET 28
+#define NIC_FILTER_MDEF_EXT_FLOW_CONTROL_DISCARD_OFFSET 29
+#define NIC_FILTER_MDEF_EXT_NET_EN_OFFSET 30
+#define NIC_FILTER_MDEF_EXT_HOST_EN_OFFSET 31
+
+#define NIC_FILTER_MDEF_BIT(offset) ((0x1) << (offset))
+#define NIC_FILTER_MDEF_BIT_VAL(offset, val) ((0x1) << ((offset) + (val)))
+
+/** Receive Enable */
+#define NIC_WRITE_RECV_ENABLE_CMD 0xCA
+#define NIC_WRITE_RECV_ENABLE_LEN_MAX 14
+
+#define NIC_WRITE_RECV_ENABLE_EN 0x1
+#define NIC_WRITE_RECV_ENABLE_ALL (0x1 << 1)
+#define NIC_WRITE_RECV_ENABLE_STA (0x1 << 2)
+#define NIC_WRITE_RECV_ENABLE_ARP_RES (0x1 << 3)
+#define NIC_WRITE_RECV_ENABLE_NM_ALERT (0x00 << 4)
+#define NIC_WRITE_RECV_ENABLE_NM_ASYNC (0x01 << 4)
+#define NIC_WRITE_RECV_ENABLE_NM_DIRECT (0x02 << 4)
+#define NIC_WRITE_RECV_ENABLE_NM_UNSUPP (0x03 << 4)
+#define NIC_WRITE_RECV_ENABLE_RESERVED (0x1 << 6)
+#define NIC_WRITE_RECV_ENABLE_CBDM (0x1 << 7)
+
+#endif
OpenPOWER on IntegriCloud