diff options
Diffstat (limited to 'firmware/microblaze/lib')
-rw-r--r-- | firmware/microblaze/lib/Makefile.am | 12 | ||||
-rw-r--r-- | firmware/microblaze/lib/arp_cache.c | 90 | ||||
-rw-r--r-- | firmware/microblaze/lib/arp_cache.h | 33 | ||||
-rw-r--r-- | firmware/microblaze/lib/banal.c | 49 | ||||
-rw-r--r-- | firmware/microblaze/lib/banal.h | 93 | ||||
-rw-r--r-- | firmware/microblaze/lib/eth_mac.h | 2 | ||||
-rw-r--r-- | firmware/microblaze/lib/ethernet.h | 2 | ||||
-rw-r--r-- | firmware/microblaze/lib/ethertype.h | 27 | ||||
-rw-r--r-- | firmware/microblaze/lib/if_arp.h | 153 | ||||
-rw-r--r-- | firmware/microblaze/lib/net_common.c | 435 | ||||
-rw-r--r-- | firmware/microblaze/lib/net_common.h | 60 |
11 files changed, 952 insertions, 4 deletions
diff --git a/firmware/microblaze/lib/Makefile.am b/firmware/microblaze/lib/Makefile.am index 0f5a5298c..02e9dda73 100644 --- a/firmware/microblaze/lib/Makefile.am +++ b/firmware/microblaze/lib/Makefile.am @@ -51,7 +51,10 @@ libu2fw_a_SOURCES = \ printf.c \ sd.c \ spi.c \ - u2_init.c + u2_init.c \ + net_common.c \ + arp_cache.c \ + banal.c noinst_HEADERS = \ ad9510.h \ @@ -84,7 +87,12 @@ noinst_HEADERS = \ stdio.h \ u2_init.h \ usrp2_bytesex.h \ - wb16550.h + wb16550.h \ + net_common.h \ + if_arp.h \ + arp_cache.h \ + banal.h \ + ethertype.h EXTRA_DIST = \ microblaze.ld diff --git a/firmware/microblaze/lib/arp_cache.c b/firmware/microblaze/lib/arp_cache.c new file mode 100644 index 000000000..9c586fa6b --- /dev/null +++ b/firmware/microblaze/lib/arp_cache.c @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * 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 "arp_cache.h" +#include <stddef.h> + +typedef struct { + struct ip_addr ip; + eth_mac_addr_t mac; +} arp_cache_t; + +#define NENTRIES 8 // power-of-2 + +static size_t nentries; +static size_t victim; +static arp_cache_t cache[NENTRIES]; + +void +arp_cache_init(void) +{ + nentries = 0; + victim = 0; +} + +// returns non-negative index if found, else -1 +static int +arp_cache_lookup(const struct ip_addr *ip) +{ + int i; + for (i = 0; i < nentries; i++) + if (cache[i].ip.addr == ip->addr) + return i; + + return -1; +} + +static int +arp_cache_alloc(void) +{ + if (nentries < NENTRIES) + return nentries++; + + int i = victim; + victim = (victim + 1) % NENTRIES; + return i; +} + +void +arp_cache_update(const struct ip_addr *ip, + const eth_mac_addr_t *mac) +{ + int i = arp_cache_lookup(ip); + if (i < 0){ + i = arp_cache_alloc(); + cache[i].ip = *ip; + cache[i].mac = *mac; + } + else { + cache[i].mac = *mac; + } +} + +bool +arp_cache_lookup_mac(const struct ip_addr *ip, + eth_mac_addr_t *mac) +{ + int i = arp_cache_lookup(ip); + if (i < 0) + return false; + + *mac = cache[i].mac; + return true; +} diff --git a/firmware/microblaze/lib/arp_cache.h b/firmware/microblaze/lib/arp_cache.h new file mode 100644 index 000000000..8e84a1f94 --- /dev/null +++ b/firmware/microblaze/lib/arp_cache.h @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * 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/>. + */ +#ifndef INCLUDED_ARP_CACHE_H +#define INCLUDED_ARP_CACHE_H + +#include <lwip/ip_addr.h> +#include <net/eth_mac_addr.h> +#include <stdbool.h> + +void arp_cache_init(void); + +void arp_cache_update(const struct ip_addr *ip, + const eth_mac_addr_t *mac); + +bool arp_cache_lookup_mac(const struct ip_addr *ip, + eth_mac_addr_t *mac); + +#endif /* INCLUDED_ARP_CACHE_H */ diff --git a/firmware/microblaze/lib/banal.c b/firmware/microblaze/lib/banal.c new file mode 100644 index 000000000..23f5f3b8a --- /dev/null +++ b/firmware/microblaze/lib/banal.c @@ -0,0 +1,49 @@ +/* -*- c++ -*- */ +/* + * 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 <banal.h> + +uint32_t +get_uint32(const unsigned char *s) +{ + return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; +} + +uint64_t +get_uint64(const unsigned char *s) +{ + return (((uint64_t)get_uint32(s)) << 32) | get_uint32(s+4); +} + +uint32_t +divide_uint64(uint64_t dividend, uint32_t divisor) +{ + uint32_t result = 0; + uint64_t dividend_ = 0; + for(int i = 31; i >= 0; i--){ + //approximate the divisor with the ith result bit set + uint64_t tmp = dividend_; + tmp += (uint64_t)divisor << i; + //set the ith result bit if the approximation is less + if (tmp <= dividend){ + dividend_ = tmp; + result |= 1 << i; + } + } + return result; +} diff --git a/firmware/microblaze/lib/banal.h b/firmware/microblaze/lib/banal.h new file mode 100644 index 000000000..6d9420602 --- /dev/null +++ b/firmware/microblaze/lib/banal.h @@ -0,0 +1,93 @@ +/* -*- c -*- */ +/* + * 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/>. + */ +#ifndef INCLUDED_BANAL_H +#define INCLUDED_BANAL_H + +#include <stdint.h> +#include <lwip/ip_addr.h> + +/* + * 1's complement sum for IP and UDP headers + * + * init chksum to zero to start. + */ +static inline unsigned int +CHKSUM(unsigned int x, unsigned int *chksum) +{ + *chksum += x; + *chksum = (*chksum & 0xffff) + (*chksum>>16); + *chksum = (*chksum & 0xffff) + (*chksum>>16); + return x; +} + +unsigned int +chksum_buffer(unsigned short *buf, int nshorts, unsigned int initial_chksum); + +//-------------- unsigned get_int 8, 16, 32, 64 --------------// + +static inline uint8_t +get_uint8(const unsigned char *s) +{ + return s[0]; +} + +static inline uint16_t +get_uint16(const unsigned char *s) +{ + return (s[0] << 8) | s[1]; +} + +uint32_t +get_uint32(const unsigned char *s); + +uint64_t +get_uint64(const unsigned char *s); + +//--------------- signed get_int 8, 16, 32, 64 --------------// + +static inline int8_t +get_int8(const unsigned char *s) +{ + return get_uint8(s); +} + +static inline int16_t +get_int16(const unsigned char *s) +{ + return get_uint16(s); +} + +static inline int32_t +get_int32(const unsigned char *s) +{ + return get_uint32(s); +} + +static inline int64_t +get_int64(const unsigned char *s) +{ + return get_uint64(s); +} + +void +print_ip(struct ip_addr ip); + +uint32_t +divide_uint64(uint64_t dividend, uint32_t divisor); + +#endif /* INCLUDED_BANAL_H */ diff --git a/firmware/microblaze/lib/eth_mac.h b/firmware/microblaze/lib/eth_mac.h index 65717f6f8..73feec955 100644 --- a/firmware/microblaze/lib/eth_mac.h +++ b/firmware/microblaze/lib/eth_mac.h @@ -19,7 +19,7 @@ #ifndef INCLUDED_ETH_MAC_H #define INCLUDED_ETH_MAC_H -#include "network.h" +#include <net/eth_mac_addr.h> void eth_mac_init(const eth_mac_addr_t *src); diff --git a/firmware/microblaze/lib/ethernet.h b/firmware/microblaze/lib/ethernet.h index 0e42d9b8d..70b7077c6 100644 --- a/firmware/microblaze/lib/ethernet.h +++ b/firmware/microblaze/lib/ethernet.h @@ -19,7 +19,7 @@ #ifndef INCLUDED_ETHERNET_H #define INCLUDED_ETHERNET_H -#include "network.h" +#include <net/eth_mac_addr.h> #include <stdbool.h> typedef void (*ethernet_link_changed_callback_t)(int speed); diff --git a/firmware/microblaze/lib/ethertype.h b/firmware/microblaze/lib/ethertype.h new file mode 100644 index 000000000..11f4bafec --- /dev/null +++ b/firmware/microblaze/lib/ethertype.h @@ -0,0 +1,27 @@ +/* -*- c++ -*- */ +/* + * 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/>. + */ +#ifndef INCLUDED_ETHERTYPE_H +#define INCLUDED_ETHERTYPE_H + +// all we care about + +#define ETHERTYPE_IPV4 0x0800 +#define ETHERTYPE_ARP 0x0806 + + +#endif /* INCLUDED_ETHERTYPE_H */ diff --git a/firmware/microblaze/lib/if_arp.h b/firmware/microblaze/lib/if_arp.h new file mode 100644 index 000000000..63519c4be --- /dev/null +++ b/firmware/microblaze/lib/if_arp.h @@ -0,0 +1,153 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the ARP (RFC 826) protocol. + * + * Version: @(#)if_arp.h 1.0.1 04/16/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988 + * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source. + * Ross Biro + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Florian La Roche, + * Jonathan Layes <layes@loran.com> + * Arnaldo Carvalho de Melo <acme@conectiva.com.br> ARPHRD_HWX25 + * + * 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. + */ +#ifndef _LINUX_IF_ARP_H +#define _LINUX_IF_ARP_H + +/* ARP protocol HARDWARE identifiers. */ +#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */ +#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ +#define ARPHRD_EETHER 2 /* Experimental Ethernet */ +#define ARPHRD_AX25 3 /* AX.25 Level 2 */ +#define ARPHRD_PRONET 4 /* PROnet token ring */ +#define ARPHRD_CHAOS 5 /* Chaosnet */ +#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */ +#define ARPHRD_ARCNET 7 /* ARCnet */ +#define ARPHRD_APPLETLK 8 /* APPLEtalk */ +#define ARPHRD_DLCI 15 /* Frame Relay DLCI */ +#define ARPHRD_ATM 19 /* ATM */ +#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */ +#define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */ +#define ARPHRD_EUI64 27 /* EUI-64 */ +#define ARPHRD_INFINIBAND 32 /* InfiniBand */ + +/* Dummy types for non ARP hardware */ +#define ARPHRD_SLIP 256 +#define ARPHRD_CSLIP 257 +#define ARPHRD_SLIP6 258 +#define ARPHRD_CSLIP6 259 +#define ARPHRD_RSRVD 260 /* Notional KISS type */ +#define ARPHRD_ADAPT 264 +#define ARPHRD_ROSE 270 +#define ARPHRD_X25 271 /* CCITT X.25 */ +#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */ +#define ARPHRD_CAN 280 /* Controller Area Network */ +#define ARPHRD_PPP 512 +#define ARPHRD_CISCO 513 /* Cisco HDLC */ +#define ARPHRD_HDLC ARPHRD_CISCO +#define ARPHRD_LAPB 516 /* LAPB */ +#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */ +#define ARPHRD_RAWHDLC 518 /* Raw HDLC */ + +#define ARPHRD_TUNNEL 768 /* IPIP tunnel */ +#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ +#define ARPHRD_FRAD 770 /* Frame Relay Access Device */ +#define ARPHRD_SKIP 771 /* SKIP vif */ +#define ARPHRD_LOOPBACK 772 /* Loopback device */ +#define ARPHRD_LOCALTLK 773 /* Localtalk device */ +#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */ +#define ARPHRD_BIF 775 /* AP1000 BIF */ +#define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4 */ +#define ARPHRD_IPDDP 777 /* IP over DDP tunneller */ +#define ARPHRD_IPGRE 778 /* GRE over IP */ +#define ARPHRD_PIMREG 779 /* PIMSM register interface */ +#define ARPHRD_HIPPI 780 /* High Performance Parallel Interface */ +#define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */ +#define ARPHRD_ECONET 782 /* Acorn Econet */ +#define ARPHRD_IRDA 783 /* Linux-IrDA */ +/* ARP works differently on different FC media .. so */ +#define ARPHRD_FCPP 784 /* Point to point fibrechannel */ +#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */ +#define ARPHRD_FCPL 786 /* Fibrechannel public loop */ +#define ARPHRD_FCFABRIC 787 /* Fibrechannel fabric */ + /* 787->799 reserved for fibrechannel media types */ +#define ARPHRD_IEEE802_TR 800 /* Magic type ident for TR */ +#define ARPHRD_IEEE80211 801 /* IEEE 802.11 */ +#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */ +#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */ + +#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ +#define ARPHRD_NONE 0xFFFE /* zero header length */ + +/* ARP protocol opcodes. */ +#define ARPOP_REQUEST 1 /* ARP request */ +#define ARPOP_REPLY 2 /* ARP reply */ +#define ARPOP_RREQUEST 3 /* RARP request */ +#define ARPOP_RREPLY 4 /* RARP reply */ +#define ARPOP_InREQUEST 8 /* InARP request */ +#define ARPOP_InREPLY 9 /* InARP reply */ +#define ARPOP_NAK 10 /* (ATM)ARP NAK */ + + +/* ARP Flag values. */ +#define ATF_COM 0x02 /* completed entry (ha valid) */ +#define ATF_PERM 0x04 /* permanent entry */ +#define ATF_PUBL 0x08 /* publish entry */ +#define ATF_USETRAILERS 0x10 /* has requested trailers */ +#define ATF_NETMASK 0x20 /* want to use a netmask (only + for proxy entries) */ +#define ATF_DONTPUB 0x40 /* don't answer this addresses */ + +typedef unsigned short __be16; + +/* + * This structure defines an ethernet arp header. + */ +struct arphdr +{ + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + +#if 0 + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +#endif + +}; + +/* + * This structure defines an ethernet arp header. + */ +struct arp_eth_ipv4 +{ + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + + unsigned char ar_sha[6]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[6]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +}; + + +#endif /* _LINUX_IF_ARP_H */ diff --git a/firmware/microblaze/lib/net_common.c b/firmware/microblaze/lib/net_common.c new file mode 100644 index 000000000..693502d18 --- /dev/null +++ b/firmware/microblaze/lib/net_common.c @@ -0,0 +1,435 @@ +/* -*- c -*- */ +/* + * Copyright 2009,2010 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 "net_common.h" +#include "banal.h" +#include <hal_io.h> +#include <buffer_pool.h> +#include <memory_map.h> +#include <memcpy_wa.h> +#include <ethernet.h> +#include <net/padded_eth_hdr.h> +#include <lwip/ip.h> +#include <lwip/udp.h> +#include <lwip/icmp.h> +#include <stdlib.h> +#include <nonstdio.h> +#include "arp_cache.h" +#include "if_arp.h" +#include <ethertype.h> +#include <string.h> + + +int cpu_tx_buf_dest_port = PORT_ETH; + +// If this is non-zero, this dbsm could be writing to the ethernet +dbsm_t *ac_could_be_sending_to_eth; + +static inline bool +ip_addr_eq(const struct ip_addr a, const struct ip_addr b) +{ + return a.addr == b.addr; +} + +// ------------------------------------------------------------------------ + +get_eth_mac_addr_t _get_eth_mac_addr = NULL; + +void register_get_eth_mac_addr(get_eth_mac_addr_t get_eth_mac_addr){ + _get_eth_mac_addr = get_eth_mac_addr; +} + +get_ip_addr_t _get_ip_addr = NULL; + +void register_get_ip_addr(get_ip_addr_t get_ip_addr){ + _get_ip_addr = get_ip_addr; +} + +//------------------------------------------------------------------------- + +#define MAX_UDP_LISTENERS 6 + +struct listener_entry { + unsigned short port; + udp_receiver_t rcvr; +}; + +static struct listener_entry listeners[MAX_UDP_LISTENERS]; + +static struct listener_entry * +find_listener_by_port(unsigned short port) +{ + for (int i = 0; i < MAX_UDP_LISTENERS; i++){ + if (port == listeners[i].port) + return &listeners[i]; + } + return 0; +} + +static struct listener_entry * +find_free_listener(void) +{ + for (int i = 0; i < MAX_UDP_LISTENERS; i++){ + if (listeners[i].rcvr == 0) + return &listeners[i]; + } + abort(); +} + +void +register_udp_listener(int port, udp_receiver_t rcvr) +{ + struct listener_entry *lx = find_listener_by_port(port); + if (lx) + lx->rcvr = rcvr; + else { + lx = find_free_listener(); + lx->port = port; + lx->rcvr = rcvr; + } +} + +// ------------------------------------------------------------------------ + + +/*! + * low level routine to assembly an ethernet frame and send it. + * + * \param dst destination mac address + * \param ethertype ethertype field + * \param buf0 first part of data + * \param len0 length of first part of data + * \param buf1 second part of data + * \param len1 length of second part of data + * \param buf2 third part of data + * \param len2 length of third part of data + */ +static void +send_pkt(eth_mac_addr_t dst, int ethertype, + const void *buf0, size_t len0, + const void *buf1, size_t len1, + const void *buf2, size_t len2) +{ + // Wait for buffer to become idle + // FIXME can this ever not be ready? + + //hal_set_leds(LED_BUF_BUSY, LED_BUF_BUSY); + while((buffer_pool_status->status & BPS_IDLE(CPU_TX_BUF)) == 0) + ; + //hal_set_leds(0, LED_BUF_BUSY); + + // Assemble the header + padded_eth_hdr_t ehdr; + ehdr.pad = 0; + ehdr.dst = dst; + ehdr.src = _get_eth_mac_addr(); + ehdr.ethertype = ethertype; + + uint32_t *p = buffer_ram(CPU_TX_BUF); + + // Copy the pieces into the buffer + *p++ = 0x0; // slow path + memcpy_wa(p, &ehdr, sizeof(ehdr)); // 4 lines + p += sizeof(ehdr)/sizeof(uint32_t); + + + // FIXME modify memcpy_wa to do read/modify/write if reqd + + if (len0 && ((len0 & 0x3) || (intptr_t) buf0 & 0x3)) + printf("send_pkt: bad alignment of len0 and/or buf0\n"); + + if (len1 && ((len1 & 0x3) || (intptr_t) buf1 & 0x3)) + printf("send_pkt: bad alignment of len1 and/or buf1\n"); + + if (len2 && ((len2 & 0x3) || (intptr_t) buf2 & 0x3)) + printf("send_pkt: bad alignment of len2 and/or buf2\n"); + + if (len0){ + memcpy_wa(p, buf0, len0); + p += len0/sizeof(uint32_t); + } + if (len1){ + memcpy_wa(p, buf1, len1); + p += len1/sizeof(uint32_t); + } + if (len2){ + memcpy_wa(p, buf2, len2); + p += len2/sizeof(uint32_t); + } + + size_t total_len = (p - buffer_ram(CPU_TX_BUF)) * sizeof(uint32_t); + if (total_len < 60) // ensure that we don't try to send a short packet + total_len = 60; + + // wait until nobody else is sending to the ethernet + if (ac_could_be_sending_to_eth){ + //hal_set_leds(LED_ETH_BUSY, LED_ETH_BUSY); + dbsm_wait_for_opening(ac_could_be_sending_to_eth); + //hal_set_leds(0x0, LED_ETH_BUSY); + } + + if (0){ + printf("send_pkt to port %d, len = %d\n", + cpu_tx_buf_dest_port, (int) total_len); + print_buffer(buffer_ram(CPU_TX_BUF), total_len/4); + } + + // fire it off + bp_send_from_buf(CPU_TX_BUF, cpu_tx_buf_dest_port, 1, 0, total_len/4); + + // wait for it to complete (not long, it's a small pkt) + while((buffer_pool_status->status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) == 0) + ; + + bp_clear_buf(CPU_TX_BUF); +} + +unsigned int +chksum_buffer(unsigned short *buf, int nshorts, unsigned int initial_chksum) +{ + unsigned int chksum = initial_chksum; + for (int i = 0; i < nshorts; i++) + CHKSUM(buf[i], &chksum); + + return chksum; +} + + +void +send_ip_pkt(struct ip_addr dst, int protocol, + const void *buf0, size_t len0, + const void *buf1, size_t len1) +{ + struct ip_addr src = _get_ip_addr(); + int ttl = 32; + + struct ip_hdr ip; + IPH_VHLTOS_SET(&ip, 4, 5, 0); + IPH_LEN_SET(&ip, IP_HLEN + len0 + len1); + IPH_ID_SET(&ip, 0); + IPH_OFFSET_SET(&ip, IP_DF); /* don't fragment */ + ip._ttl_proto = (ttl << 8) | (protocol & 0xff); + ip._chksum = 0; + ip.src = src; + ip.dest = dst; + + ip._chksum = ~chksum_buffer((unsigned short *) &ip, + sizeof(ip)/sizeof(short), 0); + + eth_mac_addr_t dst_mac; + bool found = arp_cache_lookup_mac(&ip.dest, &dst_mac); + if (!found){ + printf("net_common: failed to hit cache looking for "); + print_ip(ip.dest); + newline(); + return; + } + + send_pkt(dst_mac, ETHERTYPE_IPV4, + &ip, sizeof(ip), buf0, len0, buf1, len1); +} + +void +send_udp_pkt(int src_port, struct socket_address dst, + const void *buf, size_t len) +{ + struct udp_hdr udp _AL4; + udp.src = src_port; + udp.dest = dst.port; + udp.len = UDP_HLEN + len; + udp.chksum = 0; + + send_ip_pkt(dst.addr, IP_PROTO_UDP, + &udp, sizeof(udp), buf, len); +} + +static void +handle_udp_packet(struct ip_addr src_ip, struct ip_addr dst_ip, + struct udp_hdr *udp, size_t len) +{ + if (len != udp->len){ + printf("UDP inconsistent lengths: %d %d\n", (int)len, udp->len); + return; + } + + unsigned char *payload = ((unsigned char *) udp) + UDP_HLEN; + int payload_len = len - UDP_HLEN; + + if (0){ + printf("\nUDP: src = %d dst = %d len = %d\n", + udp->src, udp->dest, udp->len); + + //print_bytes(0, payload, payload_len); + } + + struct listener_entry *lx = find_listener_by_port(udp->dest); + if (lx){ + struct socket_address src = make_socket_address(src_ip, udp->src); + struct socket_address dst = make_socket_address(dst_ip, udp->dest); + lx->rcvr(src, dst, payload, payload_len); + } +} + +static void +handle_icmp_packet(struct ip_addr src, struct ip_addr dst, + struct icmp_echo_hdr *icmp, size_t len) +{ + switch (icmp->type){ + case ICMP_DUR: // Destinatino Unreachable + //stop_streaming(); //FIXME + if (icmp->code == ICMP_DUR_PORT){ // port unreachable + //struct udp_hdr *udp = (struct udp_hdr *)((char *)icmp + 28); + //printf("icmp port unr %d\n", udp->dest); + putchar('i'); + } + else { + //printf("icmp dst unr (code: %d)", icmp->code); + putchar('i'); + } + break; + + case ICMP_ECHO: + default: + break; + } +} + +static void __attribute__((unused)) +print_arp_ip(const unsigned char ip[4]) +{ + printf("%d.%d.%d.%d", ip[0], ip[1], ip[2],ip[3]); +} + +static void +send_arp_reply(struct arp_eth_ipv4 *req, eth_mac_addr_t our_mac) +{ + struct arp_eth_ipv4 reply _AL4; + reply.ar_hrd = req->ar_hrd; + reply.ar_pro = req->ar_pro; + reply.ar_hln = req->ar_hln; + reply.ar_pln = req->ar_pln; + reply.ar_op = ARPOP_REPLY; + memcpy(reply.ar_sha, &our_mac, 6); + memcpy(reply.ar_sip, req->ar_tip, 4); + memcpy(reply.ar_tha, req->ar_sha, 6); + memcpy(reply.ar_tip, req->ar_sip, 4); + + eth_mac_addr_t t; + memcpy(t.addr, reply.ar_tha, 6); + send_pkt(t, ETHERTYPE_ARP, &reply, sizeof(reply), 0, 0, 0, 0); +} + + +static void +handle_arp_packet(struct arp_eth_ipv4 *p, size_t size) +{ + if (size < sizeof(struct arp_eth_ipv4)){ + printf("\nhandle_arp: weird size = %d\n", (int)size); + return; + } + + if (0){ + printf("ar_hrd = %d\n", p->ar_hrd); + printf("ar_pro = %d\n", p->ar_pro); + printf("ar_hln = %d\n", p->ar_hln); + printf("ar_pln = %d\n", p->ar_pln); + printf("ar_op = %d\n", p->ar_op); + printf("ar_sha = "); print_mac_addr(p->ar_sha); newline(); + printf("ar_sip = "); print_arp_ip(p->ar_sip); newline(); + printf("ar_tha = "); print_mac_addr(p->ar_tha); newline(); + printf("ar_tip = "); print_arp_ip(p->ar_tip); newline(); + } + + if (p->ar_hrd != ARPHRD_ETHER + || p->ar_pro != ETHERTYPE_IPV4 + || p->ar_hln != 6 + || p->ar_pln != 4) + return; + + if (p->ar_op != ARPOP_REQUEST) + return; + + struct ip_addr sip; + struct ip_addr tip; + + sip.addr = get_int32(p->ar_sip); + tip.addr = get_int32(p->ar_tip); + + if (ip_addr_eq(tip, _get_ip_addr())){ // They're looking for us... + send_arp_reply(p, _get_eth_mac_addr()); + } +} + +void +handle_eth_packet(uint32_t *p, size_t nlines) +{ + //print_buffer(p, nlines); + + int ethertype = p[3] & 0xffff; + + if (ethertype == ETHERTYPE_ARP){ + struct arp_eth_ipv4 *arp = (struct arp_eth_ipv4 *)(p + 4); + handle_arp_packet(arp, nlines*sizeof(uint32_t) - 14); + } + else if (ethertype == ETHERTYPE_IPV4){ + struct ip_hdr *ip = (struct ip_hdr *)(p + 4); + if (IPH_V(ip) != 4 || IPH_HL(ip) != 5) // ignore pkts w/ bad version or options + return; + + if (IPH_OFFSET(ip) & (IP_MF | IP_OFFMASK)) // ignore fragmented packets + return; + + // FIXME filter on dest ip addr (should be broadcast or for us) + + arp_cache_update(&ip->src, (eth_mac_addr_t *)(((char *)p)+8)); + + int protocol = IPH_PROTO(ip); + int len = IPH_LEN(ip) - IP_HLEN; + + switch (protocol){ + case IP_PROTO_UDP: + handle_udp_packet(ip->src, ip->dest, (struct udp_hdr *)(((char *)ip) + IP_HLEN), len); + break; + + case IP_PROTO_ICMP: + handle_icmp_packet(ip->src, ip->dest, (struct icmp_echo_hdr *)(((char *)ip) + IP_HLEN), len); + break; + + default: // ignore + break; + } + } + else + return; // Not ARP or IPV4, ignore +} + +// ------------------------------------------------------------------------ + +void +print_ip(struct ip_addr ip) +{ + unsigned int t = ntohl(ip.addr); + printf("%d.%d.%d.%d", + (t >> 24) & 0xff, + (t >> 16) & 0xff, + (t >> 8) & 0xff, + t & 0xff); +} diff --git a/firmware/microblaze/lib/net_common.h b/firmware/microblaze/lib/net_common.h new file mode 100644 index 000000000..cfba43412 --- /dev/null +++ b/firmware/microblaze/lib/net_common.h @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010 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/>. + */ +#ifndef INCLUDED_NET_COMMON_H +#define INCLUDED_NET_COMMON_H + +#include <stdint.h> +#include <stddef.h> +#include <dbsm.h> +#include <net/socket_address.h> +#include <net/eth_mac_addr.h> + +#define CPU_TX_BUF 7 // cpu -> eth + +extern int cpu_tx_buf_dest_port; + +// If this is non-zero, this dbsm could be writing to the ethernet +extern dbsm_t *ac_could_be_sending_to_eth; + +void stop_streaming(void); + +/*! + * Helpful typedefs for callback + */ +typedef void (*udp_receiver_t)(struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len); + +typedef eth_mac_addr_t (*get_eth_mac_addr_t)(void); +typedef struct ip_addr (*get_ip_addr_t)(void); + +/*! + * Functions to register callbacks + */ +void register_get_eth_mac_addr(get_eth_mac_addr_t get_eth_mac_addr); + +void register_get_ip_addr(get_ip_addr_t get_ip_addr); + +void register_udp_listener(int port, udp_receiver_t rcvr); + +void send_udp_pkt(int src_port, struct socket_address dst, + const void *buf, size_t len); + +void handle_eth_packet(uint32_t *p, size_t nlines); + + +#endif /* INCLUDED_NET_COMMON_H */ |