view src/g23m-fad/app/app_core.c @ 291:a72feaed133a

PWT buzzer driver: conditionalize on TARGET_HAS_BUZZER Here we add a new target config preprocessor symbol TARGET_HAS_BUZZER, currently defined for c11x and c139; later we also expect to have it on for FC Venus, and in FC Magnetite we are going to turn it on for target dsample, just for the sake of completeness and philosophical correctness.
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 26 Mar 2022 19:31:18 +0000
parents fa8dc04885d8
children
line wrap: on
line source

/*-*- c-basic-offset: 2 -*-
+------------------------------------------------------------------------------
|  File:       app_core.c
+------------------------------------------------------------------------------
|  Copyright 2003 Texas Instruments Berlin, AG
|                 All rights reserved.
|
|                 This file is confidential and a trade secret of Texas
|                 Instruments Berlin, AG
|                 The receipt of or possession of this file does not convey
|                 any rights to reproduce or disclose its contents or to
|                 manufacture, use, or sell anything it may describe, in
|                 whole, or in part, without the specific written consent of
|                 Texas Instruments Berlin, AG.
+-----------------------------------------------------------------------------
|  Purpose :  Example application for TCP/IP and Socket API -- core functions.
+-----------------------------------------------------------------------------
*/


/* This should only be compiled into the entity if TCP/IP is enabled */
#ifdef FF_GPF_TCPIP

#define APP_CORE_C

#define ENTITY_APP

/*==== INCLUDES =============================================================*/

#include <string.h>             /* String functions, e. g. strncpy(). */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef _SIMULATION_
#include "typedefs.h"           /* Condat data types. */
#endif /* _SIMULATION_ */
#include "vsi.h"                /* A lot of macros. */
#ifndef _SIMULATION_
#include "custom.h"
#include "gsm.h"                /* A lot of macros. */
#include "prim.h"               /* Definitions of used SAP and directions. */
#include "pei.h"                /* PEI interface. */
#include "tools.h"              /* Common tools. */
#endif /* _SIMULATION_ */
#include "socket_api.h"             /* Socket API. */
#include "app.h"                /* Global entity definitions. */


/*==== Local data ============================================================*/

#define NPROCS 1                /* Maximum number of application processes. */

#define PORT_CHARGEN 19         /* Chargen service for download. */
#define PORT_ECHO     7         /* Echo port for tcpecho and udpecho. */
#define PORT_DISCARD  9         /* Discard port for upload. */

#define FQDN_LENGTH 255         /* Maximum length of a fully-qualified domain
                                 * name. */

#undef HTONS
#define HTONS(a) ((((a) & 0xff) << 8) | (((a) & 0xff00) >> 8))
#undef NTOHS
#define NTOHS(a) HTONS(a)
#define MIN(a, b) ((a) < (b) ? (a) : (b))

/* We can run different types of application processes, according to the
 * commend sent by the user. */
typedef enum {
  AP_NONE,                      /* For uninitialized process types. */
  AP_TCPDL,                     /* Download some data over TCP. */
  AP_TCPUL,                     /* Upload some data over TCP. */
  AP_UDPDL,                     /* Download some data over UDP. */
  AP_UDPUL,                     /* Upload some data over UDP. */
  AP_TCPECHO,                   /* Send/receive data to/from TCP echo port. */
  AP_UDPECHO,                   /* Send/receive data to/from UDP echo port. */
  AP_TCPSRV,                    /* TCP server application. */
  AP_DNSQRY,                    /* Issue DNS queries and collect result. */
  AP_TCPFORK,                   /* Forked TCP server process. */
  AP_INVALID
} APP_PROCTYPE_T ;

/* Strings for process types; used for debugging and MUST correspond strictly
 * to the process type enum labels defined above. */
static char *proc_type_name[] = {
  "AP_NONE",                    /* 00 */
  "AP_TCPDL",                   /* dl */
  "AP_TCPUL",                   /* ul */
  "AP_UDPDL",
  "AP_UDPUL",
  "AP_TCPECHO",                 /* te */
  "AP_UDPECHO",                 /* ue */
  "AP_TCPSRV",
  "AP_DNSQRY",                  /* dq */
  "AP_TCPFORK",
  "AP_INVALID"
} ;

/* Process states; the state transitions are mostly linear in this order. */
typedef enum {
  PS_IDLE,                      /* Initial state, process not running. */
  PS_W_DCM_OPEN,                /* Waiting for DCM to open connection. */
  PS_W_DCM_OPEN_ONLY,           /* Waiting for DCM to open connection - no further action. */
  PS_W_CREAT,                   /* Waiting for socket create confirmation. */
  PS_W_SCONN,                   /* Waiting for socket connect confirmation. */
  PS_W_BIND,                    /* Waiting for socket bind confirmation. */
  PS_W_LISTN,                   /* Waiting for confirmation of listen call. */
  PS_LISTENS,                   /* Listens for client connections. */
  PS_W_DNS,                     /* Waiting for a DNS query. */
  PS_COMM,                      /* Happily exchanging data. */
  PS_W_SCLOS,                   /* Waiting for socket close confirmation. */
  PS_W_DCLOS,                   /* Waiting for DCM to close connection. */
  PS_W_CONN_INFO,               /* Waiting for connection information */
  PS_DCM_OPEN,                  /* DCM (bearer) connecion opened*/
  PS_SOCK_OPEN,                 /* Socket and bearer open */
  PS_INVALID
} PROC_STAT_T ;

/* Strings for the process states; used for debugging and MUST correspond
 * strictly to the process state enum labels defined above, as the array is
 * indexed by those. */
static char *proc_state_name[] = {
  "PS_IDLE",
  "PS_W_DCM_OPEN",
  "PS_W_DCM_OPEN_ONLY",
  "PS_W_CREAT",
  "PS_W_SCONN",
  "PS_W_BIND",
  "PS_W_LISTN",
  "PS_W_LISTENS",
  "PS_W_DNS",
  "PS_COMM",
  "PS_W_SCLOS",
  "PS_W_DCLOS",
  "PS_W_CONN_INFO",
  "PS_DCM_OPEN",
  "PS_SOCK_OPEN",
  "PS_INVALID"
} ;

/* The data a process holds. May be dynamically allocated in the future. */
typedef struct PROCESS_CONTEXT_S {
  APP_PROCTYPE_T ptype ;        /* Type of application process */
  PROC_STAT_T pstate ;          /* Process status as defined above. */
  int in_shutdown ;             /* Non-zero iff process is being shut down. */
  T_SOCK_EVENTSTRUCT *last_evt; /* Last event passed from the Socket API. */
  T_SOCK_IPPROTO ipproto ;      /* IP protocol number for this process (TCP or
                                 * UDP); unused with dq. */
  char *server_name ;           /* May be a domain name or an IP address in
                                 * dotted decimal notation. */
  T_SOCK_IPADDR server_ipaddr ; /* Server IP address. (Will be IPADDR_ANY in
                                 * case of AP_TCPSRV.) */
  T_SOCK_PORT server_port ;     /* Server port number. (Also in case of
                                 * AP_TCPSRV.) */

  /* The following variables are in use only where appropriate, of course --
   * as indicated in the comment. */

  int f_id ;                    /* Identity of TCP server fork. */
  int spec_items ;              /* Specified number of items to transfer. (The
                                 * items are single bytes for dl and ul.) */
  int spec_reps ;               /* Specified number of repetitions. */

  int data_sent ;               /* Total amount of data sent (ul, te, ue). */
  int data_rcvd ;               /* Total amount of data recvd (dl, te, ue). */
  int items_sent ;              /* Number of blocks/packets/queries sent (ul,
                                 * te, ue, dq). */
  int items_rcvd ;              /* Number of blocks/packets/responses received
                                 * (dl, te, ue, dq). */
  int n_reps ;                  /* Number of repetitions done. */
  int errors ;                  /* Number of errors at all. */
  T_SOCK_SOCKET psocket ;       /* The socket in use by the process. */
  int network_is_open ;         /* Non-zero iff we have an open network
                                 * connection. */
  int psocket_is_open ;         /* Non-zero iff we have an open psocket. */
  BOOL bearer_only;             /* if set, only a Bearer will be opened */ 
} PROC_CONTEXT_T ;

