/*
 * FILENAME: example4.c
 *
 * Copyright 2011-2012 By InterNiche Technologies Inc. All rights reserved
 *
 * This example program implements a UDP echo server
 *
 * ROUTINES: udp_secho_init(), udp_echo_poll(), udp_secho_close(),
 */

#include "FreeRTOS.h"
#include "task.h"
#include "embtcp.h"   
#include <stdio.h>

#define  ECHO_PORT    7    /* standard UDP/TCP echo port */
#define  ECHOBUFSIZE  256 

SOCKTYPE es_sock = INVALID_SOCKET; /* Accepts connections on this socket */

int  udp_secho_init(void);
void udp_echo_poll(void);
int  udp_secho_close (void);

/* Define and create the udp echo server task */
TASK *to_us_echo;
void tk_us_echo(void *parm);

extern int get_mac_address(int iface, uint8_t *mac_addr);

/* FUNCTION: example_init()
 *
 * Create udp echo server task
 *
 * PARAMS: none
 *
 * RETURNS: int  0 if OK, else error code
 */
int
example_init(void)
{
   int ret;
   uint8_t my_macaddr[6];
   static uint8_t cmpbuf[6] = { 0, 0, 0, 0, 0, 0 };

   if ((get_mac_address(0, my_macaddr) != 0) || 
      memcmp(my_macaddr, cmpbuf, 6) == 0)
   {
      panic("get_mac_address() returned all zeros\n");
   }
   ret = TK_CREATE(&tk_us_echo, "us_echo", 1024, 0, 2, &to_us_echo);

   return (ret);
}


/* FUNCTION: tk_us_echo
 *
 * Waits for iniche_net_ready, then starts udp_secho_init.  
 * On return it deletes the task
 *
 * PARAM1: not used
 *
 * RETURNS: none--task deleted
 */

void
tk_us_echo(void *parm)
{
   while (!iniche_net_ready)
      TK_SLEEP(4);

   udp_secho_init();     /* Runs whole task once */
   TK_DELETE(TK_THIS);
}


/* FUNCTION: udp_secho_init()
 *
 * Initialize the UDP Echo server to listen to UDP port 7
 *
 * PARAM1: void
 *
 * RETURNS: SUCCESS or error number
 */

int 
udp_secho_init(void)
{
   int err;    /* error holder */
   struct sockaddr_in me;   /* my IP info, for bind() */
   SOCKTYPE sock;

   printf("udp echo server is starting.\n");

   me.sin_family = AF_INET;
   me.sin_addr.s_addr = INADDR_ANY;
   me.sin_port = htons(ECHO_PORT);

   /* open UDP socket */
   sock = t_socket(AF_INET, SOCK_DGRAM, 0);
   if (sock == INVALID_SOCKET)
   {
      printf("udp_echo: t_socket failed\n");
      return (-1);
   }
   err = t_bind(sock, (struct sockaddr *)&me, sizeof(me));
   if (err != 0)
   {
      err = t_errno(sock);
      printf("udp_echo: bind error: %d\n", err);
      t_socketclose(sock);
      return (-1);
   }
   es_sock = sock;

   /* put socket into non-blocking mode */
   t_setsockopt(es_sock, SOL_SOCKET, SO_NBIO, NULL, 0);
   udp_echo_poll();   
   return (0);
}


/* FUNCTION: udp_echo_poll()
 * 
 * Poll all the UDP Echo related activities. That includes a UDP
 * Echo Server and UDP Echo Clients for each I/O session.
 *
 * PARAM1: 
 *
 * RETURNS: void
 */

static char inbuf[ECHOBUFSIZE];

void
udp_echo_poll(void)
{
   struct sockaddr_in him;  /* IP info of current rhost */
   int   len;
   int   err;
   int   sa_size = sizeof(him);

   for (;;)
   {
      len = t_recvfrom(es_sock, inbuf, ECHOBUFSIZE, 0, (struct sockaddr *)&him, 
                       &sa_size);
      if (len < 0)
      {
         err = t_errno(es_sock);
         if (err != EWOULDBLOCK)
            printf("UDP echo server socket error %d\n", err);
      }
      else if (len == 0)
      {
         udp_secho_close();
      }
      else  
         t_sendto(es_sock, inbuf, len, 0, (struct sockaddr *)&him, sizeof(him));

      TK_SLEEP(2);
   }
}


/* FUNCTION: udp_secho_close()
 *
 * Cleanup the UDP Echo Server
 *
 * PARAM1: void
 *
 * RETURNS: SUCCESS or error number 
 */

int
udp_secho_close(void)
{
   printf("udp echo - closing server socket\n");
   t_socketclose(es_sock);
   return (0);
}



