CDC/ECM driver for mbed, based on USBDevice by mbed-official. Uses PicoTCP to access Ethernet USB device. License: GPLv2

Dependents:   USBEthernet_TEST

Fork of USB_Ethernet by Daniele Lacamera

Revision:
2:540f6e142d59
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stack/pico_arp.c	Sat Aug 03 13:16:14 2013 +0000
@@ -0,0 +1,317 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+Authors: Daniele Lacamera
+*********************************************************************/
+
+
+#include "pico_config.h"
+#include "pico_arp.h"
+#include "pico_tree.h"
+#include "pico_ipv4.h"
+#include "pico_device.h"
+#include "pico_stack.h"
+
+const uint8_t PICO_ETHADDR_ALL[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+#define PICO_ARP_TIMEOUT 600000
+#define PICO_ARP_RETRY 300
+
+#ifdef DEBUG_ARP
+    #define arp_dbg dbg
+#else
+    #define arp_dbg(...) do{}while(0)
+#endif
+
+static struct pico_queue pending;
+static int pending_timer_on = 0;
+
+void check_pending(unsigned long now, void *_unused)
+{
+  struct pico_frame *f = pico_dequeue(&pending);
+  if (!f) {
+    pending_timer_on = 0;
+    return;
+  }
+  if(pico_ethernet_send(f) > 0)
+    pico_frame_discard(f);
+  pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
+}
+
+
+struct
+__attribute__ ((__packed__)) 
+pico_arp_hdr
+{
+  uint16_t htype;
+  uint16_t ptype;
+  uint8_t hsize;
+  uint8_t psize;
+  uint16_t opcode;
+  uint8_t s_mac[PICO_SIZE_ETH];
+  struct pico_ip4 src;
+  uint8_t d_mac[PICO_SIZE_ETH];
+  struct pico_ip4 dst;
+};
+
+
+#define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr)))
+
+/* Arp Entries for the tables. */
+struct pico_arp {
+/* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure, 
+ * due to in-place casting!!! */
+  struct pico_eth eth;
+  struct pico_ip4 ipv4;
+  int    arp_status;
+  uint32_t timestamp;
+  struct pico_device *dev;
+};
+
+
+
+/*****************/
+/**  ARP TREE **/
+/*****************/
+
+/* Routing destination */
+
+static int arp_compare(void * ka, void * kb)
+{
+    struct pico_arp *a = ka, *b = kb;
+  if (a->ipv4.addr < b->ipv4.addr)
+    return -1;
+  else if (a->ipv4.addr > b->ipv4.addr)
+    return 1;
+  return 0;
+}
+
+PICO_TREE_DECLARE(arp_tree, arp_compare);
+
+/*********************/
+/**  END ARP TREE **/
+/*********************/
+
+struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst)
+{
+  struct pico_arp search, *found;
+  search.ipv4.addr = dst->addr;
+  found = pico_tree_findKey(&arp_tree,&search);
+  if (found && (found->arp_status != PICO_ARP_STATUS_STALE))
+    return &found->eth;
+  return NULL;
+}
+
+struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst)
+{
+  struct pico_arp* search;
+  struct pico_tree_node * index;
+  pico_tree_foreach(index,&arp_tree){
+      search = index->keyValue;
+    if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0)
+      return &search->ipv4;
+  }
+  return NULL;
+}
+
+struct pico_eth *pico_arp_get(struct pico_frame *f) {
+  struct pico_eth *a4;
+  struct pico_ip4 gateway;
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  struct pico_ipv4_link *l;
+
+  l = pico_ipv4_link_get(&hdr->dst);
+  if(l){
+    //address belongs to ourself
+    return &l->dev->eth->mac;
+  }
+
+  gateway = pico_ipv4_route_get_gateway(&hdr->dst);
+  /* check if dst is local (gateway = 0), or if to use gateway */
+  if (gateway.addr != 0)
+    a4 = pico_arp_lookup(&gateway);          /* check if gateway ip mac in cache */
+  else
+    a4 = pico_arp_lookup(&hdr->dst);         /* check if local ip mac in cache */
+  if (!a4) {
+     if (++f->failure_count < 4) {
+       dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
+       /* check if dst is local (gateway = 0), or if to use gateway */
+       if (gateway.addr != 0)
+         pico_arp_query(f->dev, &gateway);  /* arp to gateway */
+       else
+         pico_arp_query(f->dev, &hdr->dst); /* arp to dst */
+
+       pico_enqueue(&pending, f);
+       if (!pending_timer_on) {
+        pending_timer_on++;
+        pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
+       }
+     } else {
+      dbg("ARP: Destination Unreachable\n");
+      pico_notify_dest_unreachable(f);
+      pico_frame_discard(f);
+    }
+  }
+  return a4;
+}
+
+#ifdef DEBUG_ARP
+void dbg_arp(void)
+{
+  struct pico_arp *a;
+  struct pico_tree_node * index;
+
+  pico_tree_foreach(index,&arp_tree) {
+      a = index->keyValue;
+    arp_dbg("ARP to  %08x, mac: %02x:%02x:%02x:%02x:%02x:%02x\n", a->ipv4.addr,a->eth.addr[0],a->eth.addr[1],a->eth.addr[2],a->eth.addr[3],a->eth.addr[4],a->eth.addr[5] );
+  }
+}
+#endif
+
+void arp_expire(unsigned long now, void *_stale)
+{
+  struct pico_arp *stale = (struct pico_arp *) _stale;
+  stale->arp_status = PICO_ARP_STATUS_STALE;
+  arp_dbg("ARP: Setting arp_status to STALE\n");
+  pico_arp_query(stale->dev, &stale->ipv4);
+
+}
+
+void pico_arp_add_entry(struct pico_arp *entry)
+{
+    entry->arp_status = PICO_ARP_STATUS_REACHABLE;
+    entry->timestamp  = PICO_TIME();
+
+    pico_tree_insert(&arp_tree,entry);
+    arp_dbg("ARP ## reachable.\n");
+    pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry);
+}
+
+int pico_arp_create_entry(uint8_t* hwaddr, struct pico_ip4 ipv4, struct pico_device* dev)
+{
+    struct pico_arp* arp = pico_zalloc(sizeof(struct pico_arp));
+    if(!arp){
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+    memcpy(arp->eth.addr, hwaddr, 6);
+    arp->ipv4.addr = ipv4.addr;
+    arp->dev = dev;
+
+    pico_arp_add_entry(arp);
+
+    return 0;
+}
+
+int pico_arp_receive(struct pico_frame *f)
+{
+  struct pico_arp_hdr *hdr;
+  struct pico_arp search, *found, *new = NULL;
+  int ret = -1;
+  hdr = (struct pico_arp_hdr *) f->net_hdr;
+
+  if (!hdr)
+    goto end;
+
+
+  /* Populate a new arp entry */
+  search.ipv4.addr = hdr->src.addr;
+  memcpy(search.eth.addr, hdr->s_mac, PICO_SIZE_ETH);
+
+  /* Search for already existing entry */
+
+  found = pico_tree_findKey(&arp_tree,&search);
+  if (!found) {
+    new = pico_zalloc(sizeof(struct pico_arp));
+    if (!new)
+      goto end;
+    new->ipv4.addr = hdr->src.addr;
+  }
+  else if (found->arp_status == PICO_ARP_STATUS_STALE) {
+    /* Replace if stale */
+    new = found;
+
+    pico_tree_delete(&arp_tree,new);
+  }
+
+  ret = 0;
+
+  if (new) {
+    memcpy(new->eth.addr, hdr->s_mac, PICO_SIZE_ETH);
+    new->dev = f->dev;
+    pico_arp_add_entry(new);
+  }
+
+  if (hdr->opcode == PICO_ARP_REQUEST) {
+    struct pico_ip4 me;
+    struct pico_eth_hdr *eh = (struct pico_eth_hdr *)f->datalink_hdr;
+    struct pico_device *link_dev;
+    me.addr = hdr->dst.addr;
+
+    link_dev = pico_ipv4_link_find(&me);
+    if (link_dev != f->dev)
+      goto end;
+
+    hdr->opcode = PICO_ARP_REPLY;
+    memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH);
+    memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH);
+    hdr->dst.addr = hdr->src.addr;
+    hdr->src.addr = me.addr;
+
+    /* Prepare eth header for arp reply */
+    memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH);
+    memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
+    f->start = f->datalink_hdr;
+    f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR;
+    f->dev->send(f->dev, f->start, f->len);
+  }
+
+#ifdef DEBUG_ARG
+  dbg_arp();
+#endif
+
+end:
+  pico_frame_discard(f);
+  return ret;
+}
+
+int pico_arp_query(struct pico_device *dev, struct pico_ip4 *dst)
+{
+  struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR);
+  struct pico_eth_hdr *eh;
+  struct pico_arp_hdr *ah;
+  struct pico_ip4 *src;
+  int ret;
+
+  src = pico_ipv4_source_find(dst);
+  if (!src)
+    return -1;
+
+  arp_dbg("QUERY: %08x\n", dst->addr);
+
+  if (!q)
+    return -1;
+  eh = (struct pico_eth_hdr *)q->start;
+  ah = (struct pico_arp_hdr *) (q->start + PICO_SIZE_ETHHDR);
+
+  /* Fill eth header */
+  memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH);
+  memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
+  eh->proto = PICO_IDETH_ARP;
+
+  /* Fill arp header */
+  ah->htype  = PICO_ARP_HTYPE_ETH;
+  ah->ptype  = PICO_IDETH_IPV4;
+  ah->hsize  = PICO_SIZE_ETH;
+  ah->psize  = PICO_SIZE_IP4;
+  ah->opcode = PICO_ARP_REQUEST;
+  memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH);
+  ah->src.addr = src->addr;
+  ah->dst.addr = dst->addr;
+  arp_dbg("Sending arp query.\n");
+  ret = dev->send(dev, q->start, q->len);
+  pico_frame_discard(q);
+  return ret;
+}