static PROC_CONTEXT_T proc_context ;
static PROC_CONTEXT_T cl_context[APP_N_CLIENTS] ;
static char server_name[FQDN_LENGTH+1] = APP_DEF_SERVER ;
                                /* Global server name. */
static char query_name[FQDN_LENGTH+1] = APP_DEF_DNSQUERY_ADDR ;
static int buffer_size = APP_DEF_BUFFER_SIZE ;
static U16 port_number = 0 ;    /* Port number override if non-zero. */
static int udp_interval = APP_DEF_UDP_INTERVAL ;
static T_SOCK_BEARER_TYPE bearer_select    = SOCK_BEARER_AS_SPECIFIED;
static T_SOCK_BEARER_TYPE sock_bearer_type = SOCK_BEARER_GPRS;
EXTERN BOOL custom_apn_valid;


/* The cache for DNS queries is RNET_RT_RESOLV_CACHE_MAX queries big, i. e. 8
 * in the current configuration. We need to overflow this cache in order to
 * test lookup robustness. */
static char *domain_name[] = {
#ifdef _SIMULATION_              /* Not in the simulation, though. */
  "chuck.berlin.tide.ti.com",
#else  /* _SIMULATION_ */
  "gsmtest.com",
  "w21.org",
  "gw.w21.org",
  "troll.cs.tu-berlin.de",
  "gatekeeper.dec.com",
  "www.mcvax.org",
  "www.mcvaymedia.com",
  "www.vodafone.de",
  "www.ti.com",
  "mailbox.tu-berlin.de",
  "ge-2-3-0.r02.asbnva01.us.bb.verio.net",
  "www.condat.de",
  "www.tellique.de",
  "prz.tu-berlin.de",
#endif /* _SIMULATION_ */
  0
} ;



/*==== Local functions =======================================================*/

/*
 * Utility functions.
 */


static char *sock_bearer_type_string(T_SOCK_BEARER_TYPE btype)
{
  switch (btype)
  {
    case SOCK_BEARER_ANY: return "SOCK_BEARER_ANY" ;
    case SOCK_BEARER_GPRS: return "SOCK_BEARER_GPRS" ;
    case SOCK_BEARER_GSM: return "SOCK_BEARER_GSM" ;
    case SOCK_BEARER_USE_PROFILE: return "SOCK_BEARER_USE_PROFILE" ;
    case SOCK_BEARER_AS_SPECIFIED: return "SOCK_BEARER_AS_SPECIFIED" ;
    default: return "<unknown bearer type>" ;
  }
}

/** Give a print representation for the specified character. This is the
 * character itself for printable characters and a substitution character for
 * others.
 *
 * @param c    the character
 * @return     the print representation
 */
static char p_char(char c)
{
  return isprint(c) ? c : '~' ;
}


#define DUMP_LLENGTH 16         /* Data dump line length. */
#define DBUF_LENGTH (4 * DUMP_LLENGTH + 10) /* See example below. */
#define XDIGIT(n) ("0123456789abcdef"[n])

/** Dump the specified portion of the data as event traces like this:
 * 0000: 27 28 29 2a 2b 2c 2d 2e-2f 30 31 32 33 34 35 36 ['()*+,-./0123456]
 *
 * @param data    Pointer to data area
 * @param size    Size of data to dump
 */
void trace_dump_data(U8 *data, int size)
{
  char dump_buf[DBUF_LENGTH] ;  /* Buffer to dump a line into. */
  int lcount = 0 ;              /* Line count. */
  int i ;                       /* Index into data. */
  char *cp ;                    /* Pointer to current char in dump_buf[]. */

  while (size > 0)
  {
    cp = dump_buf ;
    /* Hex values. */
    for (i = 0; i < DUMP_LLENGTH && i < size; i++)
    {
      *cp++ = XDIGIT(data[i] >> 4) ;
      *cp++ = XDIGIT(data[i] & 0xf) ;
      *cp++ = (i == DUMP_LLENGTH/2 - 1) ? '-' : ' ' ;
    }

    /* Fill up with blanks. */
    for ( ; i < DUMP_LLENGTH; i++)
    {
      *cp++ = ' ' ; *cp++ = ' ' ; *cp++ = ' ' ;
    }

    /* Literal characters with some decoration. */
    *cp++ = '[' ;
    for (i = 0; i < DUMP_LLENGTH && i < size; i++, cp++)
    {
      *cp = p_char(data[i]) ;
    }
    *cp++ = ']' ;
    *cp++ = 0 ;
    TRACE_EVENT_P2("%04x: %s", DUMP_LLENGTH * lcount++, dump_buf) ;
    size -= DUMP_LLENGTH ;
    data += DUMP_LLENGTH ;
  }
}


/** Build a string, characterizing a process, suitable for tracing. The string
 * is statically allocated and will be overwritten with the next call.
 *
 * @param pcont    Pointer to process context.
 * @return         The string.
 */
char *proc_string(PROC_CONTEXT_T *pcont)
{
  /* This string must fit the longest possible process string. */
  static char procstring[sizeof("AP_TCPFORK99(PS_W_DCM_OPEN_ONLY)")] ;

  /*lint -e685 (Warning -- Relational operator always evaluates to true) */
  sprintf(procstring, "%s%d(%s)",
          pcont->ptype <= AP_INVALID  ? proc_type_name[pcont->ptype] : "AP_UNKNOWN",
          pcont->ptype == AP_TCPFORK  ? pcont->f_id : 0,
          pcont->pstate <= PS_INVALID ? proc_state_name[pcont->pstate] : "PS_UNKNOWN") ;
  /*lint +e685 (Warning -- Relational operator always evaluates to true) */
  return procstring ;
}


/** Converts a numeric IP address in network order into an IP address in
 * dotted decimal string notation. The string returned is statically allocated
 * and will be overwritten on the next call.
 *
 * @param ipaddr    The IP address in network order
 * @return String with the IP address in dotted decimal..
 */
static char *inet_ntoa(T_SOCK_IPADDR ipaddr)
{
  U8 *addrbyte ;
  static char addr_string[sizeof("000.000.000.000")] ;

  addrbyte = (U8 *) &ipaddr ;
  sprintf(addr_string, "%u.%u.%u.%u",
          addrbyte[0], addrbyte[1], addrbyte[2], addrbyte[3]) ;
  return addr_string ;
}


/** Converts an IP address in dotted decimal string notation into a numeric IP
 * address in network order.
 *
 * @param addr_string    String with the IP address in dotted decimal.
 * @return The IP address in network order, or SOCK_IPADDR_ANY if the address
 *         string cannot be parsed.
 */
static T_SOCK_IPADDR inet_aton(char *addr_string)
{
  T_SOCK_IPADDR ipaddr ;
  U8 *addrbyte ;
  int o1, o2, o3, o4 ;

  if (sscanf(addr_string, "%d.%d.%d.%d", &o1, &o2, &o3, &o4) != 4)
  {
    TRACE_EVENT_P1("cannot parse '%s' as an IP address", addr_string) ;
    return SOCK_IPADDR_ANY ;
  }
  addrbyte = (U8 *) &ipaddr ;
  addrbyte[0] = (U8) o1 ;
  addrbyte[1] = (U8) o2 ;
  addrbyte[2] = (U8) o3 ;
  addrbyte[3] = (U8) o4 ;

  return ipaddr ;
}


