view src/gpf2/tst/drv/socket.c @ 662:8cd8fd15a095

SIM speed enhancement re-enabled and made configurable TI's original code supported SIM speed enhancement, but Openmoko had it disabled, and OM's disabling of speed enhancement somehow caused certain SIM cards to start working which didn't work before (OM's bug #666). Because our FC community is much smaller in year 2020 than OM's community was in their day, we are not able to find one of those #666-affected SIMs, thus the real issue they had encountered remains elusive. Thus our solution is to re-enable SIM speed enhancement and simply wait for if and when someone runs into a #666-affected SIM once again. We provide a SIM_allow_speed_enhancement global variable that allows SIM speed enhancement to be enabled or disabled per session, and an /etc/SIM_spenh file in FFS that allows it to enabled or disabled on a non-volatile basis. SIM speed enhancement is now enabled by default.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 24 May 2020 05:02:28 +0000
parents cd37d228dae0
children
line wrap: on
line source

/* 
+------------------------------------------------------------------------------
|  File:       socket.c
+------------------------------------------------------------------------------
|  Copyright 2002 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 :  This Modul contains the socket driver adaptation
+----------------------------------------------------------------------------- 
*/ 

#ifdef _VXWORKS_
#define GNU_COMPILER
#endif

#undef SOCK_TRACE

#undef SOCKET_DEBUG

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

#if defined _NUCLEUS_ || defined _TOOLS_   /* socket-driver running on windows OS */
/*lint -e717 suppress info do...while(0); */
/*lint -esym(550,rc) suppress info not accessed */
/*lint -e813, suppress Info 813: auto variable has size > 100 -> uncritical in this context */
/*lint -e801, suppress Info 801: Use of goto is deprecated */
  #include <windows.h>
  #include <winsock.h>
  #include <stdio.h>
  #ifdef SOCK_TRACE
    #include <stdio.h>
    #include <fcntl.h>
    #include <io.h>
  #endif  /* SOCK_TRACE */
#endif  /* #ifdef _NUCLEUS_ */

#ifdef _PSOS_                /* socket-driver running on pSOS */
  #define _PNA_30_BACK
  #include <psos.h>
  #include "bsp.h"
  #include <pna.h>
  #include <prepc.h>
  #include <rescfg.h>
#endif /* #ifdef _PSOS_ */

#ifdef _VXWORKS_
  #include "vxWorks.h"
  #include "sockLib.h"
  #include "inetLib.h"
  #include "ioLib.h"
  #include "selectLib.h"
  #include "errnoLib.h"
  #include "logLib.h"
/* undefine the MALLOC and FREE of VxWorks to avoid warnings */
#undef MALLOC
#undef FREE
#endif  /* _VXWORKS_ */

/* More operating systems go here */

#ifdef _LINUX_
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <netinet/tcp.h>
  #include <sys/ioctl.h>
  #include <errno.h>
  #include <netdb.h>
#endif

#ifdef _SOLARIS_
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <netinet/tcp.h>
  #include <sys/ioctl.h>
  #include <errno.h>
  #include <netdb.h>
  #include <sys/filio.h>
#endif

#ifndef OLD_FRAME
  #include "typedefs.h"
  #include "os.h"
#endif

#include "socket.h"
#include "tools.h"
#include "vsi.h"
#include "drvconf.h"
#include "tstheader.h"

/*==== DEFINITIONS ==========================================================*/

#define MAX_PENDING_CONNECTS  5          /* The backlog allowed for listen() */
#define SEND_TIMEOUTUSEC      500        /* minimal timeout value for sending*/
#define INVALID_HANDLE        NULL
#define INVALID_SIGNALTYPE    0
#define NO_FLAGS_SET          0          /* Used with recv()/send()          */
#define MAX_ETH_LEN           (1500-40)  /* Maximum size of a Ethernet packet without IP headers */
#define WRBUF_LEN             2048

#define ALLOWED_SOCKET_SIGNALS   (DRV_SIGTYPE_READ|DRV_SIGTYPE_CONNECT|DRV_SIGTYPE_DISCONNECT)

#if defined _NUCLEUS_ || defined _TOOLS_
    typedef ULONG SIZETYPE;
    #define GETTIME(t)           (t = GetTickCount())
    #define GETREADSIZE(sok,psiz) ioctlsocket(sok, FIONREAD,(ULONG*)psiz)
    #define READ_SOCKET(s,b,l)    recv((s),(b),(int)(l),NO_FLAGS_SET)
    #define WRITE_SOCKET(s,b,l)   send((s),(const char*)(b),(l),NO_FLAGS_SET)
    #define CLOSE_SOCKET(s)       closesocket(s)
#endif /* #ifdef _NUCLEUS_ */

#ifdef _PSOS_
  typedef int SOCKET;
  typedef int SIZETYPE;
  typedef struct hostent HOSTENT;
  #define h_addr  h_addr_list[0]
  typedef HOSTENT *  PHOSTENT;
  #define FAR /**/
  #define SOCKADDR_IN struct sockaddr_in
  #define SOCKET_ERROR   (-1)
  #define INVALID_SOCKET (-1)
  #define GETTIME(t)            os_GetTime (0, &(t))
  #define GETREADSIZE(sok,psiz) ioctl(sok, FIOREAD,(char*)psiz)
  #define READ_SOCKET(s,b,l)    recv((s),(b),(int)(l),NO_FLAGS_SET)
  #define WRITE_SOCKET(s,b,l)   send((s),(char *)(b),(int)(l),NO_FLAGS_SET)
  #define CLOSE_SOCKET(s)       close(s)
#endif /* #ifdef _PSOS_ */

#ifdef _VXWORKS_
  #define SOCKADDR_IN    struct sockaddr_in
  #define FAR /* nil */
  #define SOCKET_ERROR   ERROR
  #define INVALID_SOCKET ERROR
  typedef int SOCKET;
  typedef int SIZETYPE;
  #define GETTIME(t)            os_GetTime (0, &(t))
  #define GETREADSIZE(sok,psiz) ioctl(sok, FIONREAD, (int)psiz)
  #define READ_SOCKET(s,b,l)    recv((s), (b), (int)(l), NO_FLAGS_SET)
  #define WRITE_SOCKET(s,b,l)   send((s), (char *)(b), (int)(l), NO_FLAGS_SET)
  #define CLOSE_SOCKET(s)       close(s)
#endif /* _VXWORKS_ */

#if defined (_LINUX_) || defined (_SOLARIS_)
  #define SOCKADDR_IN    struct sockaddr_in
  #define FAR /* nil */
  #define SOCKET_ERROR   (-1)
  #define INVALID_SOCKET (-1)
  typedef int SIZETYPE;
  #define GETTIME(t)            os_GetTime (0, &(t))
  #define GETREADSIZE(sok,psiz) ioctl(sok, FIONREAD, (int)psiz)
  #define READ_SOCKET(s,b,l)    recv((s), (b), (int)(l), NO_FLAGS_SET)
  #define WRITE_SOCKET(s,b,l)   send((s), (char *)(b), (int)(l), NO_FLAGS_SET)
  #define CLOSE_SOCKET(s)       close(s)
#endif /* _VXWORKS_ */

#define PORT_NO              6392
#define TX_BUFFER_SIZE       8192
#define RX_BUFFER_SIZE       8192
#define TX_TIMEOUT_MSEC     10000
#define RX_TIMEOUT_MSEC     10000

#ifdef _VXWORKS_
#define SOCKET_PRIO    115
#define SOCKET_STACK  8192
#elif defined _PSOS_
#define SOCKET_PRIO    110
#define SOCKET_STACK  1024
#elif defined _TOOLS_
#define SOCKET_PRIO      1
#define SOCKET_STACK  1024
#else
#define SOCKET_PRIO      1
#define SOCKET_STACK  1024
#endif

#define TI_MODE     0x0001

/*==== TYPES ================================================================*/
typedef enum
{
  SST_PL0,     /* 0 bytes of packet length read*/
  SST_PL1,     /* 1 bytes of packet length read*/
  SST_DATA0,   /* 0 bytes of data read*/
  SST_DATAx    /* not all data read */
} T_SOCK_STATE;

