فهرست منبع

reworked connection handling mechanisms in a way that any CIP object can be a connection target

Alois Zoitl 14 سال پیش
والد
کامیت
72b95e33e3

+ 5 - 1
bin/pc/Makefile

@@ -31,6 +31,8 @@ SOURCES= \
 	../../src/cip/cipidentity.c \
 	../../src/cip/cipethernetlink.c \
 	../../src/cip/appcontype.c \
+	../../src/cip/cipclass3connection.c \
+	../../src/cip/cipioconnection.c \
 	../../src/enet_encap/cpf.c \
 	../../src/enet_encap/endianconv.c \
 	../../src/enet_encap/encap.c \
@@ -48,6 +50,8 @@ OBJECTS= \
 	../../src/cip/cipidentity.o \
 	../../src/cip/cipethernetlink.o \
 	../../src/cip/appcontype.o \
+	../../src/cip/cipclass3connection.o \
+	../../src/cip/cipioconnection.o \
 	../../src/enet_encap/cpf.o \
 	../../src/enet_encap/endianconv.o \
 	../../src/enet_encap/encap.o
@@ -56,7 +60,7 @@ SPLINTFLAGS = -I../../src \
 	-I../../src/enet_encap \
 	-I../../src/cip \
 	-I../../src/ports/platform-pc \
-	+posixlib
+	+posixlib -skipposixheaders
 
 
 EXECUTABLE=opener

+ 4 - 0
bin/pc/Makefile.release

@@ -27,6 +27,8 @@ SOURCES= \
 	../../src/cip/cipidentity.c \
 	../../src/cip/cipethernetlink.c \
 	../../src/cip/appcontype.c \
+	../../src/cip/cipclass3connection.c \
+	../../src/cip/cipioconnection.c \
 	../../src/enet_encap/cpf.c \
 	../../src/enet_encap/endianconv.c \
 	../../src/enet_encap/encap.c \
@@ -44,6 +46,8 @@ OBJECTS= \
 	../../src/cip/cipidentity.o \
 	../../src/cip/cipethernetlink.o \
 	../../src/cip/appcontype.o \
+	../../src/cip/cipclass3connection.o \
+	../../src/cip/cipioconnection.o \
 	../../src/enet_encap/cpf.o \
 	../../src/enet_encap/endianconv.o \
 	../../src/enet_encap/encap.o

+ 5 - 2
src/cip/appcontype.c

@@ -369,7 +369,9 @@ closeAllConnsForInputWithSameType(EIP_UINT32 pa_unInputPoint,
               pstToDelete->ConnectionPath.ConnectionPoint[0],
               pstToDelete->ConnectionPath.ConnectionPoint[1], enClosed);
 
