/*
 * FILENAME: example2.c
 *
 * Copyright 2011-2012 By InterNiche Technologies Inc. All rights reserved
 *
 * This example program implements a TCP echo server
 *
 * ROUTINES: tcp_secho_init(),  tcp_echo_recv(), tesvrecho(), 
 */

#include "FreeRTOS.h"
#include "task.h"
#include "embtcp.h"   
#include <stdio.h>

/* Some configuration parameters  */
#define  ECHO_PORT      7      /* standard UDP/TCP echo port */
#define  ECHOBUFSIZE    256    /* Only echos the first 256 bytes */

SOCKTYPE elisten_sock = INVALID_SOCKET; /* echo server socket */
SOCKTYPE esvr_sock    = INVALID_SOCKET; /* echo server active socket */

char *srv_inbuf = NULL;

int  tcp_secho_init(void);
void tcp_echo_recv(void);
int  tesvrecho(SOCKTYPE sock, char *bufp, int len);

/* Define and create the tcp echo client task */
TASK *to_ts_echo;
void tk_ts_echo(void *parm);

extern int get_mac_address(int iface, uint8_t *mac_addr);

/* FUNCTION: tec_task_init()
 *
 * Create tcp echo client 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_ts_echo, "ts_echo", 1024, 0, 2, &to_ts_echo);

   return (ret);
}


/* FUNCTION: tk_tc_echo()
 *
 * Waits for iniche_net_ready, then starts tcp_secho_init.  
 * On return it deletes the task.
 *
 * PARAM1: not used
 *
 * RETURNS: none--task deleted
 */

void
tk_ts_echo(void *parm)
{
   while (!iniche_net_ready)
      TK_SLEEP(4);

   tcp_secho_init();     /* Runs whole task once */
   TK_DELETE(TK_THIS);
}


/* FUNCTION: tcp_secho_init()
 * 
 * Initialize the TCP Echo Server. 
 * 
 * PARAM1: void
 *
 * RETURNS: 0 on SUCCESS or error number. 
 */

int 
tcp_secho_init(void)
{
   int rtn = 0;
   struct sockaddr_in me;   /* my IP info, for bind() */

   printf("tcp echo srv - starting.\n");

    /* open TCP socket */
   me.sin_family = AF_INET;
   me.sin_addr.s_addr = INADDR_ANY;
   me.sin_port = htons(ECHO_PORT);

   if (((elisten_sock = t_socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) ||
       (t_bind(elisten_sock, (struct sockaddr *)&me, sizeof(me)) != 0) ||
       (t_listen(elisten_sock, 3) != 0))
   {
      printf("Failed to start tcpechosvr listening socket\n");
      return (-1);
   }
   /* for listen socket into Non-blocking mode so we can poll accept */
   t_setsockopt(elisten_sock, SOL_SOCKET, SO_NBIO, NULL, 0); 

   if ((srv_inbuf = (char *)pvPortMalloc(ECHOBUFSIZE)) == NULL)
   {
      printf("tcpechosvr out of memeory\n");
      return (-1);
   }
   memset(srv_inbuf, 0, ECHOBUFSIZE);
   tcp_echo_recv();
   return (rtn);     /* Actually, we never return here */
}


/* FUNCTION: tcp_echo_recv()
 *
 * tcp_echo_recv() - check for and process received echo data
 * 
 * PARAM1: none
 *
 * RETURNS: void
 */

void
tcp_echo_recv(void)
{
   int   len;          /* length of recv data */
   int   err;          /* error holder */
   SOCKTYPE tmpsock;   /* scratch socket */

   for (;;)
   {
      /* Handle accept for echo server  */
      struct sockaddr_in client;

      /* check for received echo connection on server */
      len = sizeof(client);
      tmpsock = t_accept(elisten_sock, (struct sockaddr *)&client, &len);
      if (tmpsock != INVALID_SOCKET)
      {
         printf("accept received a connection request\n");
         if (esvr_sock == INVALID_SOCKET)
         {
            esvr_sock = tmpsock;
         }
         else  /* we already have a connection */
         {
            printf("tcpecho: rejected extra connection\n");
            t_socketclose(tmpsock);   /* refuse to serve another */
         }
      }
      if (esvr_sock != INVALID_SOCKET)
      {
         /* check for received echo requests */
         len = t_recv(esvr_sock, srv_inbuf, ECHOBUFSIZE, 0);
         if (len < 0)
         {
            err = t_errno(esvr_sock);
            if (err != EWOULDBLOCK)
            {
               printf("TCP echo recv error %d\n", err);
            }
         }
         else if (len == 0)
         {
            printf("TCP echo: socket closed by other side\n");
            if (esvr_sock)
            {
               t_socketclose(esvr_sock);
               esvr_sock = INVALID_SOCKET;
            }
         }
         else  /* if (len > 0) - got some echo data */
         {
            tesvrecho(esvr_sock, srv_inbuf, len);
            continue;
         }
      }
      TK_SLEEP(2);
   }
}


/* FUNCTION: tesvrecho()
 *
 * Echo packet received on proper server socket
 * 
 * PARAM1: SOCKTYPE sock   - esvr_sock or esvr_sock6
 * PARAM2: char *bufp      - srv_inbuf
 * PARAM3: int bytestosend - length of data to be echoed
 *
 * RETURNS: 0 on SUCCESS or err number. 
 */

int
tesvrecho(SOCKTYPE sock, char *bufp, int bytestosend)
{   
   int bytessent;
   int err;

   while (bytestosend)
   { 
      bytessent = t_send(sock, bufp, bytestosend, 0);
      if (bytessent < 0)
      {
         err = t_errno(sock);
         if (err == EWOULDBLOCK)
         {
            taskYIELD();
            continue;
         }
         printf("TCP echo server, err=%d while sending reply\n", err);
         return (err);
      }
      else if (bytessent < bytestosend)   
      {
         /* Only part of the data was written */
         bufp += bytessent;
         bytestosend -= bytessent;
         taskYIELD();       /* Let other tasks run before we try again */
         continue;
      }
      else
         break;     /* All bytes sent  */
   }
   return (0);
}