LOCAL char *sock_result_string(T_SOCK_RESULT result)
{
  switch(result)
  {
    case SOCK_RESULT_OK                : return "SOCK_RESULT_OK";
    case SOCK_RESULT_INVALID_PARAMETER : return "SOCK_RESULT_INVALID_PARAMETER"; 
    case SOCK_RESULT_INTERNAL_ERROR    : return "SOCK_RESULT_INTERNAL_ERROR";
    case SOCK_RESULT_ADDR_IN_USE       : return "SOCK_RESULT_ADDR_IN_USE";
    case SOCK_RESULT_OUT_OF_MEMORY     : return "SOCK_RESULT_OUT_OF_MEMORY";
    case SOCK_RESULT_NOT_SUPPORTED     : return "SOCK_RESULT_NOT_SUPPORTED";
    case SOCK_RESULT_UNREACHABLE       : return "SOCK_RESULT_UNREACHABLE";
    case SOCK_RESULT_CONN_REFUSED      : return "SOCK_RESULT_CONN_REFUSED";
    case SOCK_RESULT_TIMEOUT           : return "SOCK_RESULT_TIMEOUT";
    case SOCK_RESULT_IS_CONNECTED      : return "SOCK_RESULT_IS_CONNECTED";
    case SOCK_RESULT_HOST_NOT_FOUND    : return "SOCK_RESULT_HOST_NOT_FOUND";
    case SOCK_RESULT_DNS_TEMP_ERROR    : return "SOCK_RESULT_DNS_TEMP_ERROR";
    case SOCK_RESULT_DNS_PERM_ERROR    : return "SOCK_RESULT_DNS_PERM_ERROR";
    case SOCK_RESULT_NO_IPADDR         : return "SOCK_RESULT_NO_IPADDR";
    case SOCK_RESULT_NOT_CONNECTED     : return "SOCK_RESULT_NOT_CONNECTED";
    case SOCK_RESULT_MSG_TOO_BIG       : return "SOCK_RESULT_MSG_TOO_BIG";
    case SOCK_RESULT_CONN_RESET        : return "SOCK_RESULT_CONN_RESET";
    case SOCK_RESULT_CONN_ABORTED      : return "SOCK_RESULT_CONN_ABORTED";
    case SOCK_RESULT_NO_BUFSPACE       : return "SOCK_RESULT_NO_BUFSPACE";
    case SOCK_RESULT_NETWORK_LOST      : return "SOCK_RESULT_NETWORK_LOST";
    case SOCK_RESULT_NOT_READY         : return "SOCK_RESULT_NOT_READY";
    case SOCK_RESULT_BEARER_NOT_READY  : return "SOCK_RESULT_BEARER_NOT_READY";
    case SOCK_RESULT_IN_PROGRESS       : return "SOCK_RESULT_IN_PROGRESS";
    case SOCK_RESULT_BEARER_ACTIVE     : return "SOCK_RESULT_BEARER_ACTIVE";
    default                            : return "<INVALID SOCKET RESULT!>";
  }
}



/** Trace a specific socket API result code with some context.
 *
 * @param pcont        Pointer to process context.
 * @param function     The function or event that reported the error.
 * @param result       Socket API result code.
 * @return
 */
static void sock_trace_result(PROC_CONTEXT_T *pcont, char *function,
                              T_SOCK_RESULT result)
{
  if(result NEQ SOCK_RESULT_OK)
  {
    TRACE_ERROR("Sock Result Error");
  }
  TRACE_EVENT_P3("%s: %s for %s", function, 
                 sock_result_string(result), proc_string(pcont));
}


/** Return the string for a Socket API event type. We don't have the values
 * under our (i. e. APP's) own control, so we rather do a switch than indexing
 * an array.
 *
 * @param event_type    Type of the event.
 * @return              String for the event type.
 */
char *sock_event_string(T_SOCK_EVENTTYPE event_type)
{
  switch (event_type)
  {
    case SOCK_CREATE_CNF:        return "SOCK_CREATE_CNF" ;
    case SOCK_CLOSE_CNF:         return "SOCK_CLOSE_CNF" ;
    case SOCK_BIND_CNF:          return "SOCK_BIND_CNF" ;
    case SOCK_LISTEN_CNF:        return "SOCK_LISTEN_CNF" ;
    case SOCK_CONNECT_CNF:       return "SOCK_CONNECT_CNF" ;
    case SOCK_SOCKNAME_CNF:      return "SOCK_SOCKNAME_CNF" ;
    case SOCK_PEERNAME_CNF:      return "SOCK_PEERNAME_CNF" ;
    case SOCK_HOSTINFO_CNF:      return "SOCK_HOSTINFO_CNF" ;
    case SOCK_MTU_SIZE_CNF:      return "SOCK_MTU_SIZE_CNF" ;
    case SOCK_RECV_IND:          return "SOCK_RECV_IND" ;
    case SOCK_CONNECT_IND:       return "SOCK_CONNECT_IND" ;
    case SOCK_CONN_CLOSED_IND:   return "SOCK_CONN_CLOSED_IND" ;
    case SOCK_ERROR_IND:         return "SOCK_ERROR_IND" ;
    case SOCK_FLOW_READY_IND:    return "SOCK_FLOW_READY_IND" ;
    case SOCK_OPEN_BEARER_CNF:   return "SOCK_OPEN_BEARER_CNF";
    case SOCK_CLOSE_BEARER_CNF:  return "SOCK_CLOSE_BEARER_CNF";
    case SOCK_BEARER_INFO_CNF:   return "SOCK_BEARER_INFO_CNF";
    case SOCK_BAERER_CLOSED_IND: return "SOCK_BAERER_CLOSED_IND";
    default:                     return "<INVALID EVENT>" ;
  }
}



/*
 * Process functions.
 */

static void proc_shutdown(PROC_CONTEXT_T *pcont) ;
static void proc_close_socket(PROC_CONTEXT_T *pcont) ;
static void proc_begin_comm(PROC_CONTEXT_T *pcont) ;
static void proc_close_conn(PROC_CONTEXT_T *pcont) ;


/** Switch process to a new state. Done mostly to have a single place to trace
 * process state transitions.
 *
 * @param pcont       Pointer to process context.
 * @param newstate    New state of process.
 */
static void proc_new_state(PROC_CONTEXT_T *pcont, PROC_STAT_T newstate)
{
  if (newstate < PS_INVALID)
  {
    TRACE_EVENT_P2("%s -> %s", proc_string(pcont), proc_state_name[newstate]) ;
    pcont->pstate = newstate ;
  }
  else
  {
    TRACE_EVENT_P2("%s invalid new state %d", proc_string(pcont), newstate) ;
    proc_shutdown(pcont) ;
  }
}


/** Fork a new TCP server process context to handle a TCP client. Return a
 * pointer to the process context or NULL, if no process context is free any
 * more.
 *
 */
static PROC_CONTEXT_T *proc_new_tcpfork(PROC_CONTEXT_T *oldp)
{
    int i ;
    PROC_CONTEXT_T *pcont ;

    TRACE_FUNCTION("proc_new_tcpfork()") ;
    for (i = 0; i < APP_N_CLIENTS; i++)
    {
        if (cl_context[i].ptype EQ AP_NONE)
        {
            break ;
        }
    }
    if (i == APP_N_CLIENTS)
    {
        return NULL ;
    }

    pcont = &cl_context[i] ;
    memset(pcont, 0, sizeof(*pcont)) ;
    pcont->f_id = i ;
    pcont->ptype = AP_TCPFORK ;
    pcont->pstate = PS_IDLE ;
    pcont->ipproto = oldp->ipproto ;
    pcont->server_name = oldp->server_name ;
    pcont->server_ipaddr = oldp->server_ipaddr ;
    pcont->server_port = oldp->server_port ;
    pcont->network_is_open = TRUE ;
    pcont->psocket_is_open = TRUE ;

    return pcont ;
}

/** Free a TCP server process context.
 *
 */
static void proc_free_tcpfork(PROC_CONTEXT_T *pcont)
{
    TRACE_FUNCTION("proc_free_tcpfork()") ;
    proc_new_state(pcont, PS_IDLE) ;
    memset(pcont, 0, sizeof(*pcont)) ;
}


static void proc_init(int prov, int size, int reps, APP_PROCTYPE_T ptype,
                      T_SOCK_IPPROTO ipproto, U16 port)
{
  T_SOCK_BEARER_INFO bearer_info;
  PROC_CONTEXT_T *pcont ;
  BOOL bear_only = proc_context.bearer_only;

  TRACE_FUNCTION("proc_init()") ;

  pcont = &proc_context ;
  if (pcont->pstate != PS_IDLE)
  {
    TRACE_ERROR("proc_init: process still active") ;
    return ;
  }
  memset(pcont, 0, sizeof(*pcont)) ;
  pcont->bearer_only = bear_only;
  pcont->ptype = ptype ;
  pcont->ipproto = ipproto ;
  pcont->server_name = (ptype EQ AP_TCPSRV) ? "<myself>" : server_name ;
  pcont->server_ipaddr = (ptype EQ AP_TCPSRV) ?
    SOCK_IPADDR_ANY : inet_aton(pcont->server_name) ;
  pcont->server_port = HTONS(port) ;
  pcont->spec_items = size ;
  pcont->spec_reps = reps ;
  pcont->in_shutdown = FALSE;

  pcont->psocket = 0;
  pcont->network_is_open = FALSE;
  pcont->psocket_is_open = FALSE;

  TRACE_EVENT_P7("%s for %d bytes %d reps, server %s:%d/%s on %s",
                 proc_string(pcont), pcont->spec_items, pcont->spec_reps,
                 inet_ntoa(pcont->server_ipaddr), NTOHS(pcont->server_port),
                 (ipproto EQ SOCK_IPPROTO_UDP) ? "udp" : "tcp",
                 sock_bearer_type_string(sock_bearer_type)) ;
  app_pstat();

  // fill connection params
  bearer_info.bearer_handle = sock_bearer_handle;
  bearer_info.app_handle    = APP_handle;
  bearer_info.bearer_type   = sock_bearer_type;

  if(sock_bearer_type == SOCK_BEARER_GPRS)
  {
    bearer_info.apn_valid = TRUE;
    bearer_info.phone_nr_valid = FALSE;
    bearer_info.cid = 1;

    switch(prov)
    {
      case APP_PROV_T_MOBILE:
        strcpy(bearer_info.apn, "internet.t-d1.de");
        strcpy(bearer_info.user_id, "t-d1");
        strcpy(bearer_info.password, "gprs");
        break;

      case APP_PROV_HUTCH:
        strcpy(bearer_info.apn, "www");
        strcpy(bearer_info.user_id, "");
        strcpy(bearer_info.password, "");
        break;

      case APP_PROV_AIRTEL:
        strcpy(bearer_info.apn, "airtelgprs.com");
        strcpy(bearer_info.user_id, "");
        strcpy(bearer_info.password, "");
        break;

      case APP_PROV_CUSTOM:
        /* Copy valid APN */
        if(custom_apn_valid)
        {
          strcpy(bearer_info.apn, custom_apn);
          strcpy(bearer_info.user_id, custom_user_id);
          strcpy(bearer_info.password, custom_password);
          break;
        }
        /* Copy default settings for invalid APN settings */
	
      default:
        strcpy(bearer_info.apn,"web.vodafone.de");
        strcpy(bearer_info.user_id, "");
        strcpy(bearer_info.password, "");
        break;
    }
  }
  else
  {
    bearer_info.phone_nr_valid = TRUE;
    bearer_info.apn_valid = FALSE;
    bearer_info.cid = 0;
    if(prov == APP_PROV_T_MOBILE)
    {
      strcpy(bearer_info.phone_nr, "+491712524120");
      strcpy(bearer_info.user_id, "t-d1");
      strcpy(bearer_info.password, "wap");
    }
    else
    {
      strcpy(bearer_info.phone_nr, "+491722290000");
      strcpy(bearer_info.user_id, "");
      strcpy(bearer_info.password, "");
    }
  }
  bearer_info.user_id_valid = TRUE;
  bearer_info.password_valid = TRUE;

  bearer_info.ip_address = SOCK_IPADDR_ANY;
  bearer_info.dns1 = SOCK_IPADDR_ANY;
  bearer_info.dns2 = SOCK_IPADDR_ANY;
  bearer_info.gateway = SOCK_IPADDR_ANY;
  bearer_info.authtype = SOCK_AUTH_NO;
  bearer_info.data_compr = FALSE;
  bearer_info.header_comp = FALSE;
  bearer_info.precedence = 0;
  bearer_info.delay = 0;
  bearer_info.reliability = 0;
  bearer_info.peak_throughput = 0;
  bearer_info.mean_througput = 0;
  bearer_info.shareable = FALSE;

  sock_open_bearer(sock_api_inst,bearer_select,0,&bearer_info,app_sock_callback,pcont);
  if(pcont->bearer_only)
  {
    proc_new_state(pcont, PS_W_DCM_OPEN_ONLY) ;
  }
  else
  {
    proc_new_state(pcont, PS_W_DCM_OPEN) ;
  }
}


static void proc_client_closed(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_client_closed()") ;
  
  result = sock_close(pcont->psocket);
  if (result != SOCK_RESULT_OK)
  {
    TRACE_EVENT_P1("%s: error closing client socket", proc_string(pcont)) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_free_tcpfork(pcont) ;
}



/***********************************************************************
 * Communication functions.
 */


/** Fill and send data buffer.
 *
 * @param pcont        Pointer to process context.
 * @param size         Size of data buffer.
 * @return
 */
static BOOL comm_send_buffer(PROC_CONTEXT_T *pcont, int size)
{
  char *payload ;               /* Pointer to payload buffer. */
  char *cp ;                    /* Pointer into paylaod buffer. */
  char *pp ;                    /* Pointer into test pattern. */
  T_SOCK_RESULT result ;        /* Result of send call. */

  TRACE_FUNCTION("comm_send_buffer()") ;
  MALLOC(payload, size) ;
  TRACE_EVENT_P1("PALLOC payload %x", payload) ;
  /* Fill buffer with pattern. */
  for (cp = payload, pp = APP_SEND_PATTERN; cp < payload + size; cp++, pp++)
  {
    if (pp >= APP_SEND_PATTERN + sizeof(APP_SEND_PATTERN) - 1)
    {
      pp = APP_SEND_PATTERN ;
    }
    *cp = *pp ;
  }
  if(pcont->ipproto == SOCK_IPPROTO_UDP)
  {
    // use UDP socket and specify destination IP address and destination port
    result = sock_sendto(pcont->psocket, payload, (U16)size,
                         pcont->server_ipaddr,pcont->server_port) ;
  }
  else
  {
    result = sock_send(pcont->psocket, payload, (U16)size) ;
  }
  sock_trace_result(pcont, "sock_send()", result) ;
  MFREE(payload) ;
  switch (result)
  {
    case SOCK_RESULT_OK:
      TRACE_EVENT_P6("%s sent %d (%d/%d) bytes in rep %d/%d",
                     proc_string(pcont), size, pcont->data_sent,
                     pcont->spec_items,
                     //* ((pcont->ipproto EQ SOCK_IPPROTO_TCP) ? 1 : size),
                     pcont->n_reps, pcont->spec_reps) ;
      return TRUE ;
    case SOCK_RESULT_NO_BUFSPACE:
      return FALSE ;            /* Pause until SOCK_FLOW_READY_IND. */
    default:
      proc_shutdown(pcont) ;
      return FALSE ;
  }
}


/** Server: send some data to the client.
 *
 * @param pcont        Pointer to process context.
 * @return
 */
static BOOL comm_send_srvprompt(PROC_CONTEXT_T *pcont)
{
  char *payload ;               /* Pointer to payload buffer. */
  int size ;                    /* Actual size of payload. */
  T_SOCK_RESULT result ;        /* Result of send call. */
  TRACE_FUNCTION("comm_send_srv()") ;

  MALLOC(payload, 600) ;
  sprintf(payload, "%s: %sin_shutdown, last_evt %s, will %srepeat\n",
          proc_string(pcont), pcont->in_shutdown ? "" : "not ",
          pcont->last_evt
          ? sock_event_string(pcont->last_evt->event_type) : "NULL",
          pcont->spec_reps ? "" : "not ") ;
  sprintf(payload + strlen(payload),
          "    rx %d B %d pkts, tx %d B %d pkts, errs %d conn %d\n",
          pcont->data_rcvd, pcont->items_rcvd,
          pcont->data_sent, pcont->items_sent,
          pcont->errors, pcont->n_reps) ;

  size = strlen(payload) ;
  result = sock_send(pcont->psocket, payload, (U16)size) ;
  sock_trace_result(pcont, "sock_send()", result) ;
  MFREE(payload) ;
  switch (result)
  {
    case SOCK_RESULT_OK:
      pcont->data_sent += size ;
      pcont->items_sent++ ;
      TRACE_EVENT_P5("%s sent %d (%d/%d) bytes in conn %d",
                     proc_string(pcont), size, pcont->data_sent,
                     pcont->spec_items * size,
                     pcont->n_reps) ;
      return TRUE ;
    case SOCK_RESULT_NO_BUFSPACE:
      return FALSE ;            /* Pause until SOCK_FLOW_READY_IND. */
    default:
      proc_shutdown(pcont) ;
      return FALSE ;
  }
}


/** Issue a DNS query. Called for AP_DNSQRY in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_query(PROC_CONTEXT_T *pcont)
{
  static int next_query = 0 ;   /* Next query index. */
  char *name ;                  /* Domain name to query for. */
  T_SOCK_RESULT result ;        /* Result of query call. */

  TRACE_FUNCTION("comm_query()") ;
  if (query_name[0])
  {
    name = query_name ;
  }
  else
  {
    name = domain_name[next_query] ;
  }
  TRACE_EVENT_P4("%s: query (%d/%d) for %s", proc_string(pcont),
                 pcont->items_sent + 1, pcont->spec_items, name) ;
  result = sock_gethostbyname(sock_api_inst, name, app_sock_callback, pcont) ;
  sock_trace_result(pcont, "sock_gethostbyname()", result) ;
  if (result != SOCK_RESULT_OK)
  {
    pcont->errors++ ;
    TRACE_ERROR("sock_gethostbyname() failed, sleep...") ;
    vsi_t_sleep(VSI_CALLER 2000) ;
  }

  if (!domain_name[++next_query])
  {
    next_query = 0 ;
  }
  pcont->items_sent++ ;
}



/** Send data. Called for all but AP_DNSQRY in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_send(PROC_CONTEXT_T *pcont)
{
  TRACE_EVENT_P1("comm_send() %s", proc_string(pcont)) ;

  switch (pcont->ptype)
  {
    case AP_TCPDL:
      /* Do nothing -- the server will send again anyway. */
      return ;
    case AP_UDPDL:
      if (pcont->data_sent >= pcont->spec_items)
      {
        return;
      }
      break;
    case AP_TCPUL:
    case AP_UDPUL:
      if (pcont->data_sent >= pcont->spec_items)
      {
        TRACE_EVENT_P2("%s done after %d bytes",
                       proc_string(pcont), pcont->data_sent) ;
        proc_close_socket(pcont) ;
        pcont->n_reps++ ;
        return ;
      }
      break ;
    case AP_TCPECHO:
    case AP_UDPECHO:
      if (pcont->items_sent >= pcont->spec_items)
      {
        TRACE_EVENT_P2("%s done after %d writes",
                       proc_string(pcont), pcont->items_sent) ;
        proc_close_socket(pcont) ;
        pcont->n_reps++ ;
        return ;
      }
      break ;
    case AP_DNSQRY:
      comm_query(pcont) ;
      return ;
    case AP_TCPFORK:             /* Send some data, perhaps. */
      switch (pcont->server_port)
      {
        case PORT_CHARGEN:      /* Send something (below). */
          break ;
        case PORT_ECHO:         /* Send somewhere else. */
        case PORT_DISCARD:      /* Don't send anything. */
          return ;
        default:                /* Send a server prompt. */
          comm_send_srvprompt(pcont) ;
          break ;
      }
      return ;
    case AP_NONE:
    case AP_INVALID:
    default:
      TRACE_EVENT_P1("Invalid process type %s", proc_string(pcont)) ;
      return ;
  }

  if (comm_send_buffer(pcont, buffer_size))
  {
    pcont->items_sent++ ;
    TRACE_EVENT_P1("Sent Items: %u",pcont->items_sent);
    pcont->data_sent += buffer_size ;
    vsi_t_sleep(VSI_CALLER udp_interval);
  }
  else
  {
    if (pcont->ptype EQ AP_UDPUL)
    {
      TRACE_EVENT_P2("%s sleeps %d ms", proc_string(pcont), udp_interval) ;
      vsi_t_sleep(VSI_CALLER udp_interval);
    }
  }
}


/** Handle an incoming DNS result. Called for AP_DNSQRY in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_dns_result(PROC_CONTEXT_T *pcont)
{
  T_SOCK_HOSTINFO_CNF *hinfo ;

  TRACE_FUNCTION("comm_dns_result()") ;
  pcont->items_rcvd++ ;

  hinfo = (T_SOCK_HOSTINFO_CNF *) pcont->last_evt ;
  if (hinfo->result != SOCK_RESULT_OK)
  {
    TRACE_EVENT_P3("lookup error %d in %d/%d queries",
                   pcont->errors, pcont->items_rcvd, pcont->spec_items) ;
  }
  else
  {
    TRACE_EVENT_P1("Answer for host %s", hinfo->hostname) ;
    TRACE_EVENT_P3("has address %s (%d/%d)",
                   inet_ntoa(hinfo->ipaddr),
                   pcont->items_rcvd, pcont->spec_items) ;
  }
  if (pcont->items_rcvd < pcont->spec_items)
  {
    comm_send(pcont) ;
  }
  else
  {
    proc_close_conn(pcont) ;
  }
}


/** Receive incoming data. Called for all but AP_TCPUL in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_recv(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RECV_IND *recv_ind = (T_SOCK_RECV_IND *) pcont->last_evt ;

  TRACE_FUNCTION("comm_recv()") ;
  if (pcont->ptype EQ AP_DNSQRY OR pcont->ptype EQ AP_TCPUL)
  {
    TRACE_EVENT_P2("%s: %s unexpected for ptype", proc_string(pcont),
                   sock_event_string(pcont->last_evt->event_type)) ;
    proc_shutdown(pcont) ;
    return ;
  }
  pcont->data_rcvd += recv_ind->data_length ;
  pcont->items_rcvd++ ;
  TRACE_EVENT_P5("%s: recv #%d:%u bytes, total %u, total items sent:%u", proc_string(pcont),
                 pcont->items_rcvd, recv_ind->data_length, pcont->data_rcvd,pcont->items_sent);
  trace_dump_data((U8 *) recv_ind->data_buffer,
                  MIN(APP_DATA_DUMP_LENGTH, recv_ind->data_length)) ;
  MFREE(recv_ind->data_buffer) ;
  recv_ind->data_buffer = 0 ;
  switch (pcont->ptype)
  {
    case AP_UDPDL:
      /* After every sent UDP packet, a "answer" comes from the Chargen server. 
       * If all packets are sent we are waiting for the last packet to 
       * receive, else an unexpected event would be the result in the 
       * "app_sock_callback()"; TCPIP_DATA_IND is received instead of SOCK_CLOSE_CNF
       * TODO: why (pcont->items_sent-1), I assume that the server "confirms"
       *       every packet
       */
      if ((pcont->data_sent >= pcont->spec_items) &&
          (pcont->items_rcvd == pcont->items_sent))
      {
        TRACE_EVENT("last UDP-DL packet received");
        pcont->n_reps++ ;
        proc_close_socket(pcont) ;
      }
      else {
        comm_send(pcont);
      }
      break;
    case AP_TCPDL:
      if (pcont->data_rcvd >= pcont->spec_items)
      {
        TRACE_EVENT_P3("%s done after %d/%d bytes",
                       proc_string(pcont), pcont->data_rcvd,
                       pcont->spec_items) ;
        pcont->n_reps++ ;
        proc_close_socket(pcont) ;
      }
      break ;
    case AP_UDPECHO:
    case AP_TCPECHO:
    case AP_TCPFORK:
      comm_send(pcont) ;
      break ;
    default:
      TRACE_ERROR("Unexpected ptype in comm_recv()") ;
      break ;
  }
}


