/* FILENAME: tcpdata.c
 *
 * Copyright 1997-2011 By InterNiche Technologies Inc. All rights reserved.
 *
 * This file contains CLI and other data structures for the InterNiche TCP/IP 
 * stack.  It also contains code for various user-specified functions (e.g.,
 * dtrap(), panic(), initialization routine, example CLI routine, etc.)
 *
 * Module: TCP/IP stack
 *
 * ROUTINES: embTCP_config(), upnp_callback(), dtrap(), panic(), 
 * ROUTINES: get_mac_address(), user_pre_setup(), user_post_setup(), 
 * ROUTINES: user1_cli()
 *
 * PORTABLE: yes
 *
 */

#include "FreeRTOS.h"
#include "task.h"
#include "iniche_types.h"
#include "embtcp.h"
#include "embcli.h"
#include "tcpdata.h"
#include "stkdata.h"
#include <stdio.h>
#include <rt_sys.h>

struct dhcpc_parms dhcp;
struct dnsc_parms dns;
struct icmp_parms ping;
struct ipv4_parms ipv4;
struct tcp_parms tcp;
struct udp_parms udp;

/*
 * Modules
 *
 * This is an array of all of the modules that are part of the 
 * build. Modules include netmain, CLI, console, etc.  The
 * CLI module MUST be the first module in the list.
 *
 * This list can be extended by adding entries for additional
 * modules (e.g., Telnet, HTTP, etc.).
 */

struct net_module;
extern struct net_module cli_module;
extern struct net_module netmain_module;
extern struct net_module console_module;

struct net_module *in_modules[] = 
{
   &cli_module,     /* CLI module */
   &netmain_module, /* netmain module */
   &console_module, /* console module */
};

int in_num_modules = sizeof(in_modules)/sizeof(struct net_module *);

/* Network free buffer queues
 *
 * This array defines the number and sizes of the free buffers
 * used to construct chained packets. Buffer sizes should be
 * sorted in ascending order by buffer size.
 */
NET_BUFQ in_bufq[] =
{
   { 16, 128 }, /* 16 buffers, each 128 bytes long */
   { 33, 512 }, /* 33 buffers, each 512 bytes long */
};

int in_num_bufq = sizeof(in_bufq)/sizeof(NET_BUFQ);


/* FUNCTION: embTCP_config()
 *
 * Initialize the configurable parameters for TCP/IP stack
 *
 * PARAMS: none
 *
 * RETURN: none
 */
void
embTCP_config(void)
{
   /* DHCP client parameters */

   /* Maximum number of retransmissions of DHCP packet */
   dhcp.max_tries = 4;
   /* DHCP vendor class identifier option data (max length = 32 bytes) */
   memcpy(&dhcp.vendclass, "0", 1);
   /* length of DHCP vendor class identifier option data */
   dhcp.vendclass_len = 1;
 
   /* DNS CLIENT CONFIGURATION */

   /* Number of times (including the first) that a specific DNS name 
    * resolution request will be sent before DNS gives up and returns an 
    * error
    */ 
   dns.max_tries = 4;

   /* Time interval (in seconds) between successive (retransmitted) DNS 
    * requests for a specific name resolution
    */
   dns.retx_interval = 15;

   /* Number of entries in the DNS client's table. An entry can contain 
    * information from a resolved request or a name resolution request 
    * that is in progress.
    */
   dns.max_entries = 3;
   
   /* IPv4 addresses of the DNS servers utilized for name resolution */
   dns.dns_servers[0] = "0.0.0.0";
   dns.dns_servers[1] = "0.0.0.0";
   dns.dns_servers[2] = "0.0.0.0";

   /* PING CONFIGURATION    */

   /* Enable/disable the sending of a ping response (ICMP Echo reply) 
    * when a ping request is received
    */
   ping.enable_resp = TRUE;

   /* Maximum number of simultaneous ping sessions that will be allowed.  
    * Each ping session contains information about one outstanding ICMP 
    * Echo packet.
    */
   ping.max_sessions = 2;

   /* Maximum amount of time (in seconds) to wait for a ping response */
   ping.timeout = 60;

   /* IP CONFIGURATION  */

   /* Maximum amount of time (in seconds) to wait for reassembly of a 
    * fragmented IPv4 datagram to complete
    */
   ipv4.reasm_tmo = 20;

   /* Default value of Type of Service (TOS) field in IPv4 header of 
    * outgoing packets 
    */
   ipv4.tos = 0;

   /* Default value of Time to Live (TTL) field in IPv4 header of outgoing 
    * packets 
    */
   ipv4.ttl = 64;

   /* TCP CONFIGURATION   */

   /* The maximum time (in seconds) that TCP will wait for a response to a 
    * connection request before returning an error 
    */
   tcp.conn_estab_tmo = 75;

   /* Maximum Segment Lifetime (MSL, units of seconds) */
   tcp.msl = 2;

   /* Enable/disable the transmission of a TCP RST segment when a connection 
    * request is received for a non-existent TCP port
    */
   tcp.enable_noport_rst = TRUE;

   /* Maximum amount of data that can be queued in the receive buffer of 
    * a TCP socket
    */
   tcp.recv_space = 3000;

   /* Maximum amount of data that can be queued in the send buffer of 
    * a TCP socket 
    */
   tcp.send_space = 3000;

   /* Length of time (in seconds) that a (keepalive-enabled) TCP session 
    * must be idle before TCP will start sending keepalive probes
    */
   tcp.kal.idle_time = 600;

   /* Maximum number of TCP keepalive probes that will be sent before the 
    * connection will be closed
    */
   tcp.kal.num_probes = 4;

   /* Time (in seconds) between TCP keepalive probes */
   tcp.kal.probe_interval = 15;


   /* UDP CONFIGURATION PARAMETERS */

   /* Enable/disable the transmission of an ICMP Destination Unreachable
    * (Port Unreachable) message when a UDP/IPv4 datagram is received for
    * a non-existent UDP port
    */
   udp.enable_dest_unreach = TRUE;

   /* Maximum amount of data that can be queued waiting to be read in the
    * receive buffer of a UDP socket.  It also limits the maximum size of 
    * a UDP message that can be received.
    */
   udp.rcvbuf_size = 4096;   
}


/* The maximum number of tasks that the user will have active at any
 * one time. This value is used to size some internal arrays */
int max_user_tasks = 1;

/* The FreeRTOS task priorities of the core Stack tasks.
 * These priorities can be adjusted, but the relative priorites
 * between the task should be preserved.
 */
uint8_t  netmain_priority = 5;
uint8_t  nettick_priority = 3;
uint8_t  console_priority = 2;

/* Stack sizes (in bytes) for the core Stack tasks.
 */
uint16_t netmain_stksize = 1024;
uint16_t nettick_stksize = 1536;
uint16_t console_stksize = 1024;

/*
 * free_rtos_tickrate_hz is the "tick" rate of the FreeRTOS operating
 * system. This variable should ALWAYS be set to the configTICK_RATE_HZ
 * constant defined in FreeRTOSConfig.h.
 */
int32_t free_rtos_tickrate_hz = configTICK_RATE_HZ;

/*
 * eth_segment_size is the size of the RX packet segments used by the
 * ethernet driver. eth_segment_size should be equal to the size of
 * one of the free buffer pools.
 */
uint16_t eth_segment_size = 512;

/*
 * eth_threshhold is the miniumum amount of local RX buffer space
 * maintained by the ethernet driver. Increase this value if the
 * ethernet driver cannot keep up with the incoming packet rate.
 */
uint16_t eth_threshhold = (6*512);

/*
 * The number of RX and TX descriptors allocated by the driver.
 * It is recommended that:
 *    eth_rx_num * eth_segment_size >= 4096
 *    eth_tx_num > 13
 * Increasng the number of RX descriptors may be necessary for
 * networks with a lot of traffic.
 */
uint16_t eth_rx_num = 8;
uint16_t eth_tx_num = 16;


/* FUNCTION: upnp_callback()
 *
 * This function is the registered callback handler for the interface IPv4 
 * address acquisition manager.
 *
 * PARAM1: char * - interface name
 * PARAM2: int - status indicating the output of the address acquisition
 *               process (one of the following four values: UPNP_ZERO_ADDR, 
 *               UPNP_STATIC_ADDR, UPNP_AUTOCONF_ADDR, UPNP_DHCP_ADDR)
 *
 * RETURN: none
 */
void
upnp_callback(char *name, int status)
{
   printf("upnp_callback: interface %s: status=%d\n", name, status);
}


/* FUNCTION: dtrap()
 *
 * Fatal error handler
 *
 * PARAMS: none
 *
 * RETURN: none
 */

void
dtrap(void)
{
   printf("dtrap\n");
   while (1) {};
}



/* FUNCTION: panic()
 *
 * Fatal system error
 *
 * Called if Software detects fatal system error. msg is a string 
 * describing problem. There is no return from this routine. In a 
 * testing or development environment, it should print messages, hook 
 * debuggers, etc.  In an embedded controller, it should probably try
 * to restart (ie warmboot) the system.
 *
 * PARAM1: char *             error message
 *
 * RETURN: none
 */
void
panic(char *msg)
{
   printf("panic: %s\n", msg);
   dtrap();                   /* try to hook debugger */
   exit(1);                   /* try to clean up */
}



/* FUNCTION: exit()
 *
 * Program exit function.
 *
 * PARAM1: int                exit code
 *
 * RETURN: none
 */
void
exit(int code)
{
   printf("exit %d\n", code);
   _sys_exit(code);
}



/* FUNCTION: get_mac_address()
 *
 * Copy the MAC address of the specified device into the caller's
 * buffer.
 *
 * PARAM1: int                network interface index (0..N-1)
 * PARAM2: uint8_t *          MAC buffer
 *
 * RETURN: int                0 = success, non-zero = error code
 */

static uint8_t local_mac[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

int
get_mac_address(int iface, uint8_t *emac)
{
   if (iface == 0)
   {
      memcpy(emac, &local_mac[0], 6);
      return (0);
   }
   else
   {
      dtrap();                /* unsupported interface index */
      return (-1);
   }
}



/* FUNCTION: user_pre_setup()
 *
 * Hook for user's pre_task initialization.  This function copies
 * the user-specified configuration parameters into the TCP/IP
 * stack.
 *
 * Note. This section needs to be updated to include additional information 
 * on the state of the system when this function executes.
 *
 * PARAMS: none
 *
 * RETURN: int                0 = success, < 0 = error code
 */
int
user_pre_setup(void)
{
   /* update configuration parameters in TCP/IP stack */
   embTCP_config();
   in_cfgstack();

   /* setup IPv4 address, subnet mask, gateway, and DHCP config flags */
   in_v4addrcfg("et1", "0.0.0.0", "0.0.0.0", "0.0.0.0", NETF_DHCPC|NETF_AUTOIP);
/* in_v4addrcfg("et1", "10.0.0.100", "255.255.255.0", "10.0.0.1", 0); */

   return (0);
}

/* FUNCTION: user_post_setup()
 *
 * Hook for user's post_task initialization.  This function 
 * initializes the DNS client and ping application modules.
 *
 * Note. This section needs to be updated to include additional information 
 * on the state of the system when this function executes.
 *
 * PARAMS: none
 *
 * RETURN: int                0 = success, < 0 = error code
 */
int
user_post_setup(void)
{
   int rc;
   /* in_debug = UPCTRACE | IPTRACE | TPTRACE; */

   /* initialize the DNS client */
   rc = dnc_init();
   if (rc != ESUCCESS)
   {
      printf("user_post_setup: failed to initialize DNS client\n");
      return (EFAILURE);
   }
   /* initialize the ping application (for outbound pings) */
   rc = ping_init();
   if (rc != ESUCCESS)
   {
      printf("user_post_setup: failed to initialize ping application\n");
      return (EFAILURE);
   }

   return (ESUCCESS);
}

/* 
 * list of options (e.g., -a, -b, ...) allowed with the "user1" CLI command
 */
struct cli_parm user1_parms[] = {
   { 'a', CLI_IPADDR },       /* parameter of type IPv4 address */
   { 'b', CLI_NONE },         /* parameter with no <argument> */
   { 'i', CLI_INT },          /* parameter of type integer */
   { 's', CLI_STRING },       /* parameter of type string */
   { 'u', CLI_UINT },         /* parameter of type unsigned integer */
};

/* FUNCTION: user1_cli()
 *
 * Example implementation of CLI routine (for the "user1" CLI command).
 * This functions displays the values of the various arguments passed
 * to the "user1" CLI command.
 *
 * PARAM1: CLI_CTX            CLI context
 *
 * RETURN: int                ESUCCESS if successful, otherwise error code
 *                            from the CLI_ERR_xxx family of values
 *
 * Useage: user1 -a IPADDR -b -i INT -s STRING -u UINT
 * 
 * -a: option of type IPv4 address (e.g., -a 7.7.7.7)
 * -b: boolean option (takes no arguments) (e.g., -b)
 * -i: option of type (signed) integer (e.g., -i -7)
 * -s: option of type string (e.g., -s "seven" or -s seven)
 * -u: option of type unsigned integer (e.g., -u 7)
 */
int
user1_cli(void *ctx)
{
   bool_t opt_n;
   char *str;
   int i;
   uint32_t u32i;
   struct cli_addr *caddr4p;
   ip_addr addr4;

   if (!CLI_HELP(ctx))
   {
      /* process IPv4 address option (-a) */
      if (CLI_DEFINED(ctx, 'a'))
      {
         /* extract the IPv4 multicast group address */
         caddr4p = (struct cli_addr *)CLI_VALUE(ctx, 'a');
         if (caddr4p->type == CLI_IPV4)
         {
            addr4 = *((ip_addr *)&caddr4p->addr[0]);
            printf("Option -a (IPv4 address): %u.%u.%u.%u\n", 
                            PUSH_IPADDR(addr4));
         }
         else
         {
            printf("CLI: can't parse address for option -a\n");
            return (CLI_ERR_TYPE);
         }
      }

      /* process integer option (-i) */
      if (CLI_DEFINED(ctx, 'i'))
      {
         i = (int)CLI_VALUE(ctx, 'i');
         printf("Option -i (signed integer): %d\n", i);
      }

      /* process boolean option (-b) */
      opt_n = CLI_DEFINED(ctx, 'b');
      printf("Option -b (boolean): %sabled\n", opt_n ? "en" : "dis");

      /* process string option (-s) */
      if (CLI_DEFINED(ctx, 's'))
      {
         str = (char *)CLI_VALUE(ctx, 's');
         printf("Option -s (string): %s\n", str);
      }

      /* process unsigned integer option (-u) */
      if (CLI_DEFINED(ctx, 'u'))
      {
         u32i = (int)CLI_VALUE(ctx, 'u');
         printf("Option -u (unsigned integer): %lu\n", u32i);
      }
   }

   return (ESUCCESS);
}

/* flags to include individual CLI commands in a build.  To include a
 * command, set the value of the corresponding #define to 1.  To exclude
 * a command, set the value to any other value (e.g., 0).
 */
#define INICHE_CLICMD_HELP      1
#define INICHE_CLICMD_DHCSTAT   1
#define INICHE_CLICMD_IFACE     1
#define INICHE_CLICMD_LINKSTATS 1
#define INICHE_CLICMD_NETSTAT   1
#define INICHE_CLICMD_QUEUES    1
#define INICHE_CLICMD_SETIP     1
#define INICHE_CLICMD_STATUS    1 
#define INICHE_CLICMD_ARP       1
#define INICHE_CLICMD_SETDNSSRV 1
#define INICHE_CLICMD_NSLOOKUP  1
#define INICHE_CLICMD_PING      1
#define INICHE_CLICMD_USER1     1

/* 
 * Table of CLI commands
 */
struct cli_cmd net_cmds[] = {
#if INICHE_CLICMD_HELP == 1
   {
      "help",
      "get command help",
      &cli_nt_help,
      sizeof(help_parms)/sizeof(help_parms[0]),
      &help_parms[0],
   },
#endif
#if INICHE_CLICMD_DHCSTAT == 1
   {
      "dhcstat",
      "display DHCP client statistics and status",
      &dhcpc_netstat,
      0,
      NULL
   },
#endif
#if INICHE_CLICMD_IFACE == 1
   {
      "iface",
      "display interface status",
      &netmain_iface,
      sizeof(iface_parms)/sizeof(struct cli_parm),
      &iface_parms[0],
   },
#endif
#if INICHE_CLICMD_LINKSTATS == 1
   {
      "linkstats",
      "display link statistics",
      &netmain_linkstats,
      sizeof(linkstats_parms)/sizeof(struct cli_parm),
      &linkstats_parms[0],
   },
#endif
#if INICHE_CLICMD_NETSTAT == 1
   {
      "netstat",
      "display network status",
      &netmain_netstat,
      sizeof(netstat_parms)/sizeof(struct cli_parm),
      &netstat_parms[0],
   },
#endif
#if INICHE_CLICMD_QUEUES == 1
   {
      "queues",
      "display packet buffer, mbuf, and receive queue stats",
      &dumpqueuesCTX,
      sizeof(queues_parms)/sizeof(queues_parms[0]),
      &queues_parms[0],
   },
#endif
#if INICHE_CLICMD_SETIP == 1
   {
      "setip",
      "set network interface parameters",
      &netmain_setip,
      sizeof(setip_parms)/sizeof(struct cli_parm),
      &setip_parms[0],
   },
#endif
#if INICHE_CLICMD_STATUS == 1
   {
      "status",
      "display system status",
      &netmain_status,
      sizeof(status_parms)/sizeof(struct cli_parm),
      &status_parms[0],
   },
#endif
#if INICHE_CLICMD_ARP == 1
   {
      "arp",
      "display ARP table and statistics",
      &netdiag_arp,
      sizeof(arp_parms)/sizeof(struct cli_parm),
      &arp_parms[0],
   },
#endif
#if INICHE_CLICMD_SETDNSSRV == 1
   {
      "setdnssrv",
      "set DNS server address",
      &netdiag_dnssvr,
      sizeof(dnssvr_parms)/sizeof(struct cli_parm),
      &dnssvr_parms[0],
   },
#endif
#if INICHE_CLICMD_NSLOOKUP == 1
   {
      "nslookup",
      "lookup IPv4 address for host, or dump DNS client status",
      &netdiag_nslookup,
      sizeof(nslookup_parms)/sizeof(struct cli_parm),
      &nslookup_parms[0],
   },
#endif
#if INICHE_CLICMD_PING == 1
   {
      "ping",
      "send ICMP/IPv4 Echo (ping) to host",
      &ping_cli,
      sizeof(ping_parms)/sizeof(struct cli_parm),
      &ping_parms[0],
   },
#endif
#if INICHE_CLICMD_USER1 == 1
   {
      "user1",
      "user-defined CLI command",
      &user1_cli,
      sizeof(user1_parms)/sizeof(struct cli_parm),
      &user1_parms[0],
   },
#endif
};

/* 
 * CLI menu 
 */
struct cli_menu netmain_nt = {
   "embtcp",
   sizeof(net_cmds)/sizeof(struct cli_cmd),
   &net_cmds[0],
};
