/*
    ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010 Giovanni Di Sirio.
    This file is part of ChibiOS/RT.
    ChibiOS/RT 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.
    ChibiOS/RT 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 .
*/
#include 
#include "ch.h"
#include "hal.h"
#include "evtimer.h"
#include "uip.h"
#include "uip_arp.h"
#include "httpd.h"
#include "clock-arch.h"
#define IPADDR0  192
#define IPADDR1  168
#define IPADDR2  1
#define IPADDR3  20
#define SEND_TIMEOUT 50
static const struct uip_eth_addr macaddr = {
  {0xC2, 0xAF, 0x51, 0x03, 0xCF, 0x46}
};
#define BUF ((struct uip_eth_hdr *)&uip_buf[0])
/*
 * uIP send function wrapping the EMAC functions.
 */
static void network_device_send(void) {
  MACTransmitDescriptor td;
  if (macWaitTransmitDescriptor(Ð1, &td, MS2ST(SEND_TIMEOUT)) == RDY_OK) {
    if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN)
      macWriteTransmitDescriptor(&td, uip_buf, uip_len);
    else {
      macWriteTransmitDescriptor(&td, uip_buf, UIP_LLH_LEN + UIP_TCPIP_HLEN);
      macWriteTransmitDescriptor(&td, uip_appdata,
                                 uip_len - (UIP_LLH_LEN + UIP_TCPIP_HLEN));
    }
    macReleaseTransmitDescriptor(&td);
  }
  /* Dropped... */
}
/*
 * uIP receive function wrapping the EMAC function.
 */
static size_t network_device_read(void) {
  MACReceiveDescriptor rd;
  size_t size;
  if (macWaitReceiveDescriptor(Ð1, &rd, TIME_IMMEDIATE) != RDY_OK)
    return 0;
  size = rd.rd_size;
  macReadReceiveDescriptor(&rd, uip_buf, size);
  macReleaseReceiveDescriptor(&rd);
  return size;
}
void clock_init(void) {}
clock_time_t clock_time( void )
{
  return chTimeNow();
}
/*
 * TCP/IP periodic timer.
 */
static void PeriodicTimerHandler(eventid_t id) {
  int i;
  (void)id;
  for (i = 0; i < UIP_CONNS; i++) {
    uip_periodic(i);
    if (uip_len > 0) {
      uip_arp_out();
      network_device_send();
    }
  }
}
/*
 * ARP periodic timer.
 */
static void ARPTimerHandler(eventid_t id) {
  (void)id;
  (void)macPollLinkStatus(Ð1);
  uip_arp_timer();
}
/*
 * Ethernet frame received.
 */
static void FrameReceivedHandler(eventid_t id) {
  (void)id;
  while ((uip_len = network_device_read()) > 0) {
    if (BUF->type == HTONS(UIP_ETHTYPE_IP)) {
      uip_arp_ipin();
      uip_input();
      if (uip_len > 0) {
        uip_arp_out();
        network_device_send();
      }
    }
    else if (BUF->type == HTONS(UIP_ETHTYPE_ARP)) {
      uip_arp_arpin();
      if (uip_len > 0)
        network_device_send();
    }
  }
}
#define FRAME_RECEIVED_ID       0
#define PERIODIC_TIMER_ID       1
#define ARP_TIMER_ID            2
static const evhandler_t evhndl[] = {
  FrameReceivedHandler,
  PeriodicTimerHandler,
  ARPTimerHandler
};
msg_t WebThread(void *p) {
  EvTimer evt1, evt2;
  EventListener el0, el1, el2;
  uip_ipaddr_t ipaddr;
  (void)p;
  /*
   * Event sources setup.
   */
  chEvtRegister(macGetReceiveEventSource(Ð1), &el0, FRAME_RECEIVED_ID);
  chEvtAddFlags(EVENT_MASK(FRAME_RECEIVED_ID)); /* In case some frames are already buffered */
  evtInit(&evt1, MS2ST(500));
  evtStart(&evt1);
  chEvtRegister(&evt1.et_es, &el1, PERIODIC_TIMER_ID);
  evtInit(&evt2, S2ST(10));
  evtStart(&evt2);
  chEvtRegister(&evt2.et_es, &el2, ARP_TIMER_ID);
  /*
   * EMAC settings.
   */
  macSetAddress(Ð1, &macaddr.addr[0]);
  (void)macPollLinkStatus(Ð1);
  /*
   * uIP initialization.
   */
  uip_init();
  uip_setethaddr(macaddr);
  uip_ipaddr(ipaddr, IPADDR0, IPADDR1, IPADDR2, IPADDR3);
  uip_sethostaddr(ipaddr);
  httpd_init();
  while (TRUE) {
    chEvtDispatch(evhndl, chEvtWaitOne(ALL_EVENTS));
  }
  return 0;
}