/** Handle a communication event according to the process type. Called for all
 * process types in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_event(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("comm_event()") ;

  switch (pcont->last_evt->event_type)
  {
    case SOCK_CONN_CLOSED_IND:
      if (pcont->ptype EQ AP_TCPFORK)
      {
        proc_client_closed(pcont) ;
        break ;
      }
      /*lint -fallthrough */
    case SOCK_ERROR_IND:
      TRACE_EVENT_P2("%s: %s, shutdown", proc_string(pcont),
                     sock_event_string(pcont->last_evt->event_type)) ;
      // server should not reset , even if connection is reset by client.
      // but client should shutdown , if connection is reset by server.
      if((pcont->ptype EQ AP_TCPFORK)  AND
         (pcont->last_evt->result == SOCK_RESULT_CONN_RESET OR
          pcont->last_evt->result == SOCK_RESULT_TIMEOUT))
      {
         proc_client_closed(pcont) ;
         return;
      }
      else
        proc_shutdown(pcont) ;
      return ;
    case SOCK_RECV_IND:
      comm_recv(pcont) ;
      break ;
    case SOCK_FLOW_READY_IND:
      if(pcont->ptype NEQ AP_UDPDL) {
        comm_send(pcont) ;
      }
      break ;
    case SOCK_HOSTINFO_CNF:
      if (pcont->ptype EQ AP_DNSQRY) {
        comm_dns_result(pcont) ;
        break ;
      }
      /*lint -fallthrough */
    case SOCK_CREATE_CNF:
    case SOCK_CLOSE_CNF:
    case SOCK_BIND_CNF:
    case SOCK_LISTEN_CNF:
    case SOCK_CONNECT_CNF:
    case SOCK_SOCKNAME_CNF:
    case SOCK_PEERNAME_CNF:
    case SOCK_MTU_SIZE_CNF:
    case SOCK_CONNECT_IND:
      TRACE_EVENT_P2("%s: %s unexpected at all", proc_string(pcont),
                     sock_event_string(pcont->last_evt->event_type)) ;
      proc_shutdown(pcont) ;
      return ;
    case SOCK_BAERER_CLOSED_IND:
      proc_shutdown(pcont);
      break;
    default:
      TRACE_EVENT_P2("comm_event(): %s unknown event %d",
                     proc_string(pcont), pcont->last_evt->event_type) ;
      proc_shutdown(pcont) ;
      break ;
  }
}



/***********************************************************************
 * State machine functions (i. e. state-changing functions)
 */

/** Finish the process after the network connection has been closed.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_finish(PROC_CONTEXT_T *pcont)
{
  TRACE_EVENT_P1("%s finished", proc_string(pcont)) ;
  pcont->network_is_open = FALSE ;
  pcont->in_shutdown = FALSE;
  proc_new_state(pcont, PS_IDLE) ;
}


/** Shutdown process hard, usually after an error or user request. This
 * includes closing the process's socket and network connection.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_shutdown(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("proc_shutdown()") ;

  if(pcont->in_shutdown)
  {
    TRACE_EVENT("Allready in shutdown");
    return;
  }
  pcont->in_shutdown = TRUE ;
  app_pstat() ;
  if (pcont->psocket_is_open)
  {
    proc_close_socket(pcont);
    return;
  }
  if (pcont->network_is_open OR
      pcont->pstate == PS_W_DCM_OPEN OR pcont->pstate == PS_W_DCM_OPEN_ONLY)
  {
    proc_close_conn(pcont);
    return;
  }
  else
  {
    proc_finish(pcont);
  }
}


/** Create a socket after the network connection has been established.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_open_socket(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;

  TRACE_FUNCTION("proc_open_socket()") ;
  /* We don't need to do this for the DNS query process. */
  if (pcont->ptype EQ AP_DNSQRY)
  {
    proc_begin_comm(pcont) ;
  }
  else
  {
    result = sock_create(sock_api_inst, pcont->ipproto, app_sock_callback, pcont);
    if (result NEQ SOCK_RESULT_OK)
    {
      sock_trace_result(pcont, "sock_create()", result) ;
      proc_shutdown(pcont) ;
      return;
    }
    proc_new_state(pcont, PS_W_CREAT) ;
  }
}


/** Close the network connection after the task has been done.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_close_conn(PROC_CONTEXT_T *pcont)
{
  

  TRACE_FUNCTION("proc_close_conn()");
  if(pcont->network_is_open)
  {
    pcont->in_shutdown = TRUE;
    sock_close_bearer(sock_api_inst, sock_bearer_handle, app_sock_callback, pcont);
    proc_new_state(pcont, PS_W_DCLOS) ;
  }
  else
  {
    proc_finish(pcont);
  }
}


/** Connect the socket after it has been created.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_connect_socket(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;

  TRACE_FUNCTION("proc_connect_socket()") ;
  /* If we do not yet have an IP address to connect to, look it up first. */
  if (pcont->server_ipaddr EQ SOCK_IPADDR_ANY)
  {
    result = sock_gethostbyname(sock_api_inst, pcont->server_name,app_sock_callback, pcont);
    if (result NEQ SOCK_RESULT_OK)
    {
      sock_trace_result(pcont, "sock_gethostbyname()", result) ;
      proc_shutdown(pcont) ;
      return;
    }
    proc_new_state(pcont, PS_W_DNS) ;
    return ;
  }

  result = sock_connect(pcont->psocket, pcont->server_ipaddr, pcont->server_port);
  if (result NEQ SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "sock_connect()", result) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(pcont, PS_W_SCONN) ;
}


/** Begin communicating after the socket has been created.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_begin_comm(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("proc_begin_comm()") ;

  proc_new_state(pcont, PS_COMM) ;
  switch (pcont->ptype)
  {
    case AP_TCPDL:
      /* We wait for data from the server to arrive. */
      break ;
    case AP_UDPDL:
      /* Trigger the chargen server to send fisrt UDP packet */
       comm_send(pcont);
      break ;
    case AP_TCPUL:
    case AP_UDPUL:
    case AP_TCPECHO:
    case AP_UDPECHO:
    case AP_DNSQRY:
    case AP_TCPFORK:
      comm_send(pcont) ;
      break ;
    default:
      TRACE_EVENT_P2("%s unknown state (%d)",
                     proc_string (pcont), pcont->ptype) ;
      break ;
  }
}


/** Close the socket after the requested communication has been done.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_close_socket(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("proc_close_socket()") ;

  sock_close(pcont->psocket) ;
  proc_new_state(pcont, PS_W_SCLOS) ;
}


static void proc_bind_socket(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_bind_socket()") ;

  if ((result = sock_bind(pcont->psocket, pcont->server_port))
      != SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "sock_bind()", result) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(pcont, PS_W_BIND) ;
}


static void proc_listen(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_listen()") ;

  if ((result = sock_listen(pcont->psocket)) != SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "sock_listen()", result) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(pcont, PS_W_LISTN) ;
}


static void proc_incoming(PROC_CONTEXT_T *pcont)
{
  T_SOCK_CONNECT_IND *conn_ind ;
  PROC_CONTEXT_T *newp ;
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_incoming()") ;

  conn_ind = (T_SOCK_CONNECT_IND *) pcont->last_evt ;

  if ((newp = proc_new_tcpfork(pcont)) EQ NULL)
  {
      TRACE_EVENT_P1("%s: failed to fork server, close new socket",
                    proc_string(pcont)) ;
      sock_close(conn_ind->new_socket) ;
      return ;
  }

  /* We cannot make two calls to proc_string() without one overwriting the
   * other, so we print the process strings in two successive traces. */
  TRACE_EVENT_P1("%s: forking to handle client connection...",
                 proc_string(pcont)) ;
  TRACE_EVENT_P1("...forked process is %s", proc_string(newp)) ;
  newp->psocket = conn_ind->new_socket ;
  sock_set_callback(newp->psocket, app_sock_callback, newp) ;
  TRACE_EVENT_P3("%s connection from %s:%d, looking up...", proc_string(pcont),
                 inet_ntoa(conn_ind->peer_ipaddr), NTOHS(conn_ind->peer_port)) ;
  if ((result = sock_gethostbyaddr(sock_api_inst, conn_ind->peer_ipaddr,
                                   app_sock_callback, newp))
      != SOCK_RESULT_OK)
  {
    sock_trace_result(newp, "sock_gethostbyaddr()", result) ;
    proc_shutdown(newp) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(newp, PS_W_DNS) ;
}


static void proc_hostinfo_recvd(PROC_CONTEXT_T *pcont)
{
  T_SOCK_HOSTINFO_CNF *hinfo ;
  TRACE_FUNCTION("proc_hostinfo_recvd()") ;

  hinfo = (T_SOCK_HOSTINFO_CNF *) pcont->last_evt ;
  if (hinfo->result != SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "SOCK_HOSTINFO_CNF", hinfo->result) ;
  }
  else
  {
    TRACE_EVENT_P3("%s: connected peer is %s (%s)", proc_string(pcont),
                   hinfo->hostname, inet_ntoa(hinfo->ipaddr)) ;
  }
  proc_begin_comm(pcont) ;
}



/*==== Exported functions ====================================================*/


/** Initialize the application core.
 *
 * @param handle    own communication handle
 * @return PEI_OK/PEI_ERROR depending on the success of the initialization.
 */
BOOL app_initialize_tcpip(T_HANDLE app_handle)
{
  TRACE_FUNCTION("app_initialize_tcpip()") ;
  memset(&proc_context, 0, sizeof(proc_context)) ;
  return PEI_OK ;
}


/* Macro for checking the Socket API events in app_sock_callback(). */
#define CHECK_SOCK_EVT(evttype)                                       \
{                                                                     \
  if (event->event_type != evttype)                                   \
  {                                                                   \
    TRACE_ERROR("unexpected event type waiting for " #evttype) ;      \
    proc_shutdown(pcont) ;                                            \
    break ;                                                           \
  }                                                                   \
  if (event->result != SOCK_RESULT_OK)                                \
  {                                                                   \
    if(pcont->pstate == PS_W_DCM_OPEN OR                              \
       pcont->pstate == PS_W_DCM_OPEN_ONLY)                           \
       { proc_new_state(pcont, PS_IDLE); }                            \
    proc_shutdown(pcont) ;                                            \
    break ;                                                           \
  }                                                                   \
}


/** Socket callback function as specified in the Socket API.
 *
 * @param event      Pointer to event struct passed by API.
 * @param context    Pointer to application context (here: process context)
 * @return
 */
void app_sock_callback(T_SOCK_EVENTSTRUCT *event, void *context)
{
  PROC_CONTEXT_T *pcont ;
  T_SOCK_BEARER_INFO_CNF *info;

  TRACE_FUNCTION("app_sock_callback()") ;

  pcont = (PROC_CONTEXT_T *)context ;
  pcont->last_evt = event ;     /* Save event in process context. */

  sock_trace_result(pcont, sock_event_string(event->event_type),
                    event->result) ;
  if (event->result != SOCK_RESULT_OK)
  {
    pcont->errors++ ;
    if(event->result == SOCK_RESULT_NETWORK_LOST)
    {
      pcont->network_is_open = FALSE;
    }
  }
  switch (pcont->pstate)        /* Do a preliminary check of the event. */
  {
    case PS_W_DCM_OPEN:            /* Waiting for DCM to open connection. */
      CHECK_SOCK_EVT(SOCK_OPEN_BEARER_CNF);
      
      // FST: can't be evaluated-> see makro CHECK_SOCK_EVT
      if (event->result != SOCK_RESULT_OK AND 
          event->result != SOCK_RESULT_BEARER_ACTIVE)
      {
        proc_shutdown(pcont) ;
        return ;
      }
      if (pcont->network_is_open)
      {
        TRACE_ERROR("SOCK_OPEN_BEARER_CNF received but pcont->network_is_open") ;
        proc_shutdown(pcont);
        return ;
      }
      pcont->network_is_open = TRUE ;
      proc_open_socket(pcont) ;
      break;
      
    case PS_W_DCM_OPEN_ONLY:
      CHECK_SOCK_EVT(SOCK_OPEN_BEARER_CNF);
      pcont->network_is_open = TRUE ;
      proc_new_state(pcont, PS_DCM_OPEN);
      break;

    case PS_W_DCLOS:            /* Waiting for DCM to close connection. */
      CHECK_SOCK_EVT(SOCK_CLOSE_BEARER_CNF);

      if (!pcont->network_is_open AND pcont->pstate != PS_IDLE)
      {
        TRACE_ERROR("DCM_CONN_CLOSED received but !pcont->network_is_open") ;
        proc_shutdown(pcont) ;
        return ;
      }
      proc_finish(pcont) ;
      break;

    case PS_W_CONN_INFO:
      CHECK_SOCK_EVT(SOCK_BEARER_INFO_CNF);

      info = (T_SOCK_BEARER_INFO_CNF *)event;
      app_print_conn_info(info);

      TRACE_EVENT("SOCK_BEARER_INFO_CNF received");
      break;

    case PS_W_CREAT:            /* Waiting for socket create confirmation. */
      CHECK_SOCK_EVT(SOCK_CREATE_CNF) ;
      pcont->psocket = event->socket ;
      pcont->psocket_is_open = TRUE ;
      if (pcont->ptype EQ AP_TCPSRV)
      {
        proc_bind_socket(pcont) ;
      }
      else if(pcont->ipproto == SOCK_IPPROTO_TCP)
      {
        proc_connect_socket(pcont) ;
      }
      else
      {
        // This is not possible in the moment because the RNET_API does not 
        // provide a sendto() function. Therefore it is is only possible to sent
        // via "connected" UDP sockets.
        // TODO: if the next statement will be enabled the "proc_connect_socket()" has to be removed!!
        // proc_begin_comm(pcont);
        proc_connect_socket(pcont) ;
      }
      break ;

    case PS_W_BIND:
      CHECK_SOCK_EVT(SOCK_BIND_CNF) ;
      proc_listen(pcont) ;
      break ;

    case PS_W_LISTN:
      CHECK_SOCK_EVT(SOCK_LISTEN_CNF) ;
      app_pstat() ;
      proc_new_state(pcont, PS_LISTENS) ; /* Nothing more to do here. */
      break ;

    case PS_LISTENS:            /* SOCK_CONNECT_IND or SOCK_CLOSE_CNF */
      if (event->event_type EQ SOCK_CONNECT_IND)
      {
        proc_incoming(pcont) ;
      }
      break ;

    case PS_W_DNS:
      /* After sending connect confirm to client, client will send data to server
         and server is dnsquerying now , server will be shutdown ,here Unfortunately..
         but we want to exchange data happily, so this code is added....  */
      if((event->event_type == SOCK_RECV_IND) AND (pcont->ptype == AP_TCPFORK))
         break;

      CHECK_SOCK_EVT(SOCK_HOSTINFO_CNF) ;
      proc_hostinfo_recvd(pcont) ;
      break ;

    case PS_W_SCONN:            /* Waiting for socket connect confirmation. */
      CHECK_SOCK_EVT(SOCK_CONNECT_CNF) ;
      proc_begin_comm(pcont) ;
      break ;

    case PS_COMM:               /* Happily exchanging data. */
      comm_event(pcont) ;
      break ;

    case PS_W_SCLOS:            /* Waiting for socket close confirmation. */
      CHECK_SOCK_EVT(SOCK_CLOSE_CNF) ;
                                                                     
      pcont->psocket_is_open = FALSE ;
      pcont->psocket = 0;
      app_pstat() ;
      if (pcont->n_reps >= pcont->spec_reps OR
          pcont->in_shutdown)
      {        
        proc_close_conn(pcont) ;
      }
      else
      {
        pcont->data_sent = 0 ;
        pcont->data_rcvd = 0 ;
        pcont->items_sent = 0 ;
        pcont->items_rcvd = 0 ;
        proc_open_socket(pcont) ;
      }
      break ;

    case PS_IDLE:               /* Initial state, process not running. */
      TRACE_EVENT_P2("app_sock_callback(): %s receives %s (ignored)",
                     proc_string(pcont), sock_event_string(event->event_type)) ;
      break ;

    case PS_DCM_OPEN:
      if(event->event_type == SOCK_BAERER_CLOSED_IND)
      {
        TRACE_ERROR("SOCK_BAERER_CLOSED_IND -> Shutdown");
        if(event->result == SOCK_RESULT_NETWORK_LOST)
        {
          pcont->network_is_open = FALSE;
        }
        proc_shutdown(pcont) ;
      }
      break;
    
    case PS_INVALID:            /* Invalid state. */
      TRACE_EVENT_P1("app_sock_callback(): %s invalid state", proc_string(pcont)) ;
      break;

    default:
      TRACE_ERROR("app_sock_callback(): Default Statement");
      break;
      /*
      if(event->event_type == SOCK_DCM_ERR_IND)
      {
        TRACE_ERROR("SOCK_DCM_ERR_IND -> Shutdown");
      }
      proc_shutdown(pcont) ;
      return ;
      */
  }
  /* Free data buffer if it has not been freed yet. */
  if ( (event->event_type EQ SOCK_RECV_IND) AND
       ((T_SOCK_RECV_IND *) event)->data_buffer )
  {
    MFREE(((T_SOCK_RECV_IND *) event)->data_buffer) ;
  }
  pcont->last_evt = NULL ;
  TRACE_EVENT_P1("leave app_sock_callback() for %s", proc_string(pcont)) ;
}



/*
 * Application command functions.
 */


/** Start a data communication process of the appropriate type..
 *
 * @param size     Amount of data to download or number of items to transfer.
 */
void app_start_tcpdl(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_TCPDL, SOCK_IPPROTO_TCP,
            port_number ? port_number : PORT_CHARGEN) ; }