-          closeConnection(pstToDelete); /* will remove the connection from the active connection list */
+          //FIXME check if this is ok
+          pstToDelete->m_pfCloseFunc(pstToDelete);
+          //closeConnection(pstToDelete); /* will remove the connection from the active connection list */
         }
       else
         {
@@ -381,6 +383,7 @@ closeAllConnsForInputWithSameType(EIP_UINT32 pa_unInputPoint,
 void closeAllConnections(void){
   S_CIP_ConnectionObject *pstRunner = g_pstActiveConnectionList;
   while (NULL != pstRunner){
+      //FIXME check if m_pfCloseFunc would be suitable
     closeConnection(pstRunner);
     /* Close connection will remove the connection from the list therefore we
      * need to get again the start until there is no connection left
@@ -390,7 +393,7 @@ void closeAllConnections(void){
 
 }
 
-bool
+EIP_BOOL8
 connectionWithSameConfigPointExists(EIP_UINT32 pa_unConfigPoint)
 {
   S_CIP_ConnectionObject *pstRunner = g_pstActiveConnectionList;

+ 1 - 1
src/cip/appcontype.h

@@ -67,7 +67,7 @@ void closeAllConnections(void);
 /*! \brief Check if there is an established connection that uses the same
  * config point.
  */
-bool
+EIP_BOOL8
 connectionWithSameConfigPointExists(EIP_UINT32 pa_unConfigPoint);
 
 #endif /* APPCONTYPE_H_ */

+ 65 - 0
src/cip/cipclass3connection.c

@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2011, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include "cipclass3connection.h"
+#include "cipconnectionmanager.h"
+
+S_CIP_ConnectionObject *
+getFreeExplicitConnection(void);
+
+/**** Global variables ****/
+
+/*! Array of the available explicit connections */
+S_CIP_ConnectionObject g_astExplicitConnections[OPENER_CIP_NUM_EXPLICIT_CONNS];
+
+/**** Implementation ****/
+int
+establishClass3Connection(struct CIP_ConnectionObject *pa_pstConnObj,
+    EIP_UINT16 *pa_pnExtendedError)
+{
+  int nRetVal = EIP_OK;
+  EIP_UINT32 nTmp;
+
+  //TODO add check for transport type trigger
+  //if (0x03 == (g_stDummyConnectionObject.TransportTypeTrigger & 0x03))
+
+  S_CIP_ConnectionObject *pstExplConn = getFreeExplicitConnection();
+
+  if (NULL == pstExplConn)
+    {
+      nRetVal = CIP_ERROR_CONNECTION_FAILURE;
+      *pa_pnExtendedError = CIP_CON_MGR_ERROR_NO_MORE_CONNECTIONS_AVAILABLE;
+    }
+  else
+    {
+      copyConnectionData(pstExplConn, pa_pstConnObj);
+
+      nTmp = pstExplConn->CIPProducedConnectionID;
+      generalConnectionConfiguration(pstExplConn);
+      pstExplConn->CIPProducedConnectionID = nTmp;
+      pstExplConn->m_eInstanceType = enConnTypeExplicit;
+      pstExplConn->sockfd[0] = pstExplConn->sockfd[1] = EIP_INVALID_SOCKET;
+      /* set the connection call backs */
+      pstExplConn->m_pfCloseFunc = removeFromActiveConnections;
+      /* explicit connection have to be closed on time out*/
+      pstExplConn->m_pfTimeOutFunc = removeFromActiveConnections;
+
+      addNewActiveConnection(pstExplConn);
+    }
+  return nRetVal;
+}
+
+S_CIP_ConnectionObject *
+getFreeExplicitConnection(void)
+{
+  int i;
+  for (i = 0; i < OPENER_CIP_NUM_EXPLICIT_CONNS; i++)
+    {
+      if (g_astExplicitConnections[i].State == CONN_STATE_NONEXISTENT)
+        return &(g_astExplicitConnections[i]);
+    }
+  return NULL;
+}

+ 26 - 0
src/cip/cipclass3connection.h

@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2011, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#ifndef CIPCLASS3CONNECTION_H_
+#define CIPCLASS3CONNECTION_H_
+
+
+#include <opener_api.h>
+
+/** \brief Check if Class3 connection is available and if yes setup all data.
+ *
+ * This function can be called after all data has been parsed from the forward open request
+ * @param pa_pstConnObj pointer to the connection object structure holding the parsed data from the forward open request
+ * @param pa_pnExtendedError the extended error code in case an error happened
+ * @return general status on the establishment
+ *    - EIP_OK ... on success
+ *    - On an error the general status code to be put into the response
+ */
+int
+establishClass3Connection(struct CIP_ConnectionObject *pa_pstConnObj, EIP_UINT16 *pa_pnExtendedError);
+
+
+#endif /* CIPCLASS3CONNECTION_H_ */

+ 85 - 818
src/cip/cipconnectionmanager.c

@@ -4,7 +4,6 @@
  *
  ******************************************************************************/
 #include <string.h>
-
 #include "opener_user_conf.h"
 #include "cipconnectionmanager.h"
 #include "cipcommon.h"
@@ -12,11 +11,13 @@
 #include "ciperror.h"
 #include "endianconv.h"
 #include "opener_api.h"
-#include "cpf.h"
-#include "cipassembly.h"
 #include "encap.h"
 #include "cipidentity.h"
 #include "trace.h"
+#include "cipclass3connection.h"
+#include "cipioconnection.h"
+#include "cipassembly.h"
+#include "cpf.h"
 #include "appcontype.h"
 
 /* values needed from cipidentiy */
@@ -25,9 +26,6 @@ extern EIP_UINT16 DeviceType;
 extern EIP_UINT16 ProductCode;
 extern S_CIP_Revision Revison;
 
-/* values need from tcpipinterface */
-extern EIP_UINT32 g_nMultiCastAddress;
-
 #define CIP_CONN_PATH_INVALID 1
 #define CIP_CONN_PATH_CONFIGURATION 2
 #define CIP_CONN_PATH_CONSUMPTION 3
@@ -42,21 +40,21 @@ extern EIP_UINT32 g_nMultiCastAddress;
 #define FORWARD_OPEN_HEADER_LENGTH 36         /* the length in bytes of the forward open command specific data till the start of the connection path (including con path size)*/
 #define EQLOGICALPATH(x,y) (((x)&0xfc)==(y))
 
-/*macros for comparing sequence numbers according to CIP spec vol 2 3-4.2*/
-#define SEQ_LEQ32(a, b) ((int)((a) - (b)) <= 0)
-#define SEQ_GEQ32(a, b) ((int)((a) - (b)) >= 0)
+static const int scg_nNumConnectableObjects = 2
+    + OPENER_CIP_NUM_APPLICATION_SPECIFIC_CONNECTABLE_OBJECTS;
 
-/* similar macros for comparing 16 bit sequence numbers */
-#define SEQ_LEQ16(a, b) ((short)((a) - (b)) <= 0)
-#define SEQ_GEQ16(a, b) ((short)((a) - (b)) >= 0) 
-
-/*The port to be used per default for I/O messages on UDP.*/
-#define OPENER_EIP_IO_UDP_PORT   0x08AE
+typedef struct
+{
+  EIP_UINT32 m_nClassID;
+  TConnOpenFunc m_pfOpenFunc;
+} TConnMgmHandling;
 
 /* global variables private */
-
-/*! buffer for holding the run idle information.*/
-EIP_UINT32 g_nRunIdleState;
+/*!List holding information on the object classes and open/close function
+ * pointers to which connections may be established.
+ */
+TConnMgmHandling g_astConnMgmList[2
+    + OPENER_CIP_NUM_APPLICATION_SPECIFIC_CONNECTABLE_OBJECTS];
 
 /*! List holding all currently active connections*/
 S_CIP_ConnectionObject *g_pstActiveConnectionList = NULL;
@@ -64,15 +62,8 @@ S_CIP_ConnectionObject *g_pstActiveConnectionList = NULL;
 /*! buffer connection object needed for forward open */
 S_CIP_ConnectionObject g_stDummyConnectionObject;
 
-/*!Array of the available explicit connections */
-S_CIP_ConnectionObject g_astExplicitConnections[OPENER_CIP_NUM_EXPLICIT_CONNS];
+/*! value holds the connection ID's "incarnation ID" in the upper 16 bits     */
 
-/* buffers for the config data coming with a forward open request.
- */
-EIP_UINT8 *g_pnConfigDataBuffer = NULL;
-unsigned int g_unConfigDataLen = 0;
-
-/*! value holds the connection ID's "incarnation ID" in the upper 16 bits */
 EIP_UINT32 g_nIncarnationID;
 
 /* private functions */
@@ -99,31 +90,6 @@ assembleFWDCloseResponse(EIP_UINT16 pa_ConnectionSerialNr,
     S_CIP_MR_Request * pa_MRRequest, S_CIP_MR_Response * pa_MRResponse,
     EIP_UINT16 pa_nExtErrorCode, EIP_UINT8 * pa_msg);
 
-S_CIP_ConnectionObject *
-getFreeExplicitConnection(void);
-
-void
-generalConnectionConfiguration(S_CIP_ConnectionObject *pa_pstConnObj);
-
-/* producing multicast connection have to consider the rules that apply for
- * application connection types.
- */
-EIP_STATUS
-openProducingMulticastConnection(S_CIP_ConnectionObject *pa_pstConnObj,
-    S_CIP_CPF_Data *pa_CPF_data);
-
-EIP_STATUS
-OpenMulticastConnection(int pa_direction,
-    S_CIP_ConnectionObject *pa_pstConnObj, S_CIP_CPF_Data *pa_CPF_data);
-
-EIP_STATUS
-OpenConsumingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
-    S_CIP_CPF_Data *pa_CPF_data);
-
-int
-OpenProducingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
-    S_CIP_CPF_Data *pa_CPF_data, EIP_UINT16 *pa_pnExtendedError);
-
 /** \brief check if the data given in the connection object match with an already established connection
  * 
  * The comparison is done according to the definitions in the CIP specification Section 3-5.5.2:
@@ -159,64 +125,12 @@ checkElectronicKeyData(EIP_UINT8 pa_nKeyFormat, S_CIP_KeyData *pa_pstKeyData,
  * @return general status on the establishment
  *    - EIP_OK ... on success
  *    - On an error the general status code to be put into the response
- */
-EIP_UINT8
+ */EIP_UINT8
 parseConnectionPath(S_CIP_ConnectionObject *pa_pstConnObj,
     S_CIP_MR_Request *pa_MRRequest, EIP_UINT16 *pa_pnExtendedError);
 
-/** \brief Check if Class3 connection is available and if yes setup all data.
- *
- * This function can be called after all data has been parsed from the forward open request
- * @param pa_pstConnObj pointer to the connection object structure holding the parsed data from the forward open request
- * @param pa_pnExtendedError the extended error code in case an error happened
- * @return general status on the establishment
- *    - EIP_OK ... on success
- *    - On an error the general status code to be put into the response
- */
-int
-establishClass3Connection(S_CIP_ConnectionObject *pa_pstConnObj,
-    EIP_UINT16 *pa_pnExtendedError);
-
-/** \brief Setup all data in order to establish an IO connection
- * 
- * This function can be called after all data has been parsed from the forward open request
- * @param pa_pstConnObjData pointer to the connection object structure holding the parsed data from the forward open request
- * @param pa_pnExtendedError the extended error code in case an error happened
- * @return general status on the establishment
- *    - EIP_OK ... on success
- *    - On an error the general status code to be put into the response
- */
-int
-establishIOConnction(S_CIP_ConnectionObject *pa_pstConnObjData,
-    EIP_UINT16 *pa_pnExtendedError);
-
-/* Regularly close the IO connection. If it is an exclusive owner or input only
- * connection and in charge of the connection a new owner will be searched
- */
-void
-closeIOConnection(S_CIP_ConnectionObject *pa_pstConnObjData);
-
-/*!  Send the data from the produced CIP Object of the connection via the socket of the connection object
- *   on UDP.
- *      @param pa_pstConnection  pointer to the connection object
- *      @return status  EIP_OK .. success
- *                     EIP_ERROR .. error
- */
-EIP_STATUS
-sendConnectedData(S_CIP_ConnectionObject *pa_pstConnection);
-
-void
-addNewActiveConnection(S_CIP_ConnectionObject *pa_pstConn);
-
-void
-removeFromeActiveConnections(S_CIP_ConnectionObject *pa_pstConn);
-
-void
-handleIOConnectionTimeOut(S_CIP_ConnectionObject *pa_pstConn);
-
-EIP_UINT16
-handleConfigData(S_CIP_Class *pa_pstAssemblyClass,
-    S_CIP_ConnectionObject *pa_pstIOConnObj);
+TConnMgmHandling *
+getConnMgmEntry(EIP_UINT32 pa_nClassId);
 
 int
 GETPADDEDLOGICALPATH(unsigned char **x)
@@ -247,8 +161,7 @@ GETPADDEDLOGICALPATH(unsigned char **x)
  * A unique connectionID is formed from the boot-time-specified "incarnation ID"
  * and the per-new-connection-incremented connection number/counter.
  * @return new connection id
- */
-EIP_UINT32
+ */EIP_UINT32
 getConnectionId()
 {
   static EIP_UINT32 nConnectionId = 18;
@@ -283,6 +196,9 @@ Connection_Manager_Init(EIP_UINT16 pa_nUniqueConnID)
 
   g_nIncarnationID = ((EIP_UINT32) pa_nUniqueConnID) << 16;
 
+  addConnectableObject(CIP_MESSAGE_ROUTER_CLASS_CODE, establishClass3Connection);
+  addConnectableObject(CIP_ASSEMBLY_CLASS_CODE, establishIOConnction);
+
   return EIP_OK;
 }
 
@@ -327,43 +243,13 @@ handleReceivedConnectedData(EIP_UINT8 * pa_pnData, int pa_nDataLength,
                       /* only inform assembly object if the sequence counter is greater or equal */
                       pstConnectionObject->EIPSequenceCountConsuming
                           = g_stCPFDataItem.stAddr_Item.Data.SequenceNumber;
-                      /* check class 1 sequence number*/
-                      if ((pstConnectionObject->TransportTypeTrigger & 0x0F)
-                          == 1)
-                        {
-                          EIP_UINT16 nSequenceBuf = ltohs(
-                              &(g_stCPFDataItem.stDataI_Item.Data));
-                          if (SEQ_LEQ16(nSequenceBuf, pstConnectionObject->SequenceCountConsuming))
-                            {
-                              return EIP_OK; /* no new data for the assembly */
-                            }
-                          pstConnectionObject->SequenceCountConsuming
-                              = nSequenceBuf;
-                          g_stCPFDataItem.stDataI_Item.Length -= 2;
-                        }
 
-                      if (g_stCPFDataItem.stDataI_Item.Length > 0)
+                      if (NULL != pstConnectionObject->m_pfReceiveDataFunc)
                         {
-                          /* we have no heartbeat connection */
-                          if (OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER)
-                            {
-                              EIP_UINT32 nRunIdleBuf = ltohl(
-                                  &(g_stCPFDataItem.stDataI_Item.Data));
-                              if (g_nRunIdleState != nRunIdleBuf)
-                                {
-                                  IApp_RunIdleChanged(nRunIdleBuf);
-                                }
-                              g_nRunIdleState = nRunIdleBuf;
-                              g_stCPFDataItem.stDataI_Item.Length -= 4;
-                            }
-
-                          if (notifyAssemblyConnectedDataReceived(
-                              pstConnectionObject->p_stConsumingInstance,
+                          return pstConnectionObject->m_pfReceiveDataFunc(
+                              pstConnectionObject,
                               g_stCPFDataItem.stDataI_Item.Data,
-                              g_stCPFDataItem.stDataI_Item.Length) != 0)
-                            {
-                              return EIP_ERROR;
-                            }
+                              g_stCPFDataItem.stDataI_Item.Length);
                         }
                     }
                 }
@@ -394,6 +280,7 @@ ForwardOpen(S_CIP_Instance *pa_pstInstance, S_CIP_MR_Request *pa_MRRequest,
 {
   EIP_UINT16 nConnectionStatus = CIP_CON_MGR_SUCCESS;
   EIP_UINT32 tmp;
+  TConnMgmHandling *pstConnMgmEntry;
 
   (void) pa_pstInstance; /*suppress compiler warning */
 
@@ -488,17 +375,17 @@ ForwardOpen(S_CIP_Instance *pa_pstInstance, S_CIP_MR_Request *pa_MRRequest,
     }
 
   /*parsing is now finished all data is available and check now establish the connection */
-  if (0x03 == (g_stDummyConnectionObject.TransportTypeTrigger & 0x03))
+  pstConnMgmEntry = getConnMgmEntry(
+      g_stDummyConnectionObject.ConnectionPath.ClassID);
+  if (NULL != pstConnMgmEntry)
     {
-      /*if we are here all values are checked and correct
-       buffer the T_to_O connection ID as we have to take the one given from the callee */
-      tmp = establishClass3Connection(&g_stDummyConnectionObject,
+      tmp = pstConnMgmEntry->m_pfOpenFunc(&g_stDummyConnectionObject,
           &nConnectionStatus);
     }
   else
     {
-      tmp
-          = establishIOConnction(&g_stDummyConnectionObject, &nConnectionStatus);
+      tmp = EIP_ERROR;
+      nConnectionStatus = CIP_CON_MGR_ERROR_INVALID_CONNECTION_POINT;
     }
 
   if (EIP_OK != tmp)
@@ -512,226 +399,11 @@ ForwardOpen(S_CIP_Instance *pa_pstInstance, S_CIP_MR_Request *pa_MRRequest,
     {
       OPENER_TRACE_INFO("connection manager: connect succeeded\n");
       /* in case of success the g_pstActiveConnectionList points to the new connection */
-      g_pstActiveConnectionList->State = CONN_STATE_ESTABLISHED;
       return assembleFWDOpenResponse(g_pstActiveConnectionList, pa_MRResponse,
           CIP_ERROR_SUCCESS, 0, pa_msg);
     }
 }
 
-/*   EIP_STATUS OpenPointToPointConnection(S_CIP_CPF_Data *pa_CPF_data, S_CIP_CM_Object *pa_pstCMObject, INT8 pa_direction, int pa_index)
- *   open a Point2Point connection dependent on pa_direction.
- *      pa_pstCMObject	pointer to registered Object in ConnectionManager.
- *      pa_index	index of the connection object
- *  return status
- * 		 0 .. success
- *      	-1 .. error
- */
-
-EIP_STATUS
-OpenConsumingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
-    S_CIP_CPF_Data *pa_CPF_data)
-{
-  /*static EIP_UINT16 nUDPPort = 2222; TODO think on improving the udp port assigment for point to point connections */
-  int j;
-  struct sockaddr_in addr;
-  int newfd;
-
-  j = 0;
-  if (pa_CPF_data->AddrInfo[0].TypeID == 0)
-    { /* it is not used yet */
-      j = 0;
-    }
-  else if (pa_CPF_data->AddrInfo[1].TypeID == 0)
-    {
-      j = 1;
-    }
-
-  addr.sin_family = AF_INET;
-  addr.sin_addr.s_addr = INADDR_ANY;
-  /*addr.in_port = htons(nUDPPort++);*/
-  addr.sin_port = htons(OPENER_EIP_IO_UDP_PORT);
-
-  newfd = IApp_CreateUDPSocket(CONSUMING, &addr); /* the address is only needed for bind used if consuming */
-  if (newfd == EIP_INVALID_SOCKET)
-    {
-      OPENER_TRACE_ERR("cannot create UDP socket in OpenPointToPointConnection\n");
-      return EIP_ERROR;
-    }
-
-  pa_pstConnObj->m_stOriginatorAddr = addr; /* store the address of the originator for packet scanning */
-  addr.sin_addr.s_addr = INADDR_ANY; /* restore the address */
-  pa_pstConnObj->sockfd[CONSUMING] = newfd;
-
-  pa_CPF_data->AddrInfo[j].Length = 16;
-  pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_O_TO_T;
-
-  pa_CPF_data->AddrInfo[j].nsin_port = addr.sin_port;
-  /*TODO should we add our own address here? */
-  pa_CPF_data->AddrInfo[j].nsin_addr = addr.sin_addr.s_addr;
-  memset(pa_CPF_data->AddrInfo[j].nasin_zero, 0, 8);
-  pa_CPF_data->AddrInfo[j].nsin_family = htons(AF_INET);
-
-  return EIP_OK;
-}
-
-int
-OpenProducingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
-    S_CIP_CPF_Data *pa_CPF_data, EIP_UINT16 *pa_pnExtendedError)
-{
-  int newfd;
-  in_port_t nPort; 
-  
-  nPort = OPENER_EIP_IO_UDP_PORT; /* the default port to be used if no port information is part of the forward open request */
-
-  if (CIP_ITEM_ID_SOCKADDRINFO_T_TO_O == pa_CPF_data->AddrInfo[0].TypeID)
-    {
-      nPort = pa_CPF_data->AddrInfo[0].nsin_port;
-    }
-  else
-    {
-      if (CIP_ITEM_ID_SOCKADDRINFO_T_TO_O == pa_CPF_data->AddrInfo[1].TypeID)
-        {
-          nPort = pa_CPF_data->AddrInfo[1].nsin_port;
-        }
-    }
-
-  pa_pstConnObj->remote_addr.sin_family = AF_INET;
-  pa_pstConnObj->remote_addr.sin_addr.s_addr = 0; /* we don't know the address of the originate will be set in the IApp_CreateUDPSocket */
-  pa_pstConnObj->remote_addr.sin_port = nPort;
-
-  newfd = IApp_CreateUDPSocket(PRODUCING, &pa_pstConnObj->remote_addr); /* the address is only needed for bind used if consuming */
-  if (newfd == EIP_INVALID_SOCKET)
-    {
-      OPENER_TRACE_ERR("cannot create UDP socket in OpenPointToPointConnection\n");
-      *pa_pnExtendedError = 0x0315; /* miscellaneous*/
-      return CIP_ERROR_CONNECTION_FAILURE;
-    }
-  pa_pstConnObj->sockfd[PRODUCING] = newfd;
-
-  return EIP_OK;
-}
-
-EIP_STATUS
-openProducingMulticastConnection(S_CIP_ConnectionObject *pa_pstConnObj,
-    S_CIP_CPF_Data *pa_CPF_data)
-{
-  S_CIP_ConnectionObject *pstExistingConn = getExistingProdMulticastConnection(
-      pa_pstConnObj->ConnectionPath.ConnectionPoint[1]);
-  int j;
-
-  if (NULL == pstExistingConn)
-    { /* we are the first connection producing for the given Input Assembly */
-      return OpenMulticastConnection(PRODUCING, pa_pstConnObj, pa_CPF_data);
-    }
-  else
-    {
-      /* we need to infrom our originator on the correct connection id */
-      pa_pstConnObj->CIPProducedConnectionID
-          = pstExistingConn->CIPProducedConnectionID;
-    }
-
-  /* we have a connection reuse the data and the socket */
-
-  j = 0; /* allocate an unused sockaddr struct to use */
-  if (g_stCPFDataItem.AddrInfo[0].TypeID == 0)
-    { /* it is not used yet */
-      j = 0;
-    }
-  else if (g_stCPFDataItem.AddrInfo[1].TypeID == 0)
-    {
-      j = 1;
-    }
-
-  if (enConnTypeIOExclusiveOwner == pa_pstConnObj->m_eInstanceType)
-    {
-      /* eclusive owners take the socket and further manage the connection
-       * especially in the case of time outs.
-       */
-      pa_pstConnObj->sockfd[PRODUCING] = pstExistingConn->sockfd[PRODUCING];
-      pstExistingConn->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
-    }
-  else
-    { /* this connection will not produce the data */
-      pa_pstConnObj->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
-    }
-
-  pa_CPF_data->AddrInfo[j].Length = 16;
-  pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_T_TO_O;
-  pa_pstConnObj->remote_addr.sin_family = AF_INET;
-  pa_pstConnObj->remote_addr.sin_port = pa_CPF_data->AddrInfo[j].nsin_port
-      = htons(OPENER_EIP_IO_UDP_PORT);
-  pa_pstConnObj->remote_addr.sin_addr.s_addr
-      = pa_CPF_data->AddrInfo[j].nsin_addr = g_nMultiCastAddress;
-  memset(pa_CPF_data->AddrInfo[j].nasin_zero, 0, 8);
-  pa_CPF_data->AddrInfo[j].nsin_family = htons(AF_INET);
-
-  return EIP_OK;
-}
-
-/*   INT8 OpenMulticastConnection(S_CIP_CPF_Data *pa_CPF_data, S_CIP_CM_Object *pa_pstCMObject, INT8 pa_direction, int pa_index)
- *   open a Multicast connection dependent on pa_direction.
- *      pa_CPF_data	received CPF Data Item.
- *      pa_pstCMObject	pointer to registered Object in ConnectionManager.
- *      pa_direction	flag to indicate if consuming or producing.
- *      pa_index	index of the connection object
- *  return status
- * 		 0 .. success
- *      	-1 .. error
- */
-EIP_STATUS
-OpenMulticastConnection(int pa_direction,
-    S_CIP_ConnectionObject *pa_pstConnObj, S_CIP_CPF_Data *pa_CPF_data)
-{
-  int j;
-  struct sockaddr_in addr;
-  int newfd;
-
-  j = 0; /* allocate an unused sockaddr struct to use */
-  if (g_stCPFDataItem.AddrInfo[0].TypeID == 0)
-    { /* it is not used yet */
-      j = 0;
-    }
-  else if (g_stCPFDataItem.AddrInfo[1].TypeID == 0)
-    {
-      j = 1;
-    }
-
-  addr.sin_family = AF_INET;
-  addr.sin_addr.s_addr = g_nMultiCastAddress;
-  addr.sin_port = htons(OPENER_EIP_IO_UDP_PORT);
-
-  newfd = IApp_CreateUDPSocket(pa_direction, &addr); /* the address is only needed for bind used if consuming */
-  if (newfd == EIP_INVALID_SOCKET)
-    {
-      OPENER_TRACE_ERR("cannot create UDP socket in OpenMulticastConnection\n");
-      return EIP_ERROR;
-    }
-  pa_pstConnObj->sockfd[pa_direction] = newfd;
-
-  pa_CPF_data->AddrInfo[j].Length = 16;
-  if (pa_direction == CONSUMING)
-    {
-      pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_O_TO_T;
-      pa_pstConnObj->m_stOriginatorAddr = addr;
-      addr.sin_addr.s_addr = g_nMultiCastAddress; /* restore the multicast address */
-    }
-  else
-    {
-      pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_T_TO_O;
-      pa_pstConnObj->remote_addr = addr;
-    }
-  pa_CPF_data->AddrInfo[j].nsin_port = addr.sin_port;
-  pa_CPF_data->AddrInfo[j].nsin_addr = addr.sin_addr.s_addr;
-  memset(pa_CPF_data->AddrInfo[j].nasin_zero, 0, 8);
-  pa_CPF_data->AddrInfo[j].nsin_family = htons(AF_INET);
-
-  return EIP_OK;
-}
-
-/*   void ConnectionObjectGeneralConfiguration(int pa_index)
- *   generate ConnectionID and set configuration parameter in connection object.
- *      pa_index	index of the connection object
- */
 void
 generalConnectionConfiguration(S_CIP_ConnectionObject *pa_pstConnObj)
 {
@@ -800,16 +472,19 @@ EIP_STATUS
 ForwardClose(S_CIP_Instance *pa_pstInstance, S_CIP_MR_Request * pa_MRRequest,
     S_CIP_MR_Response * pa_MRResponse, EIP_UINT8 * pa_msg)
 {
-  /* check connection serial number && Vendor ID && OriginatorSerialNr if connection is established */
-  EIP_UINT16 nConnectionStatus =
-      CIP_CON_MGR_ERROR_CONNECTION_NOT_FOUND_AT_TARGET_APPLICATION;
   EIP_UINT16 ConnectionSerialNr, OriginatorVendorID;
   EIP_UINT32 OriginatorSerialNr;
-  S_CIP_ConnectionObject *pstRunner = g_pstActiveConnectionList;
+  S_CIP_ConnectionObject *pstRunner;
+  EIP_UINT16 nConnectionStatus;
 
   /*Suppress compiler warning*/
   (void) pa_pstInstance;
 
+  /* check connection serial number && Vendor ID && OriginatorSerialNr if connection is established */
+  nConnectionStatus
+      = CIP_CON_MGR_ERROR_CONNECTION_NOT_FOUND_AT_TARGET_APPLICATION;
+  pstRunner = g_pstActiveConnectionList;
+
   /* set AddressInfo Items to invalid TypeID to prevent assembleLinearMsg to read them */
   g_stCPFDataItem.AddrInfo[0].TypeID = 0;
   g_stCPFDataItem.AddrInfo[1].TypeID = 0;
@@ -830,15 +505,9 @@ ForwardClose(S_CIP_Instance *pa_pstInstance, S_CIP_MR_Request * pa_MRRequest,
           if ((pstRunner->ConnectionSerialNumber == ConnectionSerialNr)
               && (pstRunner->OriginatorVendorID == OriginatorVendorID)
               && (pstRunner->OriginatorSerialNumber == OriginatorSerialNr))
-            { /* found the corresponding connection object -> close it */
-              if (enConnTypeExplicit != pstRunner->m_eInstanceType)
-                {
-                  closeIOConnection(pstRunner);
-                }
-              else
-                {
-                  closeConnection(pstRunner);
-                }
+            {
+              /* found the corresponding connection object -> close it */OPENER_ASSERT(NULL != pstRunner->m_pfCloseFunc);
+              pstRunner->m_pfCloseFunc(pstRunner);
               nConnectionStatus = CIP_CON_MGR_SUCCESS;
               break;
             }
@@ -884,14 +553,8 @@ manageConnections(void)
                 {
                   /* we have a timed out connection perform watchdog time out action*/
                   OPENER_TRACE_INFO(">>>>>>>>>>Connection timed out\n");
-                  if (enConnTypeExplicit == pstRunner->m_eInstanceType)
-                    { /* explicit connection have to be closed */
-                      closeConnection(pstRunner);
-                    }
-                  else
-                    {
-                      handleIOConnectionTimeOut(pstRunner);
-                    }
+                  OPENER_ASSERT(NULL != pstRunner->m_pfTimeOutFunc);
+                  pstRunner->m_pfTimeOutFunc(pstRunner);
                 }
             }
           /* only if the connection has not timed out check if data is to be send */
@@ -905,8 +568,8 @@ manageConnections(void)
                   pstRunner->TransmissionTriggerTimer -= OPENER_TIMER_TICK;
                   if (pstRunner->TransmissionTriggerTimer <= 0)
                     { /* need to send package */
-                      /* send() */
-                      res = sendConnectedData(pstRunner);
+                      /* send() */OPENER_ASSERT(NULL != pstRunner->m_pfSendDataFunc);
+                      res = pstRunner->m_pfSendDataFunc(pstRunner);
                       if (res == EIP_ERROR)
                         {
                           OPENER_TRACE_ERR(
@@ -924,18 +587,6 @@ manageConnections(void)
   return EIP_OK;
 }
 
-S_CIP_ConnectionObject *
-getFreeExplicitConnection(void)
-{
-  int i;
-  for (i = 0; i < OPENER_CIP_NUM_EXPLICIT_CONNS; i++)
-    {
-      if (g_astExplicitConnections[i].State == CONN_STATE_NONEXISTENT)
-        return &(g_astExplicitConnections[i]);
-    }
-  return NULL;
-}
-
 /*   INT8 assembleFWDOpenResponse(S_CIP_ConnectionObject *pa_pstConnObj, S_CIP_MR_Response * pa_MRResponse, EIP_UINT8 pa_nGeneralStatus, EIP_UINT16 pa_nExtendedStatus,
  void * deleteMeSomeday, EIP_UINT8 * pa_msg)
  *   create FWDOpen response dependent on status.
@@ -1408,223 +1059,6 @@ parseConnectionPath(S_CIP_ConnectionObject *pa_pstConnObj,
   return EIP_OK;
 }
 
-int
-establishClass3Connection(S_CIP_ConnectionObject *pa_pstConnObj,
-    EIP_UINT16 *pa_pnExtendedError)
-{
-  int nRetVal = EIP_OK;
-  EIP_UINT32 nTmp;
-
-  S_CIP_ConnectionObject *pstExplConn = getFreeExplicitConnection();
-
-  if (NULL == pstExplConn)
-    {
-      nRetVal = CIP_ERROR_CONNECTION_FAILURE;
-      *pa_pnExtendedError = CIP_CON_MGR_ERROR_NO_MORE_CONNECTIONS_AVAILABLE;
-    }
-  else
-    {
-      copyConnectionData(pstExplConn, pa_pstConnObj);
-      nTmp = pstExplConn->CIPProducedConnectionID;
-      generalConnectionConfiguration(pstExplConn);
-      pstExplConn->CIPProducedConnectionID = nTmp;
-      pstExplConn->m_eInstanceType = enConnTypeExplicit;
-      pstExplConn->sockfd[0] = pstExplConn->sockfd[1] = EIP_INVALID_SOCKET;
-      addNewActiveConnection(pstExplConn);
-    }
-  return nRetVal;
-}
-
-int
-establishIOConnction(S_CIP_ConnectionObject *pa_pstConnObjData,
-    EIP_UINT16 *pa_pnExtendedError)
-{
-  int O2TConnectionType, T2OConnectionType;
-  S_CIP_attribute_struct *pstAttribute;
-  /* currently we allow I/O connections only to assembly objects */
-  S_CIP_Class *pstAssemblyClass = getCIPClass(CIP_ASSEMBLY_CLASS_CODE); /* we don't need to check for zero as this is handled in the connection path parsing */
-  S_CIP_Instance *pstInstance = NULL;
-
-  S_CIP_ConnectionObject *pstIOConnObj = getIOConnectionForConnectionData(
-      pa_pstConnObjData, pa_pnExtendedError);
-  /*get pointer to the cpf data, currently we have just one global instance of the struct. This may change in the future*/
-  S_CIP_CPF_Data *pstCPF_data = &g_stCPFDataItem;
-
-  if (NULL == pstIOConnObj)
-    {
-      return CIP_ERROR_CONNECTION_FAILURE;
-    }
-
-  O2TConnectionType
-      = (pstIOConnObj->O_to_T_NetworkConnectionParameter & 0x6000) >> 13;
-  T2OConnectionType
-      = (pstIOConnObj->T_to_O_NetworkConnectionParameter & 0x6000) >> 13;
-
-  generalConnectionConfiguration(pstIOConnObj);
-
-  if ((O2TConnectionType == 0) && (T2OConnectionType == 0))
-    { /* this indicates an re-configuration of the connection currently not supported and we should not come here as this is handled in the forwardopen function*/
-
-    }
-  else
-    {
-      int nProducingIndex = 0;
-      int nDataSize;
-      if ((O2TConnectionType != 0) && (T2OConnectionType != 0))
-        { /* we have a producing and consuming connection*/
-          nProducingIndex = 1;
-        }
-
-      pstIOConnObj->p_stConsumingInstance = 0;
-      pstIOConnObj->ConsumedConnectionPathLength = 0;
-      pstIOConnObj->p_stProducingInstance = 0;
-      pstIOConnObj->ProducedConnectionPathLength = 0;
-
-      if (O2TConnectionType != 0)
-        { /*setup consumer side*/
-          if (0 != (pstInstance = getCIPInstance(pstAssemblyClass,
-              pstIOConnObj->ConnectionPath.ConnectionPoint[0])))
-            { /* consuming Connection Point is present */
-              pstIOConnObj->p_stConsumingInstance = pstInstance;
-
-              pstIOConnObj->ConsumedConnectionPathLength = 6;
-              pstIOConnObj->ConsumedConnectionPath.PathSize = 6;
-              pstIOConnObj->ConsumedConnectionPath.ClassID
-                  = pstIOConnObj->ConnectionPath.ClassID;
-              pstIOConnObj->ConsumedConnectionPath.InstanceNr
-                  = pstIOConnObj->ConnectionPath.ConnectionPoint[0];
-              pstIOConnObj->ConsumedConnectionPath.AttributNr = 3;
-
-              pstAttribute = getAttribute(pstInstance, 3);
-              OPENER_ASSERT(pstAttribute != NULL); /* an assembly object should always have an attribute 3 */
-              nDataSize = pstIOConnObj->ConsumedConnectionSize;
-
-              if ((pstIOConnObj->TransportTypeTrigger & 0x0F) == 1)
-                {
-                  /* class 1 connection */
-                  nDataSize -= 2; /* remove 16-bit sequence count length */
-                }
-              if ((OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER) && (nDataSize > 0))
-                { /* we only have an run idle header if it is not an hearbeat connection */
-                  nDataSize -= 4; /* remove the 4 bytes needed for run/idle header */
-                }
-
-              if (((S_CIP_Byte_Array *) pstAttribute->pt2data)->len
-                  != nDataSize)
-                {
-                  /*wrong connection size */
-                  *pa_pnExtendedError
-                      = CIP_CON_MGR_ERROR_INVALID_CONNECTION_SIZE;
-                  return CIP_ERROR_CONNECTION_FAILURE;
-                }
-            }
-          else
-            {
-              *pa_pnExtendedError = CIP_CON_MGR_ERROR_INVALID_CONNECTION_POINT;
-              return CIP_ERROR_CONNECTION_FAILURE;
-            }
-        }
-
-      if (T2OConnectionType != 0)
-        { /*setup producer side*/
-          if (0 != (pstInstance = getCIPInstance(pstAssemblyClass,
-              pstIOConnObj->ConnectionPath.ConnectionPoint[nProducingIndex])))
-            {
-              pstIOConnObj->p_stProducingInstance = pstInstance;
-
-              pstIOConnObj->ProducedConnectionPathLength = 6;
-              pstIOConnObj->ProducedConnectionPath.PathSize = 6;
-              pstIOConnObj->ProducedConnectionPath.ClassID
-                  = pstIOConnObj->ConnectionPath.ClassID;
-              pstIOConnObj->ProducedConnectionPath.InstanceNr
-                  = pstIOConnObj->ConnectionPath.ConnectionPoint[nProducingIndex];
-              pstIOConnObj->ProducedConnectionPath.AttributNr = 3;
-
-              pstAttribute = getAttribute(pstInstance, 3);
-              OPENER_ASSERT(pstAttribute != NULL); /* an assembly object should always have an attribute 3 */
-              nDataSize = pstIOConnObj->ProducedConnectionSize;
-              if ((pstIOConnObj->TransportTypeTrigger & 0x0F) == 1)
-                {
-                  /* class 1 connection */
-                  nDataSize -= 2; /* remove 16-bit sequence count length */
-                }
-              if (((S_CIP_Byte_Array *) pstAttribute->pt2data)->len
-                  != nDataSize)
-                {
-                  /*wrong connection size*/
-                  *pa_pnExtendedError
-                      = CIP_CON_MGR_ERROR_INVALID_CONNECTION_SIZE;
-                  return CIP_ERROR_CONNECTION_FAILURE;
-                }
-
-            }
-          else
-            {
-              *pa_pnExtendedError = CIP_CON_MGR_ERROR_INVALID_CONNECTION_POINT;
-              return CIP_ERROR_CONNECTION_FAILURE;
-            }
-        }
-
-      if (NULL != g_pnConfigDataBuffer)
-        { /* config data has been sent with this forward open request */
-          *pa_pnExtendedError
-              = handleConfigData(pstAssemblyClass, pstIOConnObj);
-          if (0 != *pa_pnExtendedError)
-            {
-              return CIP_ERROR_CONNECTION_FAILURE;
-            }
-        }
-
-      /* open a connection "point to point" or "multicast" based on the ConnectionParameter */
-      if (O2TConnectionType == 1) /* Multicast consuming */
-        {
-          if (OpenMulticastConnection(CONSUMING, pstIOConnObj, pstCPF_data)
-              == EIP_ERROR)
-            {
-              OPENER_TRACE_ERR("error in OpenMulticast Connection\n");
-              *pa_pnExtendedError = 0; /*TODO find out the correct extended error code*/
-              return CIP_ERROR_CONNECTION_FAILURE;
-            }
-        }
-      else if (O2TConnectionType == 2) /* Point to Point consuming */
-        {
-          if (OpenConsumingPointToPointConnection(pstIOConnObj, pstCPF_data)
-              == EIP_ERROR)
-            {
-              OPENER_TRACE_ERR("error in PointToPoint consuming connection\n");
-              *pa_pnExtendedError = 0; /*TODO find out the correct extended error code*/
-              return CIP_ERROR_CONNECTION_FAILURE;
-            }
-        }
-
-      if (T2OConnectionType == 1) /* Multicast producing */
-        {
-          if (openProducingMulticastConnection(pstIOConnObj, pstCPF_data)
-              == EIP_ERROR)
-            {
-              OPENER_TRACE_ERR("error in OpenMulticast Connection\n");
-              *pa_pnExtendedError = 0; /*TODO find out the correct extended error code*/
-              return CIP_ERROR_CONNECTION_FAILURE;
-            }
-        }
-      else if (T2OConnectionType == 2) /* Point to Point producing */
-        {
-
-          if (OpenProducingPointToPointConnection(pstIOConnObj, pstCPF_data,
-              pa_pnExtendedError) != EIP_OK)
-            {
-              OPENER_TRACE_ERR("error in PointToPoint producing connection\n");
-              return CIP_ERROR_CONNECTION_FAILURE;
-            }
-        }
-
-    }
-  addNewActiveConnection(pstIOConnObj);
-  IApp_IOConnectionEvent(pstIOConnObj->ConnectionPath.ConnectionPoint[0],
-      pstIOConnObj->ConnectionPath.ConnectionPoint[1], enOpened);
-  return EIP_OK;
-}
-
 void
 closeConnection(S_CIP_ConnectionObject *pa_pstConnObj)
 {
@@ -1637,86 +1071,7 @@ closeConnection(S_CIP_ConnectionObject *pa_pstConnObj)
       IApp_CloseSocket(pa_pstConnObj->sockfd[PRODUCING]);
       pa_pstConnObj->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
     }
-  removeFromeActiveConnections(pa_pstConnObj);
-}
-
-EIP_STATUS
-sendConnectedData(S_CIP_ConnectionObject *pa_pstConnection)
-{
-  S_CIP_CPF_Data *pCPFDataItem;
-  S_CIP_Byte_Array *p;
-  EIP_UINT16 replylength;
-  EIP_UINT8 *pnBuf;
-  int i;
-
-  /* TODO think of adding an own send buffer to each connection object in order to preset up the whole message on connection opening and just change the variable data items e.g., sequence number */
-
-  pCPFDataItem = &g_stCPFDataItem; /* TODO think on adding a CPF data item to the S_CIP_ConnectionObject in order to remove the code here or even better allocate memory in the connection object for storing the message to send and just change the application data*/
-
-  pa_pstConnection->EIPSequenceCountProducing++;
-
-  /* assembleCPFData */
-  pCPFDataItem->ItemCount = 2;
-  if ((pa_pstConnection->TransportClassTrigger & 0x0F) != 0)
-    { /* use Sequenced Address Items if not Connection Class 0 */
-      pCPFDataItem->stAddr_Item.TypeID = CIP_ITEM_ID_SEQUENCEDADDRESS;
-      pCPFDataItem->stAddr_Item.Length = 8;
-      pCPFDataItem->stAddr_Item.Data.SequenceNumber
-          = pa_pstConnection->EIPSequenceCountProducing;
-    }
-  else
-    {
-      pCPFDataItem->stAddr_Item.TypeID = CIP_ITEM_ID_CONNECTIONBASED;
-      pCPFDataItem->stAddr_Item.Length = 4;
-
-    }
-  pCPFDataItem->stAddr_Item.Data.ConnectionIdentifier
-      = pa_pstConnection->CIPProducedConnectionID;
-
-  pCPFDataItem->stDataI_Item.TypeID = CIP_ITEM_ID_CONNECTIONTRANSPORTPACKET;
-
-  p
-      = (S_CIP_Byte_Array *) pa_pstConnection->p_stProducingInstance->pstAttributes->pt2data;
-  pCPFDataItem->stDataI_Item.Length = 0;
-
-  /* notify the application that data will be sent immediately after the call */
-  if (IApp_BeforeAssemblyDataSend(pa_pstConnection->p_stProducingInstance))
-    {
-      /* the data has changed increase sequence counter */
-      pa_pstConnection->SequenceCountProducing++;
-    }
-
-  /* set AddressInfo Items to invalid Type */
-  pCPFDataItem->AddrInfo[0].TypeID = 0;
-  pCPFDataItem->AddrInfo[1].TypeID = 0;
-
-  replylength = assembleLinearMsg(0, pCPFDataItem,
-      &g_acMessageDataReplyBuffer[0]);
-
-  pnBuf = &g_acMessageDataReplyBuffer[replylength - 2];
-  pCPFDataItem->stDataI_Item.Length = p->len;
-  if ((pa_pstConnection->TransportTypeTrigger & 0x0F) == 1)
-    {
-      pCPFDataItem->stDataI_Item.Length += 2;
-      htols(pCPFDataItem->stDataI_Item.Length, &pnBuf);
-      htols(pa_pstConnection->SequenceCountProducing, &pnBuf);
-    }
-  else
-    {
-      htols(pCPFDataItem->stDataI_Item.Length, &pnBuf);
-    }
-
-  for (i = 0; i < p->len; i++)
-    {
-      *pnBuf = (EIP_UINT8) *(p->Data + i);
-      pnBuf++;
-    }
-
-  replylength += pCPFDataItem->stDataI_Item.Length;
-
-  return IApp_SendUDPData(&pa_pstConnection->remote_addr,
-      pa_pstConnection->sockfd[PRODUCING], &g_acMessageDataReplyBuffer[0],
-      replylength);
+  removeFromActiveConnections(pa_pstConnObj);
 }
 
 void
@@ -1736,10 +1091,11 @@ addNewActiveConnection(S_CIP_ConnectionObject *pa_pstConn)
       g_pstActiveConnectionList->m_pstFirst = pa_pstConn;
     }
   g_pstActiveConnectionList = pa_pstConn;
+  g_pstActiveConnectionList->State = CONN_STATE_ESTABLISHED;
 }
 
 void
-removeFromeActiveConnections(S_CIP_ConnectionObject *pa_pstConn)
+removeFromActiveConnections(S_CIP_ConnectionObject *pa_pstConn)
 {
   if (NULL != pa_pstConn->m_pstFirst)
     {
@@ -1755,154 +1111,65 @@ removeFromeActiveConnections(S_CIP_ConnectionObject *pa_pstConn)
     }
   pa_pstConn->m_pstFirst = NULL;
   pa_pstConn->m_pstNext = NULL;
+  pa_pstConn->State = CONN_STATE_NONEXISTENT;
 }
 
-void
-closeIOConnection(S_CIP_ConnectionObject *pa_pstConnObjData)
+EIP_BOOL8
+isConnectedOutputAssembly(EIP_UINT32 pa_nInstanceNr)
 {
-  S_CIP_ConnectionObject *pstNextNonCtrlMasterCon;
+  EIP_BOOL8 bRetVal = false;
 
-  IApp_IOConnectionEvent(pa_pstConnObjData->ConnectionPath.ConnectionPoint[0],
-      pa_pstConnObjData->ConnectionPath.ConnectionPoint[1], enClosed);
+  S_CIP_ConnectionObject *pstRunner = g_pstActiveConnectionList;
 
-  if ((enConnTypeIOExclusiveOwner == pa_pstConnObjData->m_eInstanceType)
-      || (enConnTypeIOInputOnly == pa_pstConnObjData->m_eInstanceType))
+  while (NULL != pstRunner)
     {
-      if ((CIP_MULTICAST_CONNECTION
-          == (pa_pstConnObjData->T_to_O_NetworkConnectionParameter
-              & CIP_MULTICAST_CONNECTION)) && (EIP_INVALID_SOCKET
-          != pa_pstConnObjData->sockfd[PRODUCING]))
+      if (pa_nInstanceNr == pstRunner->ConnectionPath.ConnectionPoint[0])
         {
-          pstNextNonCtrlMasterCon = getNextNonCtrlMasterCon(
-              pa_pstConnObjData->ConnectionPath.ConnectionPoint[1]);
-          if (NULL != pstNextNonCtrlMasterCon)
-            {
-              pstNextNonCtrlMasterCon->sockfd[PRODUCING]
-                  = pa_pstConnObjData->sockfd[PRODUCING];
-              pa_pstConnObjData->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
-              pstNextNonCtrlMasterCon->TransmissionTriggerTimer
-                  = pa_pstConnObjData->TransmissionTriggerTimer;
-            }
-          else
-            { /* this was the last master connection close all listen only connections listening on the port */
-              closeAllConnsForInputWithSameType(
-                  pa_pstConnObjData->ConnectionPath.ConnectionPoint[1],
-                  enConnTypeIOListenOnly);
-            }
+          bRetVal = true;
+          break;
         }
+      pstRunner = pstRunner->m_pstNext;
     }
-  closeConnection(pa_pstConnObjData);
+  return bRetVal;
 }
 
-void
-handleIOConnectionTimeOut(S_CIP_ConnectionObject *pa_pstConn)
+EIP_STATUS
+addConnectableObject(EIP_UINT32 pa_nClassId, TConnOpenFunc pa_pfOpenFunc)
 {
-  S_CIP_ConnectionObject *pstNextNonCtrlMasterCon;
-  IApp_IOConnectionEvent(pa_pstConn->ConnectionPath.ConnectionPoint[0],
-      pa_pstConn->ConnectionPath.ConnectionPoint[1], enTimedOut);
+  int i;
+  EIP_STATUS nRetVal;
+  nRetVal = EIP_ERROR;
 
-  if (CIP_MULTICAST_CONNECTION
-      == (pa_pstConn->T_to_O_NetworkConnectionParameter
-          & CIP_MULTICAST_CONNECTION))
+  /*parsing is now finished all data is available and check now establish the connection */
+  for (i = 0; i < scg_nNumConnectableObjects; ++i)
     {
-      switch (pa_pstConn->m_eInstanceType)
+      if ((0 == g_astConnMgmList[i].m_nClassID) || (pa_nClassId
+          == g_astConnMgmList[i].m_nClassID))
         {
-      case enConnTypeIOExclusiveOwner:
-        closeAllConnsForInputWithSameType(
-            pa_pstConn->ConnectionPath.ConnectionPoint[1],
-            enConnTypeIOInputOnly);
-        closeAllConnsForInputWithSameType(
-            pa_pstConn->ConnectionPath.ConnectionPoint[1],
-            enConnTypeIOListenOnly);
-        break;
-      case enConnTypeIOInputOnly:
-        if (EIP_INVALID_SOCKET != pa_pstConn->sockfd[PRODUCING])
-          { /* we are the controlling input only connection find a new controller*/
-            pstNextNonCtrlMasterCon = getNextNonCtrlMasterCon(
-                pa_pstConn->ConnectionPath.ConnectionPoint[1]);
-            if (NULL != pstNextNonCtrlMasterCon)
-              {
-                pstNextNonCtrlMasterCon->sockfd[PRODUCING]
-                    = pa_pstConn->sockfd[PRODUCING];
-                pa_pstConn->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
-                pstNextNonCtrlMasterCon->TransmissionTriggerTimer
-                    = pa_pstConn->TransmissionTriggerTimer;
-              }
-            else
-              { /* this was the last master connection close all listen only connections listening on the port */
-                closeAllConnsForInputWithSameType(
-                    pa_pstConn->ConnectionPath.ConnectionPoint[1],
-                    enConnTypeIOListenOnly);
-              }
-          }
-        break;
-      default:
-        break;
+          g_astConnMgmList[i].m_nClassID = pa_nClassId;
+          g_astConnMgmList[i].m_pfOpenFunc = pa_pfOpenFunc;
+          return EIP_OK;
         }
     }
 
-  closeConnection(pa_pstConn);
+  return EIP_ERROR;
 }
 
-EIP_UINT16
-handleConfigData(S_CIP_Class *pa_pstAssemblyClass,
-    S_CIP_ConnectionObject *pa_pstIOConnObj)
+TConnMgmHandling *
+getConnMgmEntry(EIP_UINT32 pa_nClassId)
 {
-  EIP_UINT16 unRetVal = 0;
-  S_CIP_Instance *pstConfigInstance = getCIPInstance(pa_pstAssemblyClass,
-      pa_pstIOConnObj->ConnectionPath.ConnectionPoint[2]);
-
-  if (0 != g_unConfigDataLen)
-    {
-      if (connectionWithSameConfigPointExists(
-          pa_pstIOConnObj->ConnectionPath.ConnectionPoint[2]))
-        { /* there is a connected connection with the same config point
-         * we have to have the same data as already present in the config point*/
-          S_CIP_Byte_Array *p = (S_CIP_Byte_Array *) getAttribute(
-              pstConfigInstance, 3)->pt2data;
-          if (p->len != g_unConfigDataLen)
-            {
-              unRetVal = CIP_CON_MGR_ERROR_OWNERSHIP_CONFLICT;
-            }
-          else
-            {
-              /*FIXME check if this is correct */
-              if (memcmp(p->Data, g_pnConfigDataBuffer, g_unConfigDataLen))
-                {
-                  unRetVal = CIP_CON_MGR_ERROR_OWNERSHIP_CONFLICT;
-                }
-            }
-        }
-      else
-        {
-          /*put the data on the configuration assembly object with the current
-           design this can be done rather efficiently */
-          if (EIP_OK != notifyAssemblyConnectedDataReceived(pstConfigInstance,
-              g_pnConfigDataBuffer, g_unConfigDataLen))
-            {
-              OPENER_TRACE_WARN("Configuration data was invalid\n");
-              unRetVal = CIP_CON_MGR_ERROR_INVALID_CONFIGURATION_FORMAT;
-            }
-        }
-    }
-  return unRetVal;
-}
-
-bool
-isConnectedOutputAssembly(EIP_UINT32 pa_nInstanceNr)
-{
-  bool bRetVal = false;
+  int i;
+  TConnMgmHandling *pstRetVal;
 
-  S_CIP_ConnectionObject *pstRunner = g_pstActiveConnectionList;
+  pstRetVal = NULL;
 
-  while (NULL != pstRunner)
+  for (i = 0; i < scg_nNumConnectableObjects; ++i)
     {
-      if (pa_nInstanceNr == pstRunner->ConnectionPath.ConnectionPoint[0])
+      if (pa_nClassId == g_astConnMgmList[i].m_nClassID)
         {
-          bRetVal = true;
+          pstRetVal = &(g_astConnMgmList[i]);
           break;
         }
-      pstRunner = pstRunner->m_pstNext;
     }
-  return bRetVal;
+  return pstRetVal;
 }

+ 38 - 1
src/cip/cipconnectionmanager.h

@@ -7,6 +7,7 @@
 #define CIPCONNECTIONMANAGER_H_
 
 #include "opener_user_conf.h"
+#include "opener_api.h"
 #include "typedefs.h"
 #include "ciptypes.h"
 
@@ -38,6 +39,15 @@
 #define CIP_CON_MGR_ERROR_INVALID_SEGMENT_TYPE_IN_PATH 0x0315
 #define CIP_CON_MGR_TARGET_OBJECT_OUT_OF_CONNECTIONS 0x011A
 
+/*macros for comparing sequence numbers according to CIP spec vol 2 3-4.2*/
+#define SEQ_LEQ32(a, b) ((int)((a) - (b)) <= 0)
+#define SEQ_GEQ32(a, b) ((int)((a) - (b)) >= 0)
+
+/* similar macros for comparing 16 bit sequence numbers */
+#define SEQ_LEQ16(a, b) ((short)((a) - (b)) <= 0)
+#define SEQ_GEQ16(a, b) ((short)((a) - (b)) >= 0)
+
+
 /*! States of a connection */
 typedef enum
 {
@@ -155,6 +165,12 @@ typedef struct CIP_ConnectionObject
   struct sockaddr_in m_stOriginatorAddr;  /* the address of the originator that established the connection. needed for scanning if the right packet is arriving */
   int sockfd[2]; /* socket handles, indexed by CONSUMING or PRODUCING */
 
+  /* pointers to connection handling functions */
+  TConnCloseFunc m_pfCloseFunc;
+  TConnTimeOutFunc m_pfTimeOutFunc;
+  TConnSendDataFunc m_pfSendDataFunc;
+  TConnRecvDataFunc m_pfReceiveDataFunc;
+
   /* pointers to be used in the active connection list */
   struct CIP_ConnectionObject *m_pstNext;
   struct CIP_ConnectionObject *m_pstFirst;
@@ -193,8 +209,29 @@ copyConnectionData(S_CIP_ConnectionObject *pa_pstDst,
 void
 closeConnection(S_CIP_ConnectionObject *pa_pstConnObj);
 
-bool isConnectedOutputAssembly(EIP_UINT32 pa_nInstanceNr);
+EIP_BOOL8 isConnectedOutputAssembly(EIP_UINT32 pa_nInstanceNr);
+
+/** \brief Generate the ConnectionIDs and set the general configuration parameter
+ * in the given connection object.
+ *
+ * @param pa_pstConnObj pointer to the connection object that should be set up.
+ */
+void
+generalConnectionConfiguration(S_CIP_ConnectionObject *pa_pstConnObj);
 
 
+/** \brief Insert the given connection object to the list of currently active and managed connections.
+ *
+ * By adding a connection to the active connection list the connection manager will
+ * perform the supervision and handle the timing (e.g., timeout, production inhibit, etc).
+ *
+ * @param pa_pstConnObj pointer to the connection object to be added.
+ */
+void
+addNewActiveConnection(S_CIP_ConnectionObject *pa_pstConn);
+
+void
+removeFromActiveConnections(S_CIP_ConnectionObject *pa_pstConn);
+
 #endif /*CIPCONNECTIONMANAGER_H_*/
 

+ 769 - 0
src/cip/cipioconnection.c

@@ -0,0 +1,769 @@
+/*******************************************************************************
+ * Copyright (c) 2011, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#include "cipioconnection.h"
+#include "cipconnectionmanager.h"
+#include "cipassembly.h"
+#include "cipcommon.h"
+#include "appcontype.h"
+#include "cpf.h"
+#include "trace.h"
+#include "endianconv.h"
+#include <string.h>
+
+/*The port to be used per default for I/O messages on UDP.*/
+#define OPENER_EIP_IO_UDP_PORT   0x08AE
+
+/* values needed from tcp/ip interface */
+extern EIP_UINT32 g_nMultiCastAddress;
+
+/* producing multicast connection have to consider the rules that apply for
+ * application connection types.
+ */
+EIP_STATUS
+openProducingMulticastConnection(S_CIP_ConnectionObject *pa_pstConnObj,
+    S_CIP_CPF_Data *pa_CPF_data);
+
+EIP_STATUS
+OpenMulticastConnection(int pa_direction,
+    S_CIP_ConnectionObject *pa_pstConnObj, S_CIP_CPF_Data *pa_CPF_data);
+
+EIP_STATUS
+OpenConsumingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
+    S_CIP_CPF_Data *pa_CPF_data);
+
+int
+OpenProducingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
+    S_CIP_CPF_Data *pa_CPF_data);
+
+EIP_UINT16
+handleConfigData(S_CIP_Class *pa_pstAssemblyClass,
+    S_CIP_ConnectionObject *pa_pstIOConnObj);
+
+/* Regularly close the IO connection. If it is an exclusive owner or input only
+ * connection and in charge of the connection a new owner will be searched
+ */
+void
+closeIOConnection(S_CIP_ConnectionObject *pa_pstConnObjData);
+
+void
+handleIOConnectionTimeOut(S_CIP_ConnectionObject *pa_pstConn);
+
+/*!  Send the data from the produced CIP Object of the connection via the socket of the connection object
+ *   on UDP.
+ *      @param pa_pstConnection  pointer to the connection object
+ *      @return status  EIP_OK .. success
+ *                     EIP_ERROR .. error
+ */
+EIP_STATUS
+sendConnectedData(S_CIP_ConnectionObject *pa_pstConnection);
+
+EIP_STATUS
+handleReceivedIOConnData(struct CIP_ConnectionObject *pa_pstConnection,
+    EIP_UINT8 * pa_pnData, EIP_UINT16 pa_nDataLength);
+
+/**** Global variables ****/
+
+/* buffers for the config data coming with a forward open request.
+ */EIP_UINT8 *g_pnConfigDataBuffer = NULL;
+unsigned int g_unConfigDataLen = 0;
+
+/*! buffer for holding the run idle information.                             */
+
+EIP_UINT32 g_nRunIdleState;
+
+/**** Implementation ****/
+
+int
+establishIOConnction(struct CIP_ConnectionObject *pa_pstConnObjData,
+    EIP_UINT16 *pa_pnExtendedError)
+{
+  int O2TConnectionType, T2OConnectionType;
+  int nRetVal = EIP_OK;
+  S_CIP_attribute_struct *pstAttribute;
+  /* currently we allow I/O connections only to assembly objects */
+  S_CIP_Class *pstAssemblyClass = getCIPClass(CIP_ASSEMBLY_CLASS_CODE); /* we don't need to check for zero as this is handled in the connection path parsing */
+  S_CIP_Instance *pstInstance = NULL;
+
+  S_CIP_ConnectionObject *pstIOConnObj = getIOConnectionForConnectionData(
+      pa_pstConnObjData, pa_pnExtendedError);
+
+  if (NULL == pstIOConnObj)
+    {
+      return CIP_ERROR_CONNECTION_FAILURE;
+    }
+
+  //TODO add check for transport type trigger
+
+  /* set the connection call backs */
+  pstIOConnObj->m_pfCloseFunc = closeIOConnection;
+  pstIOConnObj->m_pfTimeOutFunc = handleIOConnectionTimeOut;
+  pstIOConnObj->m_pfSendDataFunc = sendConnectedData;
+  pstIOConnObj->m_pfReceiveDataFunc = handleReceivedIOConnData;
+
+  generalConnectionConfiguration(pstIOConnObj);
+
+  O2TConnectionType
+      = (pstIOConnObj->O_to_T_NetworkConnectionParameter & 0x6000) >> 13;
+  T2OConnectionType
+      = (pstIOConnObj->T_to_O_NetworkConnectionParameter & 0x6000) >> 13;
+
+  if ((O2TConnectionType == 0) && (T2OConnectionType == 0))
+    { /* this indicates an re-configuration of the connection currently not supported and we should not come here as this is handled in the forwardopen function*/
+
+    }
+  else
+    {
+      int nProducingIndex = 0;
+      int nDataSize;
+      if ((O2TConnectionType != 0) && (T2OConnectionType != 0))
+        { /* we have a producing and consuming connection*/
+          nProducingIndex = 1;
+        }
+
+      pstIOConnObj->p_stConsumingInstance = 0;
+      pstIOConnObj->ConsumedConnectionPathLength = 0;
+      pstIOConnObj->p_stProducingInstance = 0;
+      pstIOConnObj->ProducedConnectionPathLength = 0;
+
+      if (O2TConnectionType != 0)
+        { /*setup consumer side*/
+          if (0 != (pstInstance = getCIPInstance(pstAssemblyClass,
+              pstIOConnObj->ConnectionPath.ConnectionPoint[0])))
+            { /* consuming Connection Point is present */
+              pstIOConnObj->p_stConsumingInstance = pstInstance;
+
+              pstIOConnObj->ConsumedConnectionPathLength = 6;
+              pstIOConnObj->ConsumedConnectionPath.PathSize = 6;
+              pstIOConnObj->ConsumedConnectionPath.ClassID
+                  = pstIOConnObj->ConnectionPath.ClassID;
+              pstIOConnObj->ConsumedConnectionPath.InstanceNr
+                  = pstIOConnObj->ConnectionPath.ConnectionPoint[0];
+              pstIOConnObj->ConsumedConnectionPath.AttributNr = 3;
+
+              pstAttribute = getAttribute(pstInstance, 3);
+              OPENER_ASSERT(pstAttribute != NULL); /* an assembly object should always have an attribute 3 */
+              nDataSize = pstIOConnObj->ConsumedConnectionSize;
+
+              if ((pstIOConnObj->TransportTypeTrigger & 0x0F) == 1)
+                {
+                  /* class 1 connection */
+                  nDataSize -= 2; /* remove 16-bit sequence count length */
+                }
+              if ((OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER) && (nDataSize > 0))
+                { /* we only have an run idle header if it is not an hearbeat connection */
+                  nDataSize -= 4; /* remove the 4 bytes needed for run/idle header */
+                }
+
+              if (((S_CIP_Byte_Array *) pstAttribute->pt2data)->len
+                  != nDataSize)
+                {
+                  /*wrong connection size */
+                  *pa_pnExtendedError
+                      = CIP_CON_MGR_ERROR_INVALID_CONNECTION_SIZE;
+                  return CIP_ERROR_CONNECTION_FAILURE;
+                }
+            }
+          else
+            {
+              *pa_pnExtendedError = CIP_CON_MGR_ERROR_INVALID_CONNECTION_POINT;
+              return CIP_ERROR_CONNECTION_FAILURE;
+            }
+        }
+
+      if (T2OConnectionType != 0)
+        { /*setup producer side*/
+          if (0 != (pstInstance = getCIPInstance(pstAssemblyClass,
+              pstIOConnObj->ConnectionPath.ConnectionPoint[nProducingIndex])))
+            {
+              pstIOConnObj->p_stProducingInstance = pstInstance;
+
+              pstIOConnObj->ProducedConnectionPathLength = 6;
+              pstIOConnObj->ProducedConnectionPath.PathSize = 6;
+              pstIOConnObj->ProducedConnectionPath.ClassID
+                  = pstIOConnObj->ConnectionPath.ClassID;
+              pstIOConnObj->ProducedConnectionPath.InstanceNr
+                  = pstIOConnObj->ConnectionPath.ConnectionPoint[nProducingIndex];
+              pstIOConnObj->ProducedConnectionPath.AttributNr = 3;
+
+              pstAttribute = getAttribute(pstInstance, 3);
+              OPENER_ASSERT(pstAttribute != NULL); /* an assembly object should always have an attribute 3 */
+              nDataSize = pstIOConnObj->ProducedConnectionSize;
+              if ((pstIOConnObj->TransportTypeTrigger & 0x0F) == 1)
+                {
+                  /* class 1 connection */
+                  nDataSize -= 2; /* remove 16-bit sequence count length */
+                }
+              if (((S_CIP_Byte_Array *) pstAttribute->pt2data)->len
+                  != nDataSize)
+                {
+                  /*wrong connection size*/
+                  *pa_pnExtendedError
+                      = CIP_CON_MGR_ERROR_INVALID_CONNECTION_SIZE;
+                  return CIP_ERROR_CONNECTION_FAILURE;
+                }
+
+            }
+          else
+            {
+              *pa_pnExtendedError = CIP_CON_MGR_ERROR_INVALID_CONNECTION_POINT;
+              return CIP_ERROR_CONNECTION_FAILURE;
+            }
+        }
+
+      if (NULL != g_pnConfigDataBuffer)
+        { /* config data has been sent with this forward open request */
+          *pa_pnExtendedError
+              = handleConfigData(pstAssemblyClass, pstIOConnObj);
+          if (0 != *pa_pnExtendedError)
+            {
+              return CIP_ERROR_CONNECTION_FAILURE;
+            }
+        }
+
+      nRetVal = openCommunicationChannels(pstIOConnObj);
+      if (EIP_OK != nRetVal)
+        {
+          *pa_pnExtendedError = 0; /*TODO find out the correct extended error code*/
+          return nRetVal;
+        }
+    }
+
+  addNewActiveConnection(pstIOConnObj);
+  IApp_IOConnectionEvent(pstIOConnObj->ConnectionPath.ConnectionPoint[0],
+      pstIOConnObj->ConnectionPath.ConnectionPoint[1], enOpened);
+  return nRetVal;
+}
+
+/*   EIP_STATUS OpenPointToPointConnection(S_CIP_CPF_Data *pa_CPF_data, S_CIP_CM_Object *pa_pstCMObject, INT8 pa_direction, int pa_index)
+ *   open a Point2Point connection dependent on pa_direction.
+ *      pa_pstCMObject  pointer to registered Object in ConnectionManager.
+ *      pa_index        index of the connection object
+ *  return status
+ *               0 .. success
+ *              -1 .. error
+ */
+
+EIP_STATUS
+OpenConsumingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
+    S_CIP_CPF_Data *pa_CPF_data)
+{
+  /*static EIP_UINT16 nUDPPort = 2222; TODO think on improving the udp port assigment for point to point connections */
+  int j;
+  struct sockaddr_in addr;
+  int newfd;
+
+  j = 0;
+  if (pa_CPF_data->AddrInfo[0].TypeID == 0)
+    { /* it is not used yet */
+      j = 0;
+    }
+  else if (pa_CPF_data->AddrInfo[1].TypeID == 0)
+    {
+      j = 1;
+    }
+
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = INADDR_ANY;
+  /*addr.in_port = htons(nUDPPort++);*/
+  addr.sin_port = htons(OPENER_EIP_IO_UDP_PORT);
+
+  newfd = IApp_CreateUDPSocket(CONSUMING, &addr); /* the address is only needed for bind used if consuming */
+  if (newfd == EIP_INVALID_SOCKET)
+    {
+      OPENER_TRACE_ERR(
+          "cannot create UDP socket in OpenPointToPointConnection\n");
+      return EIP_ERROR;
+    }
+
+  pa_pstConnObj->m_stOriginatorAddr = addr; /* store the address of the originator for packet scanning */
+  addr.sin_addr.s_addr = INADDR_ANY; /* restore the address */
+  pa_pstConnObj->sockfd[CONSUMING] = newfd;
+
+  pa_CPF_data->AddrInfo[j].Length = 16;
+  pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_O_TO_T;
+
+  pa_CPF_data->AddrInfo[j].nsin_port = addr.sin_port;
+  /*TODO should we add our own address here? */
+  pa_CPF_data->AddrInfo[j].nsin_addr = addr.sin_addr.s_addr;
+  memset(pa_CPF_data->AddrInfo[j].nasin_zero, 0, 8);
+  pa_CPF_data->AddrInfo[j].nsin_family = htons(AF_INET);
+
+  return EIP_OK;
+}
+
+int
+OpenProducingPointToPointConnection(S_CIP_ConnectionObject *pa_pstConnObj,
+    S_CIP_CPF_Data *pa_CPF_data)
+{
+  int newfd;
+  in_port_t nPort = OPENER_EIP_IO_UDP_PORT; /* the default port to be used if no port information is part of the forward open request */
+
+  if (CIP_ITEM_ID_SOCKADDRINFO_T_TO_O == pa_CPF_data->AddrInfo[0].TypeID)
+    {
+      nPort = pa_CPF_data->AddrInfo[0].nsin_port;
+    }
+  else
+    {
+      if (CIP_ITEM_ID_SOCKADDRINFO_T_TO_O == pa_CPF_data->AddrInfo[1].TypeID)
+        {
+          nPort = pa_CPF_data->AddrInfo[1].nsin_port;
+        }
+    }
+
+  pa_pstConnObj->remote_addr.sin_family = AF_INET;
+  pa_pstConnObj->remote_addr.sin_addr.s_addr = 0; /* we don't know the address of the originate will be set in the IApp_CreateUDPSocket */
+  pa_pstConnObj->remote_addr.sin_port = nPort;
+
+  newfd = IApp_CreateUDPSocket(PRODUCING, &pa_pstConnObj->remote_addr); /* the address is only needed for bind used if consuming */
+  if (newfd == EIP_INVALID_SOCKET)
+    {
+      OPENER_TRACE_ERR(
+          "cannot create UDP socket in OpenPointToPointConnection\n");
+      //*pa_pnExtendedError = 0x0315; /* miscellaneous*/
+      return CIP_ERROR_CONNECTION_FAILURE;
+    }
+  pa_pstConnObj->sockfd[PRODUCING] = newfd;
+
+  return EIP_OK;
+}
+
+EIP_STATUS
+openProducingMulticastConnection(S_CIP_ConnectionObject *pa_pstConnObj,
+    S_CIP_CPF_Data *pa_CPF_data)
+{
+  S_CIP_ConnectionObject *pstExistingConn = getExistingProdMulticastConnection(
+      pa_pstConnObj->ConnectionPath.ConnectionPoint[1]);
+  int j;
+
+  if (NULL == pstExistingConn)
+    { /* we are the first connection producing for the given Input Assembly */
+      return OpenMulticastConnection(PRODUCING, pa_pstConnObj, pa_CPF_data);
+    }
+  else
+    {
+      /* we need to infrom our originator on the correct connection id */
+      pa_pstConnObj->CIPProducedConnectionID
+          = pstExistingConn->CIPProducedConnectionID;
+    }
+
+  /* we have a connection reuse the data and the socket */
+
+  j = 0; /* allocate an unused sockaddr struct to use */
+  if (g_stCPFDataItem.AddrInfo[0].TypeID == 0)
+    { /* it is not used yet */
+      j = 0;
+    }
+  else if (g_stCPFDataItem.AddrInfo[1].TypeID == 0)
+    {
+      j = 1;
+    }
+
+  if (enConnTypeIOExclusiveOwner == pa_pstConnObj->m_eInstanceType)
+    {
+      /* eclusive owners take the socket and further manage the connection
+       * especially in the case of time outs.
+       */
+      pa_pstConnObj->sockfd[PRODUCING] = pstExistingConn->sockfd[PRODUCING];
+      pstExistingConn->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
+    }
+  else
+    { /* this connection will not produce the data */
+      pa_pstConnObj->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
+    }
+
+  pa_CPF_data->AddrInfo[j].Length = 16;
+  pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_T_TO_O;
+  pa_pstConnObj->remote_addr.sin_family = AF_INET;
+  pa_pstConnObj->remote_addr.sin_port = pa_CPF_data->AddrInfo[j].nsin_port
+      = htons(OPENER_EIP_IO_UDP_PORT);
+  pa_pstConnObj->remote_addr.sin_addr.s_addr
+      = pa_CPF_data->AddrInfo[j].nsin_addr = g_nMultiCastAddress;
+  memset(pa_CPF_data->AddrInfo[j].nasin_zero, 0, 8);
+  pa_CPF_data->AddrInfo[j].nsin_family = htons(AF_INET);
+
+  return EIP_OK;
+}
+
+/*   INT8 OpenMulticastConnection(S_CIP_CPF_Data *pa_CPF_data, S_CIP_CM_Object *pa_pstCMObject, INT8 pa_direction, int pa_index)
+ *   open a Multicast connection dependent on pa_direction.
+ *      pa_CPF_data     received CPF Data Item.
+ *      pa_pstCMObject  pointer to registered Object in ConnectionManager.
+ *      pa_direction    flag to indicate if consuming or producing.
+ *      pa_index        index of the connection object
+ *  return status
+ *               0 .. success
+ *              -1 .. error
+ */
+EIP_STATUS
+OpenMulticastConnection(int pa_direction,
+    S_CIP_ConnectionObject *pa_pstConnObj, S_CIP_CPF_Data *pa_CPF_data)
+{
+  int j;
+  struct sockaddr_in addr;
+  int newfd;
+
+  j = 0; /* allocate an unused sockaddr struct to use */
+  if (g_stCPFDataItem.AddrInfo[0].TypeID == 0)
+    { /* it is not used yet */
+      j = 0;
+    }
+  else if (g_stCPFDataItem.AddrInfo[1].TypeID == 0)
+    {
+      j = 1;
+    }
+
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = g_nMultiCastAddress;
+  addr.sin_port = htons(OPENER_EIP_IO_UDP_PORT);
+
+  newfd = IApp_CreateUDPSocket(pa_direction, &addr); /* the address is only needed for bind used if consuming */
+  if (newfd == EIP_INVALID_SOCKET)
+    {
+      OPENER_TRACE_ERR("cannot create UDP socket in OpenMulticastConnection\n");
+      return EIP_ERROR;
+    }
+  pa_pstConnObj->sockfd[pa_direction] = newfd;
+
+  pa_CPF_data->AddrInfo[j].Length = 16;
+  if (pa_direction == CONSUMING)
+    {
+      pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_O_TO_T;
+      pa_pstConnObj->m_stOriginatorAddr = addr;
+      addr.sin_addr.s_addr = g_nMultiCastAddress; /* restore the multicast address */
+    }
+  else
+    {
+      pa_CPF_data->AddrInfo[j].TypeID = CIP_ITEM_ID_SOCKADDRINFO_T_TO_O;
+      pa_pstConnObj->remote_addr = addr;
+    }
+  pa_CPF_data->AddrInfo[j].nsin_port = addr.sin_port;
+  pa_CPF_data->AddrInfo[j].nsin_addr = addr.sin_addr.s_addr;
+  memset(pa_CPF_data->AddrInfo[j].nasin_zero, 0, 8);
+  pa_CPF_data->AddrInfo[j].nsin_family = htons(AF_INET);
+
+  return EIP_OK;
+}
+
+EIP_UINT16
+handleConfigData(S_CIP_Class *pa_pstAssemblyClass,
+    S_CIP_ConnectionObject *pa_pstIOConnObj)
+{
+  EIP_UINT16 unRetVal = 0;
+  S_CIP_Instance *pstConfigInstance = getCIPInstance(pa_pstAssemblyClass,
+      pa_pstIOConnObj->ConnectionPath.ConnectionPoint[2]);
+
+  if (0 != g_unConfigDataLen)
+    {
+      if (connectionWithSameConfigPointExists(
+          pa_pstIOConnObj->ConnectionPath.ConnectionPoint[2]))
+        { /* there is a connected connection with the same config point
+         * we have to have the same data as already present in the config point*/
+          S_CIP_Byte_Array *p = (S_CIP_Byte_Array *) getAttribute(
+              pstConfigInstance, 3)->pt2data;
+          if (p->len != g_unConfigDataLen)
+            {
+              unRetVal = CIP_CON_MGR_ERROR_OWNERSHIP_CONFLICT;
+            }
+          else
+            {
+              /*FIXME check if this is correct */
+              if (memcmp(p->Data, g_pnConfigDataBuffer, g_unConfigDataLen))
+                {
+                  unRetVal = CIP_CON_MGR_ERROR_OWNERSHIP_CONFLICT;
+                }
+            }
+        }
+      else
+        {
+          /*put the data on the configuration assembly object with the current
+           design this can be done rather efficiently */
+          if (EIP_OK != notifyAssemblyConnectedDataReceived(pstConfigInstance,
+              g_pnConfigDataBuffer, g_unConfigDataLen))
+            {
+              OPENER_TRACE_WARN("Configuration data was invalid\n");
+              unRetVal = CIP_CON_MGR_ERROR_INVALID_CONFIGURATION_FORMAT;
+            }
+        }
+    }
+  return unRetVal;
+}
+
+void
+closeIOConnection(S_CIP_ConnectionObject *pa_pstConnObjData)
+{
+  S_CIP_ConnectionObject *pstNextNonCtrlMasterCon;
+
+  IApp_IOConnectionEvent(pa_pstConnObjData->ConnectionPath.ConnectionPoint[0],
+      pa_pstConnObjData->ConnectionPath.ConnectionPoint[1], enClosed);
+
+  if ((enConnTypeIOExclusiveOwner == pa_pstConnObjData->m_eInstanceType)
+      || (enConnTypeIOInputOnly == pa_pstConnObjData->m_eInstanceType))
+    {
+      if ((CIP_MULTICAST_CONNECTION
+          == (pa_pstConnObjData->T_to_O_NetworkConnectionParameter
+              & CIP_MULTICAST_CONNECTION)) && (EIP_INVALID_SOCKET
+          != pa_pstConnObjData->sockfd[PRODUCING]))
+        {
+          pstNextNonCtrlMasterCon = getNextNonCtrlMasterCon(
+              pa_pstConnObjData->ConnectionPath.ConnectionPoint[1]);
+          if (NULL != pstNextNonCtrlMasterCon)
+            {
+              pstNextNonCtrlMasterCon->sockfd[PRODUCING]
+                  = pa_pstConnObjData->sockfd[PRODUCING];
+              pa_pstConnObjData->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
+              pstNextNonCtrlMasterCon->TransmissionTriggerTimer
+                  = pa_pstConnObjData->TransmissionTriggerTimer;
+            }
+          else
+            { /* this was the last master connection close all listen only connections listening on the port */
+              closeAllConnsForInputWithSameType(
+                  pa_pstConnObjData->ConnectionPath.ConnectionPoint[1],
+                  enConnTypeIOListenOnly);
+            }
+        }
+    }
+
+  closeCommChannelsAndRemoveFromActiveConnsList(pa_pstConnObjData);
+}
+
+void
+handleIOConnectionTimeOut(S_CIP_ConnectionObject *pa_pstConn)
+{
+  S_CIP_ConnectionObject *pstNextNonCtrlMasterCon;
+  IApp_IOConnectionEvent(pa_pstConn->ConnectionPath.ConnectionPoint[0],
+      pa_pstConn->ConnectionPath.ConnectionPoint[1], enTimedOut);
+
+  if (CIP_MULTICAST_CONNECTION
+      == (pa_pstConn->T_to_O_NetworkConnectionParameter
+          & CIP_MULTICAST_CONNECTION))
+    {
+      switch (pa_pstConn->m_eInstanceType)
+        {
+      case enConnTypeIOExclusiveOwner:
+        closeAllConnsForInputWithSameType(
+            pa_pstConn->ConnectionPath.ConnectionPoint[1],
+            enConnTypeIOInputOnly);
+        closeAllConnsForInputWithSameType(
+            pa_pstConn->ConnectionPath.ConnectionPoint[1],
+            enConnTypeIOListenOnly);
+        break;
+      case enConnTypeIOInputOnly:
+        if (EIP_INVALID_SOCKET != pa_pstConn->sockfd[PRODUCING])
+          { /* we are the controlling input only connection find a new controller*/
+            pstNextNonCtrlMasterCon = getNextNonCtrlMasterCon(
+                pa_pstConn->ConnectionPath.ConnectionPoint[1]);
+            if (NULL != pstNextNonCtrlMasterCon)
+              {
+                pstNextNonCtrlMasterCon->sockfd[PRODUCING]
+                    = pa_pstConn->sockfd[PRODUCING];
+                pa_pstConn->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
+                pstNextNonCtrlMasterCon->TransmissionTriggerTimer
+                    = pa_pstConn->TransmissionTriggerTimer;
+              }
+            else
+              { /* this was the last master connection close all listen only connections listening on the port */
+                closeAllConnsForInputWithSameType(
+                    pa_pstConn->ConnectionPath.ConnectionPoint[1],
+                    enConnTypeIOListenOnly);
+              }
+          }
+        break;
+      default:
+        break;
+        }
+    }
+
+  OPENER_ASSERT(NULL != pa_pstConn->m_pfCloseFunc);
+  pa_pstConn->m_pfCloseFunc(pa_pstConn);
+}
+
+EIP_STATUS
+sendConnectedData(S_CIP_ConnectionObject *pa_pstConnection)
+{
+  S_CIP_CPF_Data *pCPFDataItem;
+  S_CIP_Byte_Array *p;
+  EIP_UINT16 replylength;
+  EIP_UINT8 *pnBuf;
+  int i;
+
+  /* TODO think of adding an own send buffer to each connection object in order to preset up the whole message on connection opening and just change the variable data items e.g., sequence number */
+
+  pCPFDataItem = &g_stCPFDataItem; /* TODO think on adding a CPF data item to the S_CIP_ConnectionObject in order to remove the code here or even better allocate memory in the connection object for storing the message to send and just change the application data*/
+
+  pa_pstConnection->EIPSequenceCountProducing++;
+
+  /* assembleCPFData */
+  pCPFDataItem->ItemCount = 2;
+  if ((pa_pstConnection->TransportClassTrigger & 0x0F) != 0)
+    { /* use Sequenced Address Items if not Connection Class 0 */
+      pCPFDataItem->stAddr_Item.TypeID = CIP_ITEM_ID_SEQUENCEDADDRESS;
+      pCPFDataItem->stAddr_Item.Length = 8;
+      pCPFDataItem->stAddr_Item.Data.SequenceNumber
+          = pa_pstConnection->EIPSequenceCountProducing;
+    }
+  else
+    {
+      pCPFDataItem->stAddr_Item.TypeID = CIP_ITEM_ID_CONNECTIONBASED;
+      pCPFDataItem->stAddr_Item.Length = 4;
+
+    }
+  pCPFDataItem->stAddr_Item.Data.ConnectionIdentifier
+      = pa_pstConnection->CIPProducedConnectionID;
+
+  pCPFDataItem->stDataI_Item.TypeID = CIP_ITEM_ID_CONNECTIONTRANSPORTPACKET;
+
+  p
+      = (S_CIP_Byte_Array *) pa_pstConnection->p_stProducingInstance->pstAttributes->pt2data;
+  pCPFDataItem->stDataI_Item.Length = 0;
+
+  /* notify the application that data will be sent immediately after the call */
+  if (IApp_BeforeAssemblyDataSend(pa_pstConnection->p_stProducingInstance))
+    {
+      /* the data has changed increase sequence counter */
+      pa_pstConnection->SequenceCountProducing++;
+    }
+
+  /* set AddressInfo Items to invalid Type */
+  pCPFDataItem->AddrInfo[0].TypeID = 0;
+  pCPFDataItem->AddrInfo[1].TypeID = 0;
+
+  replylength = assembleLinearMsg(0, pCPFDataItem,
+      &g_acMessageDataReplyBuffer[0]);
+
+  pnBuf = &g_acMessageDataReplyBuffer[replylength - 2];
+  pCPFDataItem->stDataI_Item.Length = p->len;
+  if ((pa_pstConnection->TransportTypeTrigger & 0x0F) == 1)
+    {
+      pCPFDataItem->stDataI_Item.Length += 2;
+      htols(pCPFDataItem->stDataI_Item.Length, &pnBuf);
+      htols(pa_pstConnection->SequenceCountProducing, &pnBuf);
+    }
+  else
+    {
+      htols(pCPFDataItem->stDataI_Item.Length, &pnBuf);
+    }
+
+  for (i = 0; i < p->len; i++)
+    {
+      *pnBuf = (EIP_UINT8) *(p->Data + i);
+      pnBuf++;
+    }
+
+  replylength += pCPFDataItem->stDataI_Item.Length;
+
+  return IApp_SendUDPData(&pa_pstConnection->remote_addr,
+      pa_pstConnection->sockfd[PRODUCING], &g_acMessageDataReplyBuffer[0],
+      replylength);
+}
+
+EIP_STATUS
+handleReceivedIOConnData(struct CIP_ConnectionObject *pa_pstConnection,
+    EIP_UINT8 * pa_pnData, EIP_UINT16 pa_nDataLength)
+{
+
+  /* check class 1 sequence number*/
+  if ((pa_pstConnection->TransportTypeTrigger & 0x0F) == 1)
+    {
+      EIP_UINT16 nSequenceBuf = ltohs(&(pa_pnData));
+      if (SEQ_LEQ16(nSequenceBuf, pa_pstConnection->SequenceCountConsuming))
+        {
+          return EIP_OK; /* no new data for the assembly */
+        }
+      pa_pstConnection->SequenceCountConsuming = nSequenceBuf;
+      pa_nDataLength -= 2;
+    }
+
+  if (pa_nDataLength > 0)
+    {
+      /* we have no heartbeat connection */
+      if (OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER)
+        {
+          EIP_UINT32 nRunIdleBuf = ltohl(&(pa_pnData));
+          if (g_nRunIdleState != nRunIdleBuf)
+            {
+              IApp_RunIdleChanged(nRunIdleBuf);
+            }
+          g_nRunIdleState = nRunIdleBuf;
+          pa_nDataLength -= 4;
+        }
+
+      if (notifyAssemblyConnectedDataReceived(
+          pa_pstConnection->p_stConsumingInstance, pa_pnData, pa_nDataLength)
+          != 0)
+        {
+          return EIP_ERROR;
+        }
+    }
+  return EIP_OK;
+}
+
+int
+openCommunicationChannels(struct CIP_ConnectionObject *pa_pstIOConnObj)
+{
+  int O2TConnectionType, T2OConnectionType;
+  int nRetVal = EIP_OK;
+  /*get pointer to the cpf data, currently we have just one global instance of the struct. This may change in the future*/
+  S_CIP_CPF_Data *pstCPF_data = &g_stCPFDataItem;
+
+  O2TConnectionType = (pa_pstIOConnObj->O_to_T_NetworkConnectionParameter
+      & 0x6000) >> 13;
+  T2OConnectionType = (pa_pstIOConnObj->T_to_O_NetworkConnectionParameter
+      & 0x6000) >> 13;
+
+  /* open a connection "point to point" or "multicast" based on the ConnectionParameter */
+  if (O2TConnectionType == 1) /* Multicast consuming */
+    {
+      if (OpenMulticastConnection(CONSUMING, pa_pstIOConnObj, pstCPF_data)
+          == EIP_ERROR)
+        {
+          OPENER_TRACE_ERR("error in OpenMulticast Connection\n");
+          return CIP_ERROR_CONNECTION_FAILURE;
+        }
+    }
+  else if (O2TConnectionType == 2) /* Point to Point consuming */
+    {
+      if (OpenConsumingPointToPointConnection(pa_pstIOConnObj, pstCPF_data)
+          == EIP_ERROR)
+        {
+          OPENER_TRACE_ERR("error in PointToPoint consuming connection\n");
+          return CIP_ERROR_CONNECTION_FAILURE;
+        }
+    }
+
+  if (T2OConnectionType == 1) /* Multicast producing */
+    {
+      if (openProducingMulticastConnection(pa_pstIOConnObj, pstCPF_data)
+          == EIP_ERROR)
+        {
+          OPENER_TRACE_ERR("error in OpenMulticast Connection\n");
+          return CIP_ERROR_CONNECTION_FAILURE;
+        }
+    }
+  else if (T2OConnectionType == 2) /* Point to Point producing */
+    {
+
+      if (OpenProducingPointToPointConnection(pa_pstIOConnObj, pstCPF_data)
+          != EIP_OK)
+        {
+          OPENER_TRACE_ERR("error in PointToPoint producing connection\n");
+          return CIP_ERROR_CONNECTION_FAILURE;
+        }
+    }
+
+  return nRetVal;
+}
+
+void
+closeCommChannelsAndRemoveFromActiveConnsList(
+    struct CIP_ConnectionObject *pa_pstConnObjData)
+{
+  IApp_CloseSocket(pa_pstConnObjData->sockfd[CONSUMING]);
+  pa_pstConnObjData->sockfd[CONSUMING] = EIP_INVALID_SOCKET;
+  IApp_CloseSocket(pa_pstConnObjData->sockfd[PRODUCING]);
+  pa_pstConnObjData->sockfd[PRODUCING] = EIP_INVALID_SOCKET;
+
+  removeFromActiveConnections(pa_pstConnObjData);
+}

+ 49 - 0
src/cip/cipioconnection.h

@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2011, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#ifndef CIPIOCONNECTION_H_
+#define CIPIOCONNECTION_H_
+
+#include <opener_api.h>
+
+/** \brief Setup all data in order to establish an IO connection
+ *
+ * This function can be called after all data has been parsed from the forward open request
+ * @param pa_pstConnObjData pointer to the connection object structure holding the parsed data from the forward open request
+ * @param pa_pnExtendedError the extended error code in case an error happened
+ * @return general status on the establishment
+ *    - EIP_OK ... on success
+ *    - On an error the general status code to be put into the response
+ */
+int
+establishIOConnction(struct CIP_ConnectionObject *pa_pstConnObjData,
+    EIP_UINT16 *pa_pnExtendedError);
+
+/** \brief Take the data given in the connection object structure and open the necessary communication channels
+ *
+ * This function will use the g_stCPFDataItem!
+ * @param pa_pstIOConnObj pointer to the connection object data
+ * @return general status on the open process
+ *    - EIP_OK ... on success
+ *    - On an error the general status code to be put into the response
+ */
+int
+openCommunicationChannels(struct CIP_ConnectionObject *pa_pstIOConnObj);
+
+
+/*! \brief close the communication channels of the given connection and remove it
+ * from the active connections list.
+ *
+ * @param pa_pstIOConnObj pointer to the connection object data
+ */
+void
+closeCommChannelsAndRemoveFromActiveConnsList(
+    struct CIP_ConnectionObject *pa_pstConnObjData);
+
+extern EIP_UINT8 *g_pnConfigDataBuffer;
+extern unsigned int g_unConfigDataLen;
+
+#endif /* CIPIOCONNECTION_H_ */

+ 23 - 0
src/opener_api.h

@@ -255,6 +255,29 @@ S_CIP_Instance *
 createAssemblyObject(EIP_UINT32 pa_nInstanceID, EIP_BYTE *pa_data,
     EIP_UINT16 pa_datalength);
 
+
+struct CIP_ConnectionObject;
+
+
+typedef int (*TConnOpenFunc)(struct CIP_ConnectionObject *pa_pstConnObj, EIP_UINT16 *pa_pnExtendedError);
+typedef void (*TConnCloseFunc)(struct CIP_ConnectionObject *pa_pstConnObj);
+typedef void (*TConnTimeOutFunc)(struct CIP_ConnectionObject *pa_pstConnObj);
+typedef EIP_STATUS (*TConnSendDataFunc)(struct CIP_ConnectionObject *pa_pstConnection);
+typedef EIP_STATUS (*TConnRecvDataFunc)(struct CIP_ConnectionObject *pa_pstConnection, EIP_UINT8 * pa_pnData, EIP_UINT16 pa_nDataLength);
+
+
+/*!\ingroup CIP_API
+ * \brief register open and close functions for an specific object.
+ *
+ * With this function any object can be enabled to be a target for forward open/close
+ * request.
+ * @param pa_nClassId    the class id
+ * @param pa_pfOpenFunc   pointer to the function handling the open process
+ * @return EIP_OK on success
+ */
+EIP_STATUS
+addConnectableObject(EIP_UINT32 pa_nClassId, TConnOpenFunc pa_pfOpenFunc);
+
 /** \ingroup CIP_API
  * Configures the connection point for an exclusive owner connection.
  *

+ 1 - 1
src/ports/platform-pc/main.c

@@ -156,7 +156,7 @@ IApp_AfterAssemblyDataReceived(S_CIP_Instance *pa_pstInstance)
   return nRetVal;
 }
 
-bool
+EIP_BOOL8
 IApp_BeforeAssemblyDataSend(S_CIP_Instance *pa_pstInstance)
 {
   /*update data to be sent e.g., read inputs of the device */

+ 2 - 2
src/ports/platform-pc/networkhandler.c

@@ -333,13 +333,13 @@ Start_NetworkHandler()
                     if (nReceived_size == 0)
                       {
                         OPENER_TRACE_STATE("connection closed by client\n");
-                        closeConnection(pstConnection);
+                        pstConnection->m_pfCloseFunc(pstConnection);
                         continue;
                       }
                     if (nReceived_size <= 0)
                       {
                         OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno));
-                        closeConnection(pstConnection);
+                        pstConnection->m_pfCloseFunc(pstConnection);
                         continue;
                       }
 

+ 9 - 1
src/ports/platform-pc/opener_user_conf.h

@@ -38,6 +38,14 @@ typedef unsigned short in_port_t;
 #define OPENER_DEVICE_MINOR_REVISION      2
 #define OPENER_DEVICE_NAME      "OpENer PC"
 
+/*! Define the number of objects that may be used in connections
+ *
+ *  This number needs only to consider additional objects. Connections to
+ *  the connection manager object as well as to the assembly object are supported
+ *  in any case.
+ */
+#define OPENER_CIP_NUM_APPLICATION_SPECIFIC_CONNECTABLE_OBJECTS 1
+
 /*! Define the number of supported explicit connections.
  *  According to ODVA's PUB 70 this number should be greater than 6.
  */  
@@ -122,7 +130,7 @@ typedef unsigned short in_port_t;
 #else
 
 /* for release builds execute the assertion, but don't test it */
-#define OPENER_ASSERT(assertion) assertion
+#define OPENER_ASSERT(assertion) (assertion)
 
 /* the above may result in "statement with no effect" warnings.
  *  If you do not use assert()s to run functions, the an empty