typedef struct
{
  USHORT            Connect;
  SOCKET            Listener;
  SOCKET            Socket;
  USHORT            EnabledSignals;
  OS_HANDLE         ThreadID ;
  USHORT            SocketHandle;
  T_DRV_CB_FUNC     Callback;
} Client_Type;

/*==== EXTERNALS ============================================================*/
EXTERN BOOL           socket_flush;

/*==== LOCAL VARS ===========================================================*/

LOCAL socket_DCB_Type L_DCB ;
LOCAL Client_Type     L_ClientData ;
LOCAL BOOL            L_ThreadActive ;
LOCAL T_DRV_SIGNAL    SocketSignal;
LOCAL UBYTE    wrbuf[WRBUF_LEN+2];
LOCAL UBYTE   *wrbuf_pos = wrbuf;
T_SOCK_STATE   sock_state = SST_PL0;

#ifdef SOCK_TRACE
ULONG                 fh_sock = -1;
LOCAL char            stbuf[80];
#endif

LOCAL BOOL     isLittleEndian = TRUE;
int tst_socket; /* for psos */
int tst_socket_initialized; /* flag for pSOS */
int tst_socket_in_TxLen;    /* for pSOS */


/*==== DIAGNOSTICS ==========================================================*/

#ifdef SOCKET_DEBUG
#include <STDIO.H>
static char *logfile = "socket.log";
static char *bufferfullfile = "bufferfull.log";
static char *inprocessfile = "inprogress.log";
static char *noconnectfile1 = "noconnect1.log";
static char *noconnectfile2 = "noconnect2.log";
static char *noconnectfile3 = "noconnect3.log";
static char *readerrorfile = "readerror.log";
static FILE *fp = NULL;
#endif /* SOCKET_DEBUG */

/*==== END DIAGNOSTICS ======================================================*/

extern USHORT ext_data_pool_handle;

GLOBAL ULONG drv_socket_task_stack_size = SOCKET_STACK;
GLOBAL USHORT drv_socket_task_prio = SOCKET_PRIO;

/*==== PRIVATE FUNCTIONS ====================================================*/
/*
+------------------------------------------------------------------------------
|  Function    : L_CreateThread
+------------------------------------------------------------------------------
|  Description : This function creates a thread.
|
|  Parameters  : ThreadFunc        - pointer to the function beeing a thread
|
|  Return      : FALSE             - Thread not created
|                TRUE              - Thread create (Thread ID stored in
|                                    L_ClientData.ThreadID
|
+------------------------------------------------------------------------------
*/
#ifdef OLD_FRAME
BOOL L_CreateThread (USHORT (*ThreadFunc)(USHORT,ULONG))
{
  return (CreateThread ((LPSECURITY_ATTRIBUTES) NULL,
                0,
                (LPTHREAD_START_ROUTINE)ThreadFunc,
                (LPVOID) NULL,
                0,
                &L_ClientData.ThreadID) != NULL);
}
#else
BOOL L_CreateThread (void (*ThreadFunc)(T_HANDLE,ULONG))
{
  if ( os_CreateTask (0, (char*)"SOCKET", ThreadFunc, drv_socket_task_stack_size, drv_socket_task_prio, &L_ClientData.ThreadID, ext_data_pool_handle) < 0L )
    return FALSE;
  if ( os_StartTask (0, L_ClientData.ThreadID, 0) < 0 )
    return FALSE ;

  return TRUE;
}
#endif

/*
+------------------------------------------------------------------------------
|  Function    : L_SetSocketBuffer
+------------------------------------------------------------------------------
|  Description : This function sets the IP read and write buffer
|
|  Parameters  : in_TxLen          - size of transmission buffer
|                in_RxLen          - size of receiver buffer
|
|  Return      : FALSE             - either the read or write buffer could not
|                                    be set.
|                TRUE              - OK
|
+------------------------------------------------------------------------------
*/
LOCAL BOOL L_SetSocketBuffer (USHORT  in_TxLen, USHORT in_RxLen)
{
  int in_TxLen1 = (int) in_TxLen;
  int in_RxLen1 = (int) in_RxLen;
#ifndef _PSOS_
  int nodelay = TRUE;
#if defined (_VXWORKS_) || defined (_LINUX_) || defined (_SOLARIS_)
  struct linger nolinger = {0, 0};
#else
  int linger = TRUE;
#endif
  int rc=0;

  if (in_TxLen > 0)
  {
    if ( (rc=setsockopt(L_ClientData.Socket, SOL_SOCKET, SO_SNDBUF,
                   (char*)&in_TxLen1, sizeof(in_TxLen1))) != 0)
    {
#ifdef _VXWORKS_
      rc = errnoGet ();
#else
  #if defined (_LINUX_) || defined (_SOLARIS_)
      rc = errno;
  #else
      rc = WSAGetLastError();
  #endif
#endif
#if defined _NUCLEUS_ || defined _TOOLS_
      printf("SOCKET: setsockopt() returned error code %d\n", rc); 
#endif
      return FALSE ;
    }
  }
#if defined (_VXWORKS_) || defined (_LINUX_) || defined (_SOLARIS_)
  if (setsockopt(L_ClientData.Socket, SOL_SOCKET, SO_LINGER,
               (char*)&nolinger, sizeof(nolinger)))
#else
  if (setsockopt(L_ClientData.Socket, SOL_SOCKET, (int)SO_DONTLINGER,
               (char*)&linger, sizeof(linger)))
#endif
    return FALSE ;
  if (setsockopt(L_ClientData.Socket, IPPROTO_TCP, TCP_NODELAY,
               (char*)&nodelay, sizeof(nodelay)))
    return FALSE ;

#else /* PSOS */
  tst_socket_in_TxLen = in_TxLen;
#endif /* PSOS */
  if (in_RxLen > 0)
  {
    if (setsockopt(L_ClientData.Socket, SOL_SOCKET, SO_RCVBUF,
                   (char *) &in_RxLen1, sizeof(in_RxLen1)))
      return FALSE ;
  }

  return TRUE ;
}