void app_start_tcpul(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_TCPUL, SOCK_IPPROTO_TCP,
            port_number ? port_number : PORT_DISCARD) ; }

void app_start_udpdl(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_UDPDL, SOCK_IPPROTO_UDP,
            port_number ? port_number : PORT_CHARGEN) ; }

void app_start_udpul(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_UDPUL, SOCK_IPPROTO_UDP,
            port_number ? port_number : PORT_DISCARD) ; }

void app_start_tcpecho(int prov, int items, int reps)
{ proc_init(prov, items, reps, AP_TCPECHO, SOCK_IPPROTO_TCP,
            port_number ? port_number : PORT_ECHO) ; }

void app_start_udpecho(int prov, int items, int reps)
{ proc_init(prov, items, reps, AP_UDPECHO, SOCK_IPPROTO_UDP,
            port_number ? port_number : PORT_ECHO) ; }

void app_start_dnsquery(int prov, int times, char *address)
{
  if (address)
  {
    strncpy(query_name, address, FQDN_LENGTH) ;
  }
  else
  {
    query_name[0] =0 ;
  }
  proc_init(prov, times,1, AP_DNSQRY,(T_SOCK_IPPROTO)0, 0) ;
}

void app_start_tcpsrv(int prov, int port, int repeat)
{ proc_init(prov, 0, repeat, AP_TCPSRV, SOCK_IPPROTO_TCP, port) ; }



/** Shutdown the specified process.
 *
 * @param pid    Process ID.
 */
void app_shutdown(void)
{
  TRACE_FUNCTION("app_shutdown()") ;
  proc_shutdown(&proc_context) ;
}


/** Set the current server name or IP address.
 *
 * @param server    Name or IP address (in dotted decimal notation) of server.
 */
void app_server(char *server)
{
  if (server)
  {
    strncpy(server_name, server, FQDN_LENGTH) ;
  }
  TRACE_EVENT_P1("server_name is %s", server_name) ;
}


/** Set or show the current buffer size.
 *
 * @param bufsize    size of buffer as a string or NULL
 */
void app_buffer(char *bufsize)
{
  if (bufsize)
  {
    buffer_size = atoi(bufsize) ;
  }
  TRACE_EVENT_P1("buffer_size is %d", buffer_size) ;
}


/** Set or show the current buffer size.
 *
 * @param port     port number override
 */
void app_port(char *port)
{
  if (port)
  {
    port_number = (U16) atoi(port) ;
  }
  if (port_number)
  {
    TRACE_EVENT_P1("port number override is %d", port_number) ;
  }
  else
  {
    TRACE_EVENT("standard port numbers used") ;
  }
}


/** Set or show the current bearer type.
 *
 * @param bearer     bearer type
 */
void app_bearer(char *bearer)
{
  if (bearer)
  {
    if (!strcmp(string_to_lower(bearer), "any"))
    {
      sock_bearer_type = SOCK_BEARER_ANY;
    }
    else if (!strcmp(string_to_lower(bearer), "gprs"))
    {
      sock_bearer_type = SOCK_BEARER_GPRS ;
    }
    else if (!strcmp(string_to_lower(bearer), "gsm"))
    {
      sock_bearer_type = SOCK_BEARER_GSM;
    }
    else if (!strcmp(string_to_lower(bearer), "prof"))
    {
      sock_bearer_type = SOCK_BEARER_USE_PROFILE;
    }    
    else if (!strcmp(string_to_lower(bearer), "spec"))
    {
      sock_bearer_type = SOCK_BEARER_AS_SPECIFIED;
    }

    else
    {
      TRACE_EVENT_P1("bearer type %s unknown", bearer) ;
    }
  }
  TRACE_EVENT_P1("bearer type is %s", sock_bearer_type_string(sock_bearer_type)) ;
}


/** Trace information about the process.
 */
void app_pstat(void)
{
  PROC_CONTEXT_T *pcont ;

  TRACE_FUNCTION("app_pstat()") ;

  pcont = &proc_context ;
  TRACE_EVENT_P3("%s in_shutdown %d last_evt %08x",
                 proc_string(pcont), pcont->in_shutdown, pcont->last_evt) ;
  TRACE_EVENT_P6("prot %d srv %s %s:%d sp_it %d sp_rep %d",
                 pcont->ipproto,
                 pcont->server_name ? pcont->server_name : "",
                 inet_ntoa(pcont->server_ipaddr),
                 NTOHS(pcont->server_port),
                 pcont->spec_items,
                 pcont->spec_reps) ;
  TRACE_EVENT_P6("dta tx %d dta rx %d it tx %d it rx %d rep %d errs %d",
                 pcont->data_sent,
                 pcont->data_rcvd,
                 pcont->items_sent,
                 pcont->items_rcvd,
                 pcont->n_reps,
                 pcont->errors) ;
  TRACE_EVENT_P5("Socket descr: %x, %sNetwork%s, %spSocket%s",
                 pcont->psocket,
                 pcont->network_is_open ? "" : "no ",
                 pcont->network_is_open ? " open" : "",
                 pcont->psocket_is_open ? "" : "no ",
                 pcont->psocket_is_open ? " open" : "") ;
  TRACE_EVENT_P4("global: server %s query %s buffer %d port %d",
                 server_name, query_name, buffer_size, port_number) ;
}


/** Make the application stop or continue receiving data from the network by
 * calling the xoff or xon function, respectively.
 *
 * @param flow_on    if non-zero, switch flow on; off otherwise.
 */
void app_switch_flow(int flow_on)
{
  PROC_CONTEXT_T *pcont = &proc_context ;
  TRACE_FUNCTION("app_switch_flow()") ;

  if (flow_on)
  {
    TRACE_EVENT("switching socket to xon") ;
    sock_flow_xon(pcont->psocket) ;
  }
  else
  {
    TRACE_EVENT("switching socket to xoff") ;
    sock_flow_xoff(pcont->psocket) ;
  }
}


LOCAL void app_print_conn_info(T_SOCK_BEARER_INFO_CNF *info)
{
  TRACE_EVENT_P1("BearerType: %s",
                 sock_bearer_type_string(info->bearer_params.bearer_type));
  TRACE_EVENT_P1("APN: %s", info->bearer_params.apn);
  TRACE_EVENT_P1("PhoneNumber: %s", info->bearer_params.phone_nr);
  TRACE_EVENT_P1("UserId: %s", info->bearer_params.user_id);
  TRACE_EVENT_P1("Password: %s", info->bearer_params.password);
  TRACE_EVENT_P1("IP-Address: %s", inet_ntoa(info->bearer_params.ip_address));
  TRACE_EVENT_P1("DNS1-Address: %s", inet_ntoa(info->bearer_params.dns1));  
  TRACE_EVENT_P1("DNS2-Address: %s", inet_ntoa(info->bearer_params.dns2));
  TRACE_EVENT_P1("Gateway-Address: %s", inet_ntoa(info->bearer_params.gateway));
  TRACE_EVENT_P1("CID: %d",info->bearer_params.cid);
}



void app_open_bearer(int prov, int size, int reps)
{
  proc_context.bearer_only = TRUE;
  proc_init(prov, size, reps, AP_NONE, SOCK_IPPROTO_TCP, port_number );
  proc_context.bearer_only = FALSE;
  
}

void app_close_bearer()
{
  app_shutdown();
}


#endif /* FF_GPF_TCPIP */

/* EOF */