/*
+------------------------------------------------------------------------------
|  Function    : L_Disconnect
+------------------------------------------------------------------------------
|  Description : This function is called when the connection to the peer entity
|                is lost. If the release signal is set a signal is generated.
|
|  Parameters  :  -
|
|  Return      :  -
|
+------------------------------------------------------------------------------
*/
LOCAL void L_Disconnect (void)
{

  L_ClientData.Connect = FALSE;
  if (L_ClientData.EnabledSignals != INVALID_SIGNALTYPE)
  {
    SocketSignal.SignalType = DRV_SIGTYPE_DISCONNECT;
    SocketSignal.DrvHandle = L_ClientData.SocketHandle;
    (*L_ClientData.Callback)(&SocketSignal) ;
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : L_AsyncSelect
+------------------------------------------------------------------------------
|  Description : This function waits (blocking) for either something to read
|                or an execption on the socket.
|
|  Parameters  : -
|
|  Return      : FALSE             - failure on the socket
|                TRUE              - a read event was signalled
|
+------------------------------------------------------------------------------
*/
LOCAL BOOL L_AsyncSelect (void)
{
  fd_set  fd_r ;
  fd_set  fd_e ;
  int     status, rc;

  /* wait for possibility to read */
  FD_ZERO(&fd_r);
  FD_SET(L_ClientData.Socket, &fd_r);
  FD_ZERO(&fd_e);
  FD_SET(L_ClientData.Socket, &fd_e);

  status = select(FD_SETSIZE, &fd_r, NULL, &fd_e, (struct timeval *) 0);

  if (status > 0)
  {
    if (!FD_ISSET (L_ClientData.Socket, &fd_e))
    {
      if (L_ClientData.Callback != NULL)
      {
        if (FD_ISSET (L_ClientData.Socket, &fd_r))
        {
          SIZETYPE size ;

          if ((rc = GETREADSIZE( L_ClientData.Socket, &size)) != 0 || !size)
          {
#ifdef SOCK_TRACE
            if (fh_sock != -1)
            {
              char sstop[10];
              ULONG stop = GetTickCount();

              sprintf(sstop, "%03d:%03d",  (stop/1000) % 1000, stop % 1000);
              sprintf(stbuf, "reset at %s rc=%d size=%d left %d \n",
                              sstop, rc, size,
                              wrbuf_pos-wrbuf);
              write (fh_sock, stbuf, strlen(stbuf));
/*              close(fh_sock);
              fh_sock = -1; */
            }
#endif
            os_SuspendTask ( 0, 2 );
            CLOSE_SOCKET (L_ClientData.Socket);
            L_ClientData.Socket = INVALID_SOCKET;
            wrbuf_pos = wrbuf;
            sock_state = SST_PL0;
            return FALSE ;
          }

          /* Indicate that it is possible to read something */
          if (L_ClientData.EnabledSignals & DRV_SIGTYPE_READ)
          {
            SocketSignal.SignalType = DRV_SIGTYPE_READ;
            SocketSignal.DrvHandle = L_ClientData.SocketHandle;
            (*L_ClientData.Callback)(&SocketSignal) ;
          }
        }
      }
      return TRUE ;
    }
  }
  return FALSE;
}

/*
+------------------------------------------------------------------------------
|  Function    : L_ClientThread
+------------------------------------------------------------------------------
|  Description : This function is a thread used if the driver is configured to
|                be a client.
|
|  Parameters  : -
|
|  Return      : -
|
+------------------------------------------------------------------------------
*/
void L_ClientThread (T_HANDLE Handle, ULONG Value )
{
  L_ThreadActive = TRUE ;

  L_SetSocketBuffer (L_DCB.tx_buffer_size, L_DCB.rx_buffer_size) ;

  /*-------------------------------------
    Listen what's goning on on the socket
    -------------------------------------*/
  while (L_ThreadActive)
    if (!L_AsyncSelect())
    {
      L_ThreadActive = FALSE ;
      L_Disconnect() ;
    }
}


/*
+------------------------------------------------------------------------------
|  Function    : L_ServerThread
+------------------------------------------------------------------------------
|  Description : This function is a thread used if the driver is configured to
|                be a server. It serves only one client. It cycles between
|                two modes, wait for a client to connect and listening on the
|                connection for any further action.
|                The thread exits when the flag L_ThreadActive is set to false.
|
|  Parameters  : -
|
|  Return      : -
|
+------------------------------------------------------------------------------
*/
void L_ServerThread (T_HANDLE TaskHandle, ULONG Value)
{
  BOOL BufferSizeSet;
  SOCKADDR_IN local_sin;                      /* Local socket - internet style */
#ifdef _VXWORKS_
  SOCKADDR_IN clientAddr;                     /* client */
  int sockAddrSize = sizeof (struct sockaddr_in);

  memset ((char *) &local_sin, 0, sockAddrSize);
  local_sin.sin_len = (u_char) sockAddrSize;
#endif/* _VXWORKS_ */
  L_ClientData.Listener = socket (AF_INET, SOCK_STREAM, 0);
  if (L_ClientData.Listener == INVALID_SOCKET)
    goto error;

  if (L_DCB.port == SOCKET_INVALID_PORT)
    goto error;

  local_sin.sin_addr.s_addr = htonl(INADDR_ANY);
  local_sin.sin_family = AF_INET;
  local_sin.sin_port = htons(L_DCB.port);      /* Convert to network ordering */

  /*-------------------------------------------
    Associate an address with a socket. (bind)
    -------------------------------------------*/
#ifdef _PSOS_
  if (bind (L_ClientData.Listener, (struct sockaddr_in*) &local_sin, sizeof(local_sin)) != 0)
#else
#ifdef _VXWORKS_
  /* Look at the following cast of local_sin.
   * This is from VxWorks Network 5.4 Programmer’s Guide, example 7-1, page 131
   */
  if (bind (L_ClientData.Listener, (struct sockaddr*) &local_sin, sockAddrSize) == ERROR)
#else /* _VXWORKS_ */
  if (bind (L_ClientData.Listener, (const struct sockaddr FAR *) &local_sin, sizeof(local_sin)) != 0)
#endif /* _VXWORKS_ */
#endif
  {
    CLOSE_SOCKET (L_ClientData.Listener);
    goto error;
  }
#ifdef _VXWORKS_
  if (listen (L_ClientData.Listener, MAX_PENDING_CONNECTS) == ERROR)
#else /* _VXWORKS_ */
  if (listen (L_ClientData.Listener, MAX_PENDING_CONNECTS) != 0)
#endif /* _VXWORKS_ */
  {
    CLOSE_SOCKET (L_ClientData.Listener);
    goto error;
  }

  BufferSizeSet = FALSE ;
  L_ThreadActive = TRUE ;

  while (L_ThreadActive)
  {
    /*-------------------------------------
      Wait for somebody to connect
      -------------------------------------*/
    if (L_ClientData.Socket != INVALID_SOCKET)
    {
#ifdef SOCK_TRACE
      if (fh_sock != -1)
      {
        char sstop[10];
        ULONG stop = GetTickCount();

        sprintf(sstop, "%03d:%03d",  (stop/1000) % 1000, stop % 1000);
        sprintf(stbuf, "close at %s socket=%d\n",
                        sstop, L_ClientData.Socket);
        write (fh_sock, stbuf, strlen(stbuf));
/*      close(fh_sock);
        fh_sock = -1;   */
      }
#endif
      CLOSE_SOCKET (L_ClientData.Socket);
      L_ClientData.Socket = INVALID_SOCKET;
      wrbuf_pos = wrbuf;
      sock_state = SST_PL0;
    }
#ifdef _VXWORKS_
    if ((L_ClientData.Socket = accept (L_ClientData.Listener,
                                       (struct sockaddr *) &clientAddr,
                                       &sockAddrSize)) != ERROR)
#else /* _VXWORKS_ */
    L_ClientData.Socket = accept(L_ClientData.Listener, NULL, NULL) ;
    if (L_ClientData.Socket != INVALID_SOCKET)
#endif /* _VXWORKS_ */
    {
#ifdef SOCK_TRACE
      if (fh_sock == -1)
        fh_sock = open("SOCK_S.dbg", O_WRONLY| O_TEXT| O_TRUNC| O_CREAT, 0666);
      if (fh_sock != -1)
      {
        char sstop[10];
        ULONG stop = GetTickCount();

        sprintf(sstop, "%03d:%03d",  (stop/1000) % 1000, stop % 1000);
        sprintf(stbuf, "accept at %s socket=%d listener=%d\n",
                        sstop, L_ClientData.Socket, L_ClientData.Listener);
        write (fh_sock, stbuf, strlen(stbuf));
      }
#endif

#ifdef _PSOS_
      {
        ULONG tid;
        int err;
        /* for pSOS */
        /* wait for TST task */
        while( t_ident( FRM_TST_NAME, 0, &tid ) != 0 )
          tm_wkafter( 10 );

        tst_socket = shr_socket( L_ClientData.Socket, (int)tid );
        if( tst_socket < 0 )
        {
          err = errno;
          goto error;
        }
      }
#endif

      if (!BufferSizeSet)
        L_SetSocketBuffer (L_DCB.tx_buffer_size, L_DCB.rx_buffer_size) ;

      /* Signalisiere Connect */
      L_ClientData.Connect = TRUE;
      if (L_ClientData.EnabledSignals & DRV_SIGTYPE_CONNECT)
      {
        SocketSignal.SignalType = DRV_SIGTYPE_CONNECT;
        SocketSignal.DrvHandle = L_ClientData.SocketHandle;
        (*L_ClientData.Callback)(&SocketSignal) ;
      }

      /*-------------------------------------
        Listen what's goning on on the socket
        -------------------------------------*/
      while (L_ThreadActive)
        if (!L_AsyncSelect())
        {
          L_Disconnect() ;
          break ;
        }
    }
  }

error:
#ifndef OLD_FRAME
  for(;;)
    os_SuspendTask( 0, 1000 );
#endif
}

/*
+------------------------------------------------------------------------------
|  Function    : socket_Create
+------------------------------------------------------------------------------
|  Description : This function is used to set up the driver to act as a
|                server. The function tries to initialize the socket, creates
|                a thread in which it awaits first awaits the establishement
|                of a connection by a client. As soon as a client has
|                connected a signal (SOCKET_CONNECTED) is generated (call
|                socket_SetSignal() to activate a signal). From this time the
|                driver is able to send data (socket_write()) to the client and
|                to read received data (socket_read()). To get notified about
|                the reception of data the apropriate signal has to be set.
|                In the case of a successful completion the driver returns
|                DRV_OK.
|                If the driver is already busy DRV_INPROCESS is returned.
|                If the driver is not configured, the function returns
|                DRV_ NOTCONFIGURED.
|
|  Parameters  : -
|
|  Return      : DRV_OK             - Function successful
|                DRV_INPROCESS      - The driver is currently reading data.
|                                     The data is incomplete.
|                DRV_NOTCONFIGURED  - The driver is not yet configured
|                SOCKET_ERRORUNSPEC - Error occured during initialization
|
+------------------------------------------------------------------------------
*/
LOCAL UBYTE L_CreateServer (void)
{
  if (!L_CreateThread (L_ServerThread))
  {
    CLOSE_SOCKET (L_ClientData.Listener);
    return SOCKET_ERRUNSPEC;
  }
#ifdef _TOOLS_
  printf("SOCKET: now listening on port %i ...\n",L_DCB.port); 
#endif

  return DRV_OK ;
}
/*
+------------------------------------------------------------------------------
| Function    : socket_WriteToOS
+------------------------------------------------------------------------------
| Description : This function is used to write data to the driver of operating
|               system.
|               The parameter thr_BufferSize contains the number of
|               characters to write. In the case of a successful completion,
|               the function returns DRV_OK.
|
| Parameters  : in_BufferPtr          - This parameter points to the buffer
|                                       that is passed to the driver for
|                                       further processing
|               thr_BufferSize        - number of characters to write.
|
| Return      : DRV_OK            - Function successful
|               DRV_INPROCESS     - Driver is busy writing data
|               SOCKET_NOCONNECT  - Connection not available
|
+------------------------------------------------------------------------------
*/
LOCAL UBYTE socket_WriteToOS (void*  in_BufferPtr, USHORT thr_BufferSize)
{
  int             err;
  USHORT          c_written;
#ifndef _PSOS_
  fd_set          fd_w;
  fd_set          fd_e;
  struct timeval  tv;

  if (L_ClientData.Socket == INVALID_SOCKET)
    return SOCKET_NOCONNECT;

  if ( L_ClientData.Connect == FALSE )
    return SOCKET_NOCONNECT ;

  tv.tv_sec = (int)(L_DCB.tx_timeout_msec / 1000) ;
  tv.tv_usec = SEND_TIMEOUTUSEC ;

  FD_ZERO(&fd_w);
  FD_SET(L_ClientData.Socket, &fd_w);
  FD_ZERO(&fd_e);
  FD_SET(L_ClientData.Socket, &fd_e);

  if (select(FD_SETSIZE, NULL, &fd_w, &fd_e, &tv) <= 0)
  {
#ifdef SOCKET_DEBUG
    char buffer[200];
    char *ptr = in_BufferPtr;
    char c = ptr[21];
    fp = fopen(inprocessfile, "at");
    if ( *ptr == 'P' )
      ptr[21] = 0;
    else
      ptr[thr_BufferSize] = 0;
    strcpy (buffer, "errno:" );    
    sprintf (&buffer[6], "%8d", WSAGetLastError() );
    buffer[14] = ' ';
    memcpy (&buffer[15], ptr, (thr_BufferSize)+1);
    fprintf (fp, "%s\n", buffer );
    ptr[21] = c;
    fclose(fp);
#endif  /* SOCKET_DEBUG */
#ifdef _VXWORKS_
    err = errnoGet ();
#else /* _VXWORKS_ */
    err = errno;
    printf("SOCKET: socket write failed with error code: %d\n",err );
#endif /* _VXWORKS_ */

    return DRV_INPROCESS ;
  }

  if (FD_ISSET (L_ClientData.Socket, &fd_e))
  {
#ifdef SOCKET_DEBUG
    char buffer[200];
    char *ptr = in_BufferPtr;
    char c = ptr[21];
    fp = fopen(noconnectfile1, "at");
    if ( *ptr == 'P' )
      ptr[21] = 0;
    else
      ptr[thr_BufferSize] = 0;
    strcpy (buffer, "errno:" );
    sprintf (&buffer[6], "%8d", WSAGetLastError() );
    buffer[14] = ' ';
    memcpy (&buffer[15], ptr, (thr_BufferSize)+1);
    fprintf (fp, "%s\n", buffer );
    ptr[21] = c;
    fclose(fp);
#endif  /* SOCKET_DEBUG */
    return SOCKET_NOCONNECT ;
  }

  /*---------------------------------
    Send the data
    ---------------------------------*/
  c_written = (USHORT) WRITE_SOCKET(L_ClientData.Socket, in_BufferPtr, (USHORT)thr_BufferSize);

  if (c_written == (USHORT)SOCKET_ERROR || c_written != thr_BufferSize)
  {
#ifdef SOCKET_DEBUG
    char buffer[200];
    char *ptr = in_BufferPtr;
    char c = ptr[21];
    fp = fopen(noconnectfile2, "at");
    if ( *ptr == 'P' )
      ptr[21] = 0;
    else
      ptr[thr_BufferSize] = 0;
    strcpy (buffer, "errno:" );
    sprintf (&buffer[6], "%8d", WSAGetLastError() );
    buffer[14] = ' ';
    memcpy (&buffer[15], ptr, (thr_BufferSize)+1);
    fprintf (fp, "%s\n", buffer );
    ptr[21] = c;
    fclose(fp);
#endif  /* SOCKET_DEBUG */
    return SOCKET_NOCONNECT ;
  }

#ifdef SOCK_TRACE
  if (fh_sock != -1)
  {
    char sstop[10];
    ULONG stop = GetTickCount();

    sprintf(sstop, "%03d:%03d",  (stop/1000) % 1000, stop % 1000);
    sprintf(stbuf, "sent %d at %s\n",
                 thr_BufferSize,
                 sstop);
    write (fh_sock, stbuf, strlen(stbuf));
  }
#endif
  return DRV_OK ;

#else

  /* pSOS */

  /*---------------------------------
    Send the data
    ---------------------------------*/
  c_written = (USHORT) WRITE_SOCKET(tst_socket, in_BufferPtr, (USHORT)thr_BufferSize);

  if (c_written == (USHORT)SOCKET_ERROR || c_written != thr_BufferSize)
  {
    err = errno;
    return SOCKET_NOCONNECT ;
  }

  return DRV_OK ;
#endif  /* _PSOS_ */
}

/*==== PUBLIC FUNCTIONS =====================================================*/

/*
+------------------------------------------------------------------------------
|  Function    : socket_Exit
+------------------------------------------------------------------------------
|  Description : The function is called when the driver functionality is no
|                longer required. The function "de-allocates" the resources
|                and releases active connections. The driver terminates
|                regardless of any outstanding data to be sent.
|
|  Parameters  : -
|
|  Return      : -
|
+------------------------------------------------------------------------------
*/
void socket_Exit (void)
{
  os_DestroyTask ( 0, L_ClientData.ThreadID );
  socket_Close() ;
}


/*
+------------------------------------------------------------------------------
|  Function    : socket_Open
+------------------------------------------------------------------------------
|  Description : This function is used to establish a connection to server or
|                activate the driver to act as a server, using the settings of
|                the socket_DCB. A hostname must be specified to open a
|                connection to a server, in this case the driver runs in the
|                client mode. If no hostname is specified the driver will run in
|                server mode. In the server mode it serves a single client. As
|                soon as a client is connected the CONNECT signal is generated.
|                In case of a successful completion the driver is no able to send
|                data (socket_write()) to the peer entity and to read data
|                received from the peer entity (socket_read()). To get notified
|                about the reception of data the appropriate signal has to be set
|                (socket_SetSignal()).
|                In the case of a successful completion the driver returns DRV_OK.
|                If the driver is already busy DRV_INPROCESS is returned.
|                If the driver is not configured, the function returns
|                DRV_NOTCONFIGURED.
|                If an error occurs while establishing the requested mode, the
|                function returns SOCKET_ERRUNSPEC.
|
|  Parameters  : -
|
|  Return      : DRV_OK            - Function successful
|                DRV_INPROCESS     - The driver is currently reading data.
|                                    The data is incomplete.
|                DRV_NOTCONFIGURED - The driver is not yet configured
|                SOCKET_ERRUNSPEC  - Error occured during initialization
|
+------------------------------------------------------------------------------
*/
USHORT socket_Open (void)
{
  SOCKADDR_IN dest_sin;  /* DESTination Socket INternet */
  SOCKET      connectsocket;
#ifdef _VXWORKS_
  int sockAddrSize;
#else /* _VXWORKS_ */
  #if defined (_LINUX_) || defined (_SOLARIS_)
    struct hostent* phe;
  #else
    PHOSTENT    phe;
  #endif
#endif /* _VXWORKS_ */
  if (L_ThreadActive)
  {
    return DRV_INPROCESS ;
  }

  /*---------------------------------
    if no hostname is specified we
    open as a server
    ---------------------------------*/
  if (!*L_DCB.hostname)
  {
    return L_CreateServer() ;
  }

  connectsocket = socket (AF_INET, SOCK_STREAM, 0);
  if (connectsocket == INVALID_SOCKET)
  {
    return SOCKET_ERRUNSPEC;
  }

#ifdef _PSOS_

  if ( !gethostbyname(L_DCB.hostname, phe))
  {
    CLOSE_SOCKET(connectsocket);
    return SOCKET_ERRUNSPEC;
  }

#else /* _PSOS_ */
  #ifdef _VXWORKS_
    sockAddrSize = sizeof (struct sockaddr_in);
    memset((char *) &dest_sin, 0, sockAddrSize);
    dest_sin.sin_len = (u_char) sockAddrSize;
    dest_sin.sin_family = AF_INET;
    dest_sin.sin_port = htons(L_DCB.port);
    if (((dest_sin.sin_addr.s_addr = inet_addr (L_DCB.hostname)) == ERROR) &&
    ((dest_sin.sin_addr.s_addr = hostGetByName (L_DCB.hostname)) == ERROR))
    {
      CLOSE_SOCKET(connectsocket);
      return SOCKET_ERRUNSPEC;
    }
  #else /* _VXWORKS_ */
  phe = gethostbyname(L_DCB.hostname);
  if (!phe)
  {
#ifdef _TOOLS_
    printf("SOCKET: host %s not found ;-(\n",L_DCB.hostname); 
#endif
    CLOSE_SOCKET(connectsocket);
    return SOCKET_ERRUNSPEC;
  }
  #endif /* _VXWORKS_ */
#endif  /* _PSOS_ */

#ifndef _VXWORKS_
  memset(&dest_sin, 0, sizeof(struct sockaddr_in));
  memcpy((char*)&(dest_sin.sin_addr), phe->h_addr, (unsigned int)((int)(phe->h_length)));
  dest_sin.sin_family = AF_INET;
  if ( L_DCB.config & TI_MODE )
    dest_sin.sin_port = L_DCB.port;
  else
    dest_sin.sin_port = htons(L_DCB.port);
#endif /* _VXWORKS_ */

#ifdef _PSOS_
  if (connect (connectsocket, (struct sockaddr_in*) &dest_sin, sizeof(dest_sin)))
#else /* _PSOS_ */
  #ifdef _VXWORKS_
  if (connect (connectsocket, (struct sockaddr*) &dest_sin, sockAddrSize) == ERROR)
  #else /* _VXWORKS_ */
    #if defined (_LINUX_) || defined (_SOLARIS_)
      if (connect (connectsocket, (struct sockaddr*) &dest_sin,
                   sizeof(dest_sin)))
    #else
      if (connect (connectsocket, (const PSOCKADDR) &dest_sin,
                   sizeof(dest_sin)))
    #endif
  #endif /* _VXWORKS_ */
#endif /* defined(_PSOS_) || defined(_VXWORKS_) */
  {
#ifdef _TOOLS_
    printf("SOCKET: connection to %s on port %i failed ;-(\n",L_DCB.hostname,L_DCB.port); 
#endif
    CLOSE_SOCKET(connectsocket);
    return SOCKET_ERRUNSPEC;
  }
#ifdef _TOOLS_
  printf("SOCKET: successfully connected to %s on port %i\n",L_DCB.hostname,L_DCB.port); 
#endif

  if (!L_CreateThread (L_ClientThread))
  {
    CLOSE_SOCKET (L_ClientData.Listener);
    return SOCKET_ERRUNSPEC;
  }

  L_ClientData.Socket = connectsocket ;
  L_ClientData.Connect = TRUE;
  if (L_ClientData.EnabledSignals & DRV_SIGTYPE_CONNECT)
  {
    SocketSignal.SignalType = DRV_SIGTYPE_CONNECT;
    SocketSignal.DrvHandle = L_ClientData.SocketHandle;
    (*L_ClientData.Callback)(&SocketSignal) ;
  }
  return DRV_OK ;
}

/*
+------------------------------------------------------------------------------
|  Function    : socket_Close
+------------------------------------------------------------------------------
|  Description : This function is used by a client to close the connection or
|                by server to shut down the server functionality.
|                In case of a successful completion the connection is shutdown
|                and neither socket_Read nor socket_Write will be successful.
|                To get notified about the termination of a connection the
|                appropriate signal has to be set (socket_SetSignal()).
|
|  Parameters  : -
|
|  Return      : -
|
+------------------------------------------------------------------------------
*/
USHORT socket_Close (void)
{
  L_ThreadActive = FALSE ;

#ifdef SOCK_TRACE
  if (fh_sock != -1)
  {
    char sstop[10];
    ULONG stop = GetTickCount();

    sprintf(sstop, "%03d:%03d",  (stop/1000) % 1000, stop % 1000);
    sprintf(stbuf, "socket_Close at %s socket=%d listener=%d\n",
                    sstop, L_ClientData.Socket, L_ClientData.Listener);
    write (fh_sock, stbuf, strlen(stbuf));
  }
#endif
  if (L_ClientData.Socket != INVALID_SOCKET)
  {
    CLOSE_SOCKET (L_ClientData.Socket);
    L_ClientData.Socket = INVALID_SOCKET;
  }

  if (L_ClientData.Listener != INVALID_SOCKET)
    CLOSE_SOCKET (L_ClientData.Listener);

  L_ClientData.ThreadID = 0 ;
  return DRV_OK;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_Read_with_Timeout
+------------------------------------------------------------------------------
| Description : This function is used to read data from the USART driver.
|               For more details see socket_Read()
|
| Return:       >= 0         - Number of bytes read
|               SOCKET_ERROR - error occurred
+------------------------------------------------------------------------------
*/
LOCAL ULONG socket_Read_with_Timeout( void* destBuf, ULONG reqLen )
{
  ULONG timeout,
        totalBytes = 0,
        stop_timeout ;
  int readBytes,
        attempts = 0;

  /* calculate timeout time */
  GETTIME( stop_timeout );
  stop_timeout = stop_timeout + L_DCB.rx_timeout_msec ;

  /* read nonblocking until requested data is read or timeout */
  do
  {
    readBytes = (SHORT)READ_SOCKET( L_ClientData.Socket,
                                    ((char *)destBuf) + totalBytes,
                                    reqLen - totalBytes );

    if( readBytes < 0 || readBytes == SOCKET_ERROR )
    {
#ifdef SOCKET_DEBUG
#ifndef _PSOS_
      fp = fopen(readerrorfile, "at");
      fprintf (fp, "WSAGetLastError returned: %8d", WSAGetLastError() );
      fclose(fp);
#endif
#endif  /* SOCKET_DEBUG */
      return 0;
    }
    else
    {
      totalBytes += (unsigned int)readBytes;

      GETTIME (timeout) ;

      if( totalBytes < reqLen )
      {
        if (attempts++)   /* try to get data by two consecutive accesses
                             then sleep (but this should not be necessary) */
        {
#ifdef OLD_FRAME
          Sleep (1) ;
#else
          os_SuspendTask ( 0, 1 );
#endif
        }
      }
      else
      {
        return totalBytes;
      }
    }
  } while (timeout < stop_timeout);

  return 0;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_Read
+------------------------------------------------------------------------------
| Description : This function is used to read data from the USART driver. The
|               data is copied into the buffer to which out_BufferPtr points.
|               The parameter *thr_BufferSizePtr contains the size of the
|               buffer in characters.
|               In the case of a successful completion, the driver's buffer
|               is cleared. The driver keeps the data available when calling
|               the function drv_Look().
|               If the driver is not configured, the function returns
|               DRV_NOTCONFIGURED.
|
|               NOTE: When calling the function with a buffer size of 0, the
|                     function will return DRV_OK. The size of the buffer
|                     needed to store the available data is stored in the
|                     parameter *thr_BufferSizePtr. In this case, the
|                     out_BufferPtr can be set to NULL.
|
|
| Parameters  : out_BufferPtr         - This parameter points to the buffer
|                                       wherein the data is to be copied
|               thr_BufferSizePtr     - On call: number of characters to
|                                       read. If the function returns DRV_OK,
|                                       it contains the number of characters
|                                       read. If the function returns
|                                       DRV_INPROCESS, it contains 0.
|
| Return      : DRV_OK             - Function successful
|               DRV_INPROCESS      - The driver is currently reading data.
|                                    The data is incomplete.
|               DRV_NOTCONFIGURED  - The driver is not yet configured
|               SOCKET_NOCONNECT   - Connection not available
|
+------------------------------------------------------------------------------
*/
USHORT socket_Read (void* out_BufferPtr, ULONG*  thr_BufferSizePtr)
{
#ifdef _VXWORKS_
  SIZETYPE           pending_data_size = 0;
#else
  ULONG              pending_data_size = 0;
#endif
  ULONG              bytesToRead, bytesReq;
  int                rc;
  static USHORT      packet_size;
  union
  {
    USHORT s;
    UBYTE  b[2];
  } conv;
#ifdef SOCKET_DEBUG
  static ULONG BytesRead = 0;
#endif
#ifdef SOCK_TRACE
  static ULONG start;
  static ULONG lasttime = 0;
#endif

  bytesReq = *thr_BufferSizePtr;
  *thr_BufferSizePtr = 0;      /* no bytes returned yet */
  while ( (rc = (GETREADSIZE( L_ClientData.Socket, &pending_data_size ))) == 0 &&
          pending_data_size > 0 )
  {
    switch (sock_state)
    {
      case SST_PL0:
#ifdef SOCK_TRACE
        GETTIME (start) ;
#endif
        packet_size = 0;
        if (pending_data_size >= 2)
        {
          bytesToRead = 2;
          sock_state = SST_DATA0;
        }
        else
        {
          bytesToRead = 1;
          sock_state = SST_PL1;
        }
        if ( !(L_DCB.config & TI_MODE) )
        {
          if (socket_Read_with_Timeout(&packet_size, bytesToRead) != bytesToRead)
          {
            sock_state = SST_PL0;
            return SOCKET_NOCONNECT;
          }
        }
        break;
      case SST_PL1:
        if (socket_Read_with_Timeout(((char*)&packet_size)+1, 1) != 1)
        {
          sock_state = SST_PL0;
          return SOCKET_NOCONNECT;
        }
        sock_state = SST_DATA0;
        break;
      case SST_DATA0:
        if ( !(L_DCB.config & TI_MODE) && isLittleEndian )
        {
          conv.b[0] = *(((UBYTE*)&packet_size)+1); /* LSB */
          conv.b[1] = *  (UBYTE*)&packet_size;     /* MSB */
          packet_size = conv.s;
        }  
        /*lint -fallthrough*/
      case SST_DATAx:
        /* now read the packet payload or a part of it */
        if ( L_DCB.config & TI_MODE )
        {
          bytesToRead = (pending_data_size < bytesReq) ? pending_data_size : bytesReq;
        }
        else
        {
          bytesToRead = (packet_size < bytesReq) ? packet_size : bytesReq;
          if (pending_data_size < bytesToRead)
            bytesToRead = pending_data_size;
        }
        if( (*thr_BufferSizePtr =
              socket_Read_with_Timeout( ((char *)out_BufferPtr), bytesToRead )) !=
              bytesToRead )
        {
          *thr_BufferSizePtr = 0;
          if ( !(L_DCB.config & TI_MODE) )
            sock_state = SST_PL0;
          return SOCKET_NOCONNECT;
        }
#ifdef SOCK_TRACE
        if (fh_sock != -1 && sock_state == SST_DATA0 && bytesToRead >= 13)
        {
          char split[10+1];
          char sstart[20], sstop[20];
          static ULONG c_received = 0;
          ULONG len = packet_size;
          ULONG stop = GetTickCount();

          if (((char*)out_BufferPtr)[13] == 'T')
          {
            memcpy(split, ((char*)out_BufferPtr)+9, 7);
            split[7] = '\0';
          }
          else
          {
            memcpy(split, ((char*)out_BufferPtr)+9, 4);
            split[4] = '\0';
          }
          if (start != lasttime)
          {
            if (lasttime - start > 11)
              sprintf(sstart, "->%d %03d:%03d\n", c_received, (start/1000) % 1000, start % 1000);
            else
              sprintf(sstart, "%03d:%03d\n", (start/1000) % 1000, start % 1000);
            c_received = 0;
          }
          c_received += packet_size + 2;
          if (start != stop)
          {
            sprintf(sstop, "->%d %03d:%03d\n", c_received, (stop/1000) % 1000, stop % 1000);
            c_received = 0;
          }
          sprintf(stbuf, "%s%d %s\n%s",
                       (start != lasttime) ? sstart : "",
                       packet_size + 2, split,
                       (start != stop) ? sstop : "");
          write (fh_sock, stbuf, strlen(stbuf));
          lasttime = stop;
        }
#endif
        if ( !(L_DCB.config & TI_MODE) )
        {
          if (*thr_BufferSizePtr == packet_size)
            sock_state = SST_PL0;
          else
          {
            packet_size -= (USHORT)*thr_BufferSizePtr;
            sock_state = SST_DATAx;
          }
        }
        return DRV_OK;
    /*lint -e527 suppress Warning -- Unreachable */
        break;
    /*lint +e527 */
    }  /* switch */
  } /* while */
  if (rc)
  {
    sock_state = SST_PL0;
    return SOCKET_NOCONNECT;
  }
#ifdef SOCKET_DEBUG
  {
    static char Buffer[2000];
    memcpy (Buffer+BytesRead,out_BufferPtr,*thr_BufferSizePtr);
    BytesRead += *thr_BufferSizePtr;
    if ( (Buffer[0] == 'P') )
    {
      OS_TIME time;
      char c = Buffer[21];
      fp = fopen(logfile, "at");
      Buffer[21] = 0;
      os_GetTime ( 0, &time );
      fprintf (fp, "P%ld IN:  %s\n", time/10, &Buffer[9] );
      Buffer[21] = c;
      fclose(fp);
    }
  }

#endif
  return DRV_OK ;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_Write
+------------------------------------------------------------------------------
| Description : This function is used to write data to the driver. The
|               parameter *thr_BufferSizePtr contains the number of
|               characters to write. In the case of a successful completion,
|               the function returns DRV_OK.
|               If the data cannot be written because the storage capacity of
|               the driver has been exhausted, the function returns
|               DRV_BUFFER_FULL and the maximum number of characters that can
|               be written in *thr_BufferSizePtr.
|               If the driver is currently busy writing data and therefore
|               cannot accept further data to be written, it returns
|               DRV_INPROCESS and sets the parameter *thr_BufferSizePtr to 0.
|               If the driver is not configured, the function returns
|               DRV_ NOTCONFIGURED.
|
|               NOTE:  When calling the function with a buffer size of 0, the
|                     function will return the number of characters that can be
|                     written in the parameter *thr_BufferSizePtr. In this
|                     case, the in_BufferPtr can be set to NULL.
|
| Parameters  : in_BufferPtr          - This parameter points to the buffer
|                                       that is passed to the driver for
|                                       further processing
|               thr_BufferSizePtr     - On call: number of characters to
|                                       write. If the function returns
|                                       DRV_BUFFER_FULL, it contains the
|                                       maximum number of characters that can
|                                       be written. If the function returns
|                                       DRV_OK, it contains the number of
|                                       characters written. If the function
|                                       returns DRV_INPROCESS, it contains 0.
|
|
| Return      : DRV_OK            - Function successful
|               DRV_BUFFER_FULL   - Not enough space
|               DRV_INPROCESS     - Driver is busy writing data
|               DRV_NOTCONFIGURED - The driver is not yet configured
|               SOCKET_NOCONNECT  - Connection not available
|
+------------------------------------------------------------------------------
*/
/*lint -esym(613,auxb) suppress warning possibly use off NULL pointer auxb */
/*lint -esym(644,auxb) suppress warning possibly not initialized */
/*lint -e668           suppress warning possibly passing NULL pointer to memcpy */
USHORT socket_Write (void*  in_BufferPtr, ULONG*  thr_BufferSizePtr)
{
  UBYTE    rc = DRV_OK;
  int   max_len, rest;
  char *   auxb;
  ULONG buffer_size;
  
  buffer_size = *thr_BufferSizePtr & ~PRIM_FLAG_MASK;

#ifndef _PSOS_
#ifdef SOCKET_DEBUG
  UBYTE Prim = 0;
  {
    char *ptr = in_BufferPtr;
    if ( *ptr == 'P' )
    {
      OS_TIME time;
      char c = ptr[21];
      fp = fopen(logfile, "at");
      ptr[21] = 0;
      os_GetTime ( 0, &time );
      fprintf (fp, "P%ld OUT: %s\n", time/10, &ptr[9] );
      ptr[21] = c;
      fclose(fp);
      Prim = 1;
    }
  }
#endif  /* SOCKET_DEBUG */
#else /* _PSOS */
  if( !tst_socket_initialized )
  {
    if (tst_socket_in_TxLen > 0)
    {
      int nodelay = TRUE;
      if (setsockopt(tst_socket, SOL_SOCKET, SO_SNDBUF,
                     (char*)&tst_socket_in_TxLen, sizeof(tst_socket_in_TxLen)))
        return DRV_INITFAILURE;
      if (setsockopt(tst_socket, IPPROTO_TCP, TCP_NODELAY,
                   (char*)&nodelay, sizeof(nodelay)))
        return DRV_INITFAILURE;
    }
    tst_socket_initialized = 1;
  }
#endif /* _PSOS_ */

  if ( L_DCB.config & TI_MODE )
  {
    /* add TI-MUX header */
    auxb=(char*)malloc(buffer_size+2);
    memcpy((void*)(auxb+2),in_BufferPtr,buffer_size);
    auxb[0]=19; /* assigned to L23 */
    auxb[1]=(char)buffer_size;
    in_BufferPtr=auxb;
    buffer_size+=2;
  }

  max_len = (L_DCB.tx_buffer_size < WRBUF_LEN) ? L_DCB.tx_buffer_size : WRBUF_LEN;
  if (max_len < (int)buffer_size)
  {
#ifndef _PSOS_
#ifdef SOCKET_DEBUG
    char buffer[200];
    char *ptr = in_BufferPtr;
    char c = ptr[21];
    fp = fopen(bufferfullfile, "at");
    if ( *ptr == 'P' )
      ptr[21] = 0;
    else
      ptr[buffer_size] = 0;
    strcpy (buffer, "errno:" );
    sprintf (&buffer[6], "%8d", WSAGetLastError() );
    buffer[14] = ' ';
    memcpy (&buffer[15], ptr, (buffer_size)+1);
    fprintf (fp, "%s\n", buffer );
    ptr[21] = c;
    fclose(fp);
#endif  /* SOCKET_DEBUG */
#endif  /* _PSOS_ */
    if ( L_DCB.config & TI_MODE )
      free(auxb);
    *thr_BufferSizePtr = (unsigned int)max_len |
                                      (*thr_BufferSizePtr & PRIM_FLAG_MASK) ;
    return DRV_BUFFER_FULL ;
  }

  rest = MAX_ETH_LEN - (wrbuf_pos - wrbuf);
  if (buffer_size + 2 >= (unsigned int)rest)    /* more than maximum ethernet packet size needed ?*/
  {
  /*---------------------------------
    send the buffer
    ---------------------------------*/
    rc = socket_WriteToOS(wrbuf, (USHORT)(wrbuf_pos - wrbuf));
    wrbuf_pos = wrbuf;

    if (rc != DRV_OK)
    {
      if ( L_DCB.config & TI_MODE )
        free(auxb);
      *thr_BufferSizePtr = (*thr_BufferSizePtr & PRIM_FLAG_MASK) ;
      return rc;
    }
  }

  if ( !(L_DCB.config & TI_MODE) )
  {
    /*------------------------------------------------
      put the size of the data into buffer (MSB first)
      ------------------------------------------------*/
    if (isLittleEndian)
    {
      *wrbuf_pos     = *(((UBYTE*)&buffer_size)+1); /* MSB */
      *(wrbuf_pos+1) = *  (UBYTE*)&buffer_size;     /* LSB */
    }
    else
      *((USHORT*)wrbuf_pos) = (USHORT)buffer_size;
    wrbuf_pos += 2;
  }
  /*---------------------------------
    put the data itself into buffer
    ---------------------------------*/
  memcpy(wrbuf_pos, in_BufferPtr, (size_t)buffer_size);
  wrbuf_pos += buffer_size;

  if (socket_flush)
  {
    /*---------------------------------
      send the buffer
      ---------------------------------*/
    rc = socket_WriteToOS(wrbuf, (USHORT)(wrbuf_pos - wrbuf));
    wrbuf_pos = wrbuf;
    socket_flush = 0;

    if (rc != DRV_OK)
    {
      *thr_BufferSizePtr = (*thr_BufferSizePtr & PRIM_FLAG_MASK) ;
    }
  }

  if ( L_DCB.config & TI_MODE )
  {
    free(auxb);
  }
  return rc ;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_Flush
+------------------------------------------------------------------------------
| Description : This function flushes the socket write buffer where data
|               is stored until a complete packet can be sent or the
|               transmission is forced by an external setting of the
|               socket_flush flag. 
|
| Parameters  : ---
|
| Return      : DRV_OK                  - Function completed successfully
|
+------------------------------------------------------------------------------
*/
USHORT socket_Flush ( void )
{
ULONG len = 0;

  socket_flush = 1;
  return ( socket_Write(NULL, &len) );
}

/*
+------------------------------------------------------------------------------
| Function    : socket_SetSignal
+------------------------------------------------------------------------------
| Description : This function is used to define a single or multiple signals
|               that is/are indicated to the process when the event identified
|               in the signal information data type as SignalType occurs. The
|               USART uses only the standard signals defined in [C_8415.0026].
|               To remove a signal, call the function socket_ResetSignal().
|               If one of the parameters of the signal information data is
|               invalid, the function returns DRV_INVALID_PARAMS.
|               If no signal call-back function has been defined at the time
|               of initialization, the driver returns DRV_SIGFCT_NOTAVAILABLE.
|
| Parameters  : in_SignalIDPtr        - Pointer to the signal information
|                                       data
|
| Return      : DRV_OK                  - Function completed successfully
|               DRV_INVALID_PARAMS      - One or more parameters are out of
|                                         range or invalid
|               DRV_SIGFCT_NOTAVAILABLE - Event signaling functionality is
|                                         not available
|
+------------------------------------------------------------------------------
*/
USHORT socket_SetSignal (USHORT SignalType)
{
  if (L_ClientData.Callback == NULL)
    return DRV_SIGFCT_NOTAVAILABLE ;

  if (SignalType & ALLOWED_SOCKET_SIGNALS)
    L_ClientData.EnabledSignals |= SignalType;
  else
    return DRV_INVALID_PARAMS ;
  return DRV_OK ;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_ResetSignal
+------------------------------------------------------------------------------
| Description : This function is used to remove previously set single or
|               multiple signals. The signals that are removed are identified
|               by the Signal Information Data element called SignalType. All
|               other elements of the Signal Information Data must be
|               identical to the signal(s) that are to be removed (process
|               handle and signal value). If the SignalID provided cannot be
|               located, the function returns DRV_INVALID_PARAMS.
|               If no signal call-back function has been defined at the time
|               of initialization, the driver returns DRV_SIGFCT_NOTAVAILABLE.
|
| Parameters  : in_SignalIDPtr        - Pointer to the signal information
|                                       data
|
| Return      : DRV_OK                    - Function completed successfully
|               DRV_INVALID_PARAMS        - One or more parameters are out of
|                                           range or invalid
|               DRV_SIGFCT_NOTAVAILABLE   - Event signaling functionality is
|                                           not available
|
+------------------------------------------------------------------------------
*/
USHORT socket_ResetSignal (USHORT SignalType)
{
  if (L_ClientData.Callback == NULL)
    return DRV_SIGFCT_NOTAVAILABLE ;

  if (SignalType & ALLOWED_SOCKET_SIGNALS)
    L_ClientData.EnabledSignals &= ~SignalType;
  else
    return DRV_INVALID_PARAMS ;

  return DRV_OK ;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_SetConfig
+------------------------------------------------------------------------------
| Description : This function is used to configure the driver (port,
|               transmission rate, flow control, etc). The driver can be
|               configured at any one time before a connection is opened. The
|               parameters that can be configured are included in the USART's
|               device control block socket_DCB_Type. For detailed information
|               about the contents of the device control block, refer to
|               Chapter 2.1.1. If any value of the configuration is out of
|               range or invalid in combination with any other value of the
|               configuration, the function returns DRV_INVALID_PARAMS.
|               Call the socket_GetConfig() function to retrieve the driver's
|               configuration.
|               The driver needs to be configured after initialization. Only
|               the following functions can be called while the driver is not
|               configured: socket_Clear, socket_SetSignal and socket_GetSignal.
|               All other functions return DRV_NOTCONFIGURED.
|
| Parameters  : in_DCBPtr             - Pointer to the driver control block
|
| Return      : DRV_OK                    - Function completed successfully
|               DRV_INVALID_PARAMS        - One or more parameters are out of
|                                           range or invalid
|
+------------------------------------------------------------------------------
*/
USHORT socket_SetConfig (char* in_DCBPtr)
{
char token [SOCKET_MAX_LEN_HOSTNAME+1];
USHORT port;
unsigned int len;

  if ( (len = GetNextToken (in_DCBPtr, token, " #")) == 0)
  {
    return DRV_INVALID_PARAMS;
  }
  else
  {
    in_DCBPtr += (len+1);
  }

  port = (USHORT)atoi(token);

  if ( (len = GetNextToken (in_DCBPtr, token, " #")) == 0) 
  {
    return DRV_INVALID_PARAMS;
  }
  else
  {
    in_DCBPtr += (len+1);
  }

  if ( !strcmp ( DRV_TI_MODE, token ) )
  {
    L_DCB.config = TI_MODE;
    sock_state = SST_DATAx;
  }
  else if ( !strcmp ( DRV_DEFAULT, token ) )
  {
    L_DCB.config = 0;
    sock_state = SST_PL0;
  }
  else
    return DRV_INVALID_PARAMS;

  if ( (len = GetNextToken (in_DCBPtr, token, " #")) == 0 ) 
  {
#ifdef _TOOLS_
    gethostname (token,SOCKET_MAX_LEN_HOSTNAME);
#endif /* _TOOLS_ */
  }

  if (L_ThreadActive)
  {
    /* check if host is already used */
    if (strcmp(L_DCB.hostname, token)==0 && L_DCB.port==port) 
    {
#ifdef _TOOLS_
      printf("SOCKET: keeping connection to host %s on port %u\n",L_DCB.hostname,L_DCB.port); 
#endif
      return DRV_OK;
    }

    socket_Close();
  }
  
  L_DCB.port=port;
  strcpy ( L_DCB.hostname, token);
  
  if (socket_Open () != DRV_OK)
  {
    return DRV_INVALID_PARAMS ;
  }

  return DRV_OK ;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_GetConfig
+------------------------------------------------------------------------------
| Description : This function is used to retrieve the configuration of the
|               driver. The configuration is returned in the driver control
|               block to which the pointer provided out_DCBPtr points. For
|               detailed information about the contents of the device control
|               block, refer to Chapter 2.1.1. The configuration can be
|               requested at any one time.
|               If the driver is not configured, the function returns
|               DRV_ NOTCONFIGURED.
|
| Parameters  : out_DCBPtr                - Pointer to the driver control block
|
| Return      : DRV_OK                    - Function completed successfully
|               DRV_INVALID_PARAMS        - One or more parameters are out of
|                                           range or invalid
|
+------------------------------------------------------------------------------
*/
USHORT socket_GetConfig (socket_DCB_Type*  out_DCBPtr)
{
  if (out_DCBPtr == NULL)
    return DRV_INVALID_PARAMS ;

  memcpy (out_DCBPtr, &L_DCB, sizeof (L_DCB)) ;
  return DRV_OK ;
}

/*
+------------------------------------------------------------------------------
| Function    : socket_Init
+------------------------------------------------------------------------------
| Description : The function initializes the internal data of the driver.
|               The function returns DRV_INITIALIZED if the driver has
|               already been initialized and is ready to be used or is
|               already in use. In the case of an initialization failure,
|               i.e. the driver cannot be used, the function returns
|               DRV_INITFAILURE.
|
| Parameters  : in_SignalCBPtr    - This parameter points to the function that
|                                   is called at the time an event that is to
|                                   be signaled occurs. This value can be set
|                                   to NULL if event signaling should not be
|                                   possible.
|
| Return      : DRV_OK            - Initialization successful
|               DRV_INITIALIZED   - Driver already initialized
|               DRV_INITFAILURE   - Initialization failed
|
+------------------------------------------------------------------------------
*/
GLOBAL USHORT socket_Init ( USHORT DrvHandle, T_DRV_CB_FUNC CallbackFunc, T_DRV_EXPORT const **DrvInfo )
{
static const T_DRV_EXPORT Socket_Info =
{
  "SOCKET",
  0,
  {
#ifdef _TOOLS_
    socket_Init,
#endif
    socket_Exit,
    socket_Read,
    socket_Write,
    NULL,
    NULL,
    socket_Flush,
    socket_SetSignal,
    socket_ResetSignal,
    socket_SetConfig,
    NULL,
    NULL,
  }
};
union
{
  USHORT s;
  UBYTE b[2];
} test;

#ifndef _PSOS_
#if defined (_VXWORKS_) || defined (_LINUX_) || defined (_SOLARIS_)
/* ToDo: vxWorks-libs initiieren
  sockLib
  inetLib
*/
#else /* _VXWORKS_ */
  WSADATA WSAData;
  if (WSAStartup(MAKEWORD(1,1), &WSAData))
    return DRV_INITFAILURE;
#endif /* _VXWORKS_ */
#endif

  test.s = 1;
  isLittleEndian = (test.b[0] == 1);

  L_ThreadActive = FALSE ;
  L_DCB.config = 0;
  L_DCB.port = 6392;
  L_DCB.tx_buffer_size = TX_BUFFER_SIZE;
  L_DCB.rx_buffer_size = RX_BUFFER_SIZE ;
  L_DCB.tx_timeout_msec = TX_TIMEOUT_MSEC;
  L_DCB.rx_timeout_msec = RX_TIMEOUT_MSEC;
  *L_DCB.hostname = 0 ;
  L_ClientData.Connect = FALSE;
  L_ClientData.SocketHandle = DrvHandle;
  L_ClientData.Socket = INVALID_SOCKET ;
  L_ClientData.Listener = INVALID_SOCKET ;
#ifdef _PSOS_
  tst_socket = INVALID_SOCKET ;
  tst_socket_initialized = 0;
  tst_socket_in_TxLen = 0;
#endif

  L_ClientData.Callback   = CallbackFunc ;
  L_ClientData.EnabledSignals = ALLOWED_SOCKET_SIGNALS ;
  *DrvInfo = &Socket_Info;
#ifdef SOCKET_DEBUG
  fp = fopen(logfile,"wt");
  fclose(fp);
  fp = fopen(inprocessfile,"wt");
  fclose(fp);
  fp = fopen(bufferfullfile,"wt");
  fclose(fp);
  fp = fopen(noconnectfile1,"wt");
  fclose(fp);
  fp = fopen(noconnectfile2,"wt");
  fclose(fp);
  fp = fopen(noconnectfile3,"wt");
  fclose(fp);
  fp = fopen(readerrorfile,"wt");
  fclose(fp);
#endif /* SOCKET_DEBUG */
  return DRV_OK ;
}

/*==== END OF FILE ==========================================================*/