diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index de323ef4..9ebadac2 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -562,17 +562,20 @@ IedConnection_connect(IedConnection self, IedClientError* error, const char* hos if (IedConnection_getState(self) != IED_STATE_CONNECTED) { - MmsConnection_setConnectionLostHandler(self->connection, connectionLostHandler, (void*) self); + MmsConnection_setConnectionLostHandler(self->connection, NULL, NULL); MmsConnection_setInformationReportHandler(self->connection, informationReportHandler, self); MmsConnection_setConnectTimeout(self->connection, self->connectionTimeout); if (MmsConnection_connect(self->connection, &mmsError, hostname, tcpPort)) { + MmsConnection_setConnectionLostHandler(self->connection, connectionLostHandler, (void*) self); *error = IED_ERROR_OK; IedConnection_setState(self, IED_STATE_CONNECTED); } - else + else { + IedConnection_setState(self, IED_STATE_IDLE); *error = iedConnection_mapMmsErrorToIedError(mmsError); + } } else *error = IED_ERROR_ALREADY_CONNECTED; diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index ee1ca0ec..9c448fee 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -59,6 +59,13 @@ typedef struct { char* revision; } MmsServerIdentity; +typedef enum { + MMS_CONNECTION_STATE_CLOSED, + MMS_CONNECTION_STATE_CONNECTING, + MMS_CONNECTION_STATE_CONNECTED, + MMS_CONNECTION_STATE_CLOSING +} MmsConnectionState; + typedef void (*MmsInformationReportHandler) (void* parameter, char* domainName, char* variableListName, MmsValue* value, bool isVariableListName); @@ -90,6 +97,20 @@ MmsConnection_create(void); MmsConnection MmsConnection_createSecure(TLSConfiguration tlsConfig); +/** + * \brief Create a new MmsConnection instance configured for non-threaded mode + * + * NOTE: This constructor doesn't create a background thread for connection handling. + * The user has to call the MmsConnection_tick function periodically to ensure that + * the MMS connection can be handled properly. + * + * \param tlsConfig TLS configuration parameters and certificates or NULL for non-TLS mode. + * + * \return the newly created instance. + */ +MmsConnection +MmsConnection_createNonThreaded(TLSConfiguration tlsConfig); + /** * \brief Callback function to intercept raw MMS messages * @@ -181,6 +202,11 @@ MmsConnection_getIsoConnectionParameters(MmsConnection self); MmsConnectionParameters MmsConnection_getMmsConnectionParameters(MmsConnection self); +typedef void (*MmsConnectionStateChangedHandler) (MmsConnection connection, void* parameter, MmsConnectionState newState); + +void +MmsConnection_setConnectionStateChangedHandler(MmsConnection self, MmsConnectionStateChangedHandler handler, void* parameter); + /** * \brief User provided handler function that will be called if the connection to the server is lost * @@ -235,6 +261,23 @@ MmsConnection_destroy(MmsConnection self); bool MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort); +void +MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort); + + +// return value indicates that connection is currently waiting and calling thread can be suspended + +/** + * \brief Call MmsConnection state machine and connection handling code (for non-threaded mode only) + * + * This function has to be called periodically by the user application in non-threaded mode. + * + * \return true when connection is currently waiting and calling thread can be suspended, false means + * connection is busy and the tick function should be called again as soon as possible. + */ +bool +MmsConnection_tick(MmsConnection self); + /** * \brief Close the connection - not recommended * @@ -246,6 +289,9 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server void MmsConnection_close(MmsConnection self); +typedef void +(*MmsConnection_ConcludeAbortHandler) (void* parameter, MmsError mmsError, bool success); + /** * \brief Uses the MMS/ACSE abort service to close the connection to the server * @@ -260,6 +306,9 @@ MmsConnection_close(MmsConnection self); void MmsConnection_abort(MmsConnection self, MmsError* mmsError); +void +MmsConnection_abortAsync(MmsConnection self, MmsError* mmsError); + /** * \brief Uses the MMS conclude service to close the connection to the server * @@ -274,6 +323,9 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError); void MmsConnection_conclude(MmsConnection self, MmsError* mmsError); +void +MmsConnection_concludeAsync(MmsConnection self, MmsError* mmsError, MmsConnection_ConcludeAbortHandler handler, void* parameter); + typedef void (*MmsConnection_GenericServiceHandler) (int invokeId, void* parameter, MmsError mmsError, bool success); diff --git a/src/mms/inc_private/iso_client_connection.h b/src/mms/inc_private/iso_client_connection.h index 88ab924d..dfaa26ce 100644 --- a/src/mms/inc_private/iso_client_connection.h +++ b/src/mms/inc_private/iso_client_connection.h @@ -1,7 +1,7 @@ /* * iso_client_connection.h * - * This is an internal interface of MMSClientConnection that connects MMS to the ISO client + * This is an internal interface of MMS client connection that connects MMS to the ISO client * protocol stack. It is used as an abstraction layer to isolate the MMS code from the lower * protocol layers. * @@ -53,15 +53,30 @@ typedef void* typedef struct sIsoClientConnection* IsoClientConnection; IsoClientConnection -IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParameter); +IsoClientConnection_create(IsoConnectionParameters parameters, IsoIndicationCallback callback, void* callbackParameter); void IsoClientConnection_destroy(IsoClientConnection self); +bool +IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTimeoutInMs); + +/** + * called by tick function + * + * \return value indicates that connection is currently waiting and calling thread can be suspended + */ +bool +IsoClientConnection_handleConnection(IsoClientConnection self); + void -IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters params, - ByteBuffer* payload, uint32_t connectTimeoutInMs); +IsoClientConnection_associate(IsoClientConnection self, uint32_t connectTimeoutInMs); +/** + * Send message and release the transmit buffer + * + * \param payload message to send + */ void IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payload); @@ -69,12 +84,10 @@ void IsoClientConnection_release(IsoClientConnection self); /** - * \brief Send ACSE abort message and wait until connection is closed by server or timeout occured - * - * \return true if abort has been successful, false indicates a timeout + * \brief Send ACSE abort message and wait until connection is closed by server or timeout occurred */ -bool -IsoClientConnection_abort(IsoClientConnection self); +void +IsoClientConnection_abortAsync(IsoClientConnection self); void IsoClientConnection_close(IsoClientConnection self); @@ -93,14 +106,6 @@ IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self); void IsoClientConnection_releaseTransmitBuffer(IsoClientConnection self); -/* - * The client should release the receive buffer in order for the IsoClientConnection to - * reuse the buffer! If this function is not called then the reception of messages is - * blocked! - */ -void -IsoClientConnection_releaseReceiveBuffer(IsoClientConnection self); - void* IsoClientConnection_getSecurityToken(IsoClientConnection self); diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 0ce3cfb4..2a1a78cb 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -1,7 +1,7 @@ /* * mms_msg_internal.h * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -42,12 +42,7 @@ #define DEBUG_MMS_CLIENT 0 #endif -typedef enum { - MMS_STATE_CLOSED, - MMS_STATE_CONNECTING, - MMS_STATE_CONNECTED -} AssociationState; - +#if 0 typedef enum { MMS_CON_IDLE, MMS_CON_WAITING, @@ -55,6 +50,7 @@ typedef enum { MMS_CON_ASSOCIATED, MMS_CON_RESPONSE_PENDING } ConnectionState; +#endif #define CONCLUDE_STATE_CONNECTION_ACTIVE 0 #define CONCLUDE_STATE_REQUESTED 1 @@ -102,12 +98,6 @@ struct sMmsConnection { Semaphore lastInvokeIdLock; uint32_t lastInvokeId; - Semaphore lastResponseLock; - volatile uint32_t responseInvokeId; - ByteBuffer* lastResponse; - volatile uint32_t lastResponseBufPos; - volatile MmsError lastResponseError; - Semaphore outstandingCallsLock; MmsOutstandingCall outstandingCalls; @@ -116,30 +106,36 @@ struct sMmsConnection { IsoClientConnection isoClient; - volatile AssociationState associationState; - Semaphore associationStateLock; +#if (CONFIG_MMS_THREADLESS_STACK == 0) + Thread connectionHandlingThread; + bool createThread; + bool connectionThreadRunning; +#endif - volatile ConnectionState connectionState; - Semaphore connectionStateLock; + volatile MmsConnectionState connectionState; + Semaphore associationStateLock; MmsConnectionParameters parameters; IsoConnectionParameters isoParameters; + MmsConnectionStateChangedHandler stateChangedHandler; + void* stateChangedHandlerParameter; + MmsInformationReportHandler reportHandler; void* reportHandlerParameter; MmsConnectionLostHandler connectionLostHandler; void* connectionLostHandlerParameter; + MmsConnection_ConcludeAbortHandler concludeHandler; + void* concludeHandlerParameter; + uint64_t concludeTimeout; + #if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) void* rawMmsMessageHandler; void* rawMmsMessageHandlerParameter; #endif - /* state of an active connection conclude/release process */ - volatile int concludeState; - Semaphore concludeStateLock; - #if (MMS_OBTAIN_FILE_SERVICE == 1) int32_t nextFrsmId; MmsFileReadStateMachine frsms[CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION]; @@ -322,7 +318,7 @@ bool mmsClient_parseFileDirectoryResponse(ByteBuffer* response, int bufPos, uint32_t invokeId, MmsConnection_FileDirectoryHandler handler, void* parameter); bool -mmsClient_parseInitiateResponse(MmsConnection self); +mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response); int mmsClient_createConcludeRequest(MmsConnection self, ByteBuffer* message); diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index 53eca945..e78b8f5d 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -3,7 +3,7 @@ * * Client side representation of the ISO stack (COTP, session, presentation, ACSE) * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -47,21 +47,38 @@ #endif /* DEBUG_ISO_SERVER */ #define STATE_IDLE 0 -#define STATE_ASSOCIATED 1 +#define STATE_CONNECTED 1 #define STATE_ERROR 2 +#define STATE_CONNECTING 3 #define TPKT_RFC1006_HEADER_SIZE 4 #define ISO_CLIENT_BUFFER_SIZE CONFIG_MMS_MAXIMUM_PDU_SIZE + 100 +typedef enum { + INT_STATE_IDLE, + INT_STATE_TCP_CONNECTING, + INT_STATE_WAIT_FOR_COTP_CONNECT_RESP, + INT_STATE_WAIT_FOR_ACSE_RESP, + INT_STATE_WAIT_FOR_DATA_MSG, + INT_STATE_CLOSING_CONNECTION, + INT_STATE_CLOSE_ON_ERROR, + INT_STATE_ERROR +} eIsoClientInternalState; + struct sIsoClientConnection { + IsoConnectionParameters parameters; + IsoIndicationCallback callback; void* callbackParameter; + volatile eIsoClientInternalState intState; volatile int state; Semaphore stateMutex; + uint64_t nextReadTimeout; + Socket socket; #if (CONFIG_MMS_SUPPORT_TLS == 1) @@ -81,20 +98,14 @@ struct sIsoClientConnection Semaphore transmitBufferMutex; ByteBuffer* receivePayloadBuffer; - Semaphore receiveBufferMutex; + + Semaphore tickMutex; uint8_t* cotpReadBuf; uint8_t* cotpWriteBuf; ByteBuffer* cotpReadBuffer; ByteBuffer* cotpWriteBuffer; - - volatile bool handlingThreadRunning; - volatile bool stopHandlingThread; - volatile bool destroyHandlingThread; - volatile bool startHandlingThread; - - Thread thread; }; static void @@ -117,387 +128,565 @@ getState(IsoClientConnection self) return stateVal; } -static void -connectionHandlingThread(IsoClientConnection self) +static inline void +setIntState(IsoClientConnection self, eIsoClientInternalState newState) { - IsoSessionIndication sessionIndication; + self->intState = newState; +} - self->handlingThreadRunning = true; +static inline eIsoClientInternalState +getIntState(IsoClientConnection self) +{ + return self->intState; +} - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT_CONNECTION: new connection %p\n", self); +IsoClientConnection +IsoClientConnection_create(IsoConnectionParameters parameters, IsoIndicationCallback callback, void* callbackParameter) +{ + IsoClientConnection self = (IsoClientConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoClientConnection)); - /* Wait until lower layer association is finished */ - Semaphore_wait(self->receiveBufferMutex); + if (self) { - CotpConnection_resetPayload(self->cotpConnection); + self->parameters = parameters; + self->callback = callback; + self->callbackParameter = callbackParameter; - while (true) { + self->intState = INT_STATE_IDLE; + self->state = STATE_IDLE; + self->stateMutex = Semaphore_create(1); - TpktState packetState; + self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(ISO_CLIENT_BUFFER_SIZE); - while ((packetState = CotpConnection_readToTpktBuffer(self->cotpConnection)) == TPKT_WAITING) - { - Thread_sleep(1); + self->transmitPayloadBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); + self->transmitPayloadBuffer->buffer = self->sendBuffer; + self->transmitPayloadBuffer->maxSize = ISO_CLIENT_BUFFER_SIZE; - if (self->stopHandlingThread) { - packetState = TPKT_ERROR; - break; - } + self->receivePayloadBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); - self->callback(ISO_IND_TICK, self->callbackParameter, NULL); - } + self->transmitBufferMutex = Semaphore_create(1); - if (packetState == TPKT_ERROR) - break; + self->tickMutex = Semaphore_create(1); - CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); + self->receiveBuf = (uint8_t*) GLOBAL_MALLOC(ISO_CLIENT_BUFFER_SIZE); + self->receiveBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); + ByteBuffer_wrap(self->receiveBuffer, self->receiveBuf, 0, ISO_CLIENT_BUFFER_SIZE); - if (cotpIndication == COTP_MORE_FRAGMENTS_FOLLOW) - continue; + self->presentation = (IsoPresentation*) GLOBAL_CALLOC(1, sizeof(IsoPresentation)); - if (cotpIndication != COTP_DATA_INDICATION) - break; + self->session = (IsoSession*) GLOBAL_CALLOC(1, sizeof(IsoSession)); - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT_CONNECTION: parse message\n"); + self->cotpReadBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); + self->cotpWriteBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); - sessionIndication = - IsoSession_parseMessage(self->session, - CotpConnection_getPayload(self->cotpConnection)); + self->cotpReadBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); + ByteBuffer_wrap(self->cotpReadBuffer, self->cotpReadBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); - if (sessionIndication != SESSION_DATA) { - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT_CONNECTION: Invalid session message\n"); - break; - } + self->cotpWriteBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); + ByteBuffer_wrap(self->cotpWriteBuffer, self->cotpWriteBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); + + self->cotpConnection = (CotpConnection*) GLOBAL_CALLOC(1, sizeof(CotpConnection)); + } + + return self; +} + + + +static bool +sendConnectionRequestMessage(IsoClientConnection self) +{ + /* COTP (ISO transport) handshake */ + CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer); + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (params->tlsConfiguration) { + + /* create TLSSocket and start TLS authentication */ + TLSSocket tlsSocket = TLSSocket_create(self->socket, params->tlsConfiguration, false); + + if (tlsSocket) + self->cotpConnection->tlsSocket = tlsSocket; + else { - if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) { if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT_CONNECTION: Invalid presentation message\n"); - break; + printf("TLS handshake failed!\n"); + + goto returnError; } + } +#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ + + + /* COTP (ISO transport) handshake */ + CotpIndication cotpIndication = + CotpConnection_sendConnectionRequestMessage(self->cotpConnection, self->parameters); - self->callback(ISO_IND_DATA, self->callbackParameter, - &(self->presentation->nextPayload)); + if (cotpIndication != COTP_OK) + return false; + else + return true; +} - /* wait for user to release the buffer */ - Semaphore_wait(self->receiveBufferMutex); +static void +sendAcseInitiateRequest(IsoClientConnection self) +{ + /* Upper layers handshake */ + struct sBufferChain sAcsePayload; + BufferChain acsePayload = &sAcsePayload; + acsePayload->buffer = self->transmitPayloadBuffer->buffer; + acsePayload->partLength = self->transmitPayloadBuffer->size; + acsePayload->length = self->transmitPayloadBuffer->size; + acsePayload->nextPart = NULL; - CotpConnection_resetPayload(self->cotpConnection); - } + AcseConnection_init(&(self->acseConnection), NULL, NULL, NULL); - self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL);; + AcseAuthenticationParameter authParameter = self->parameters->acseAuthParameter; - setState(self, STATE_IDLE); + struct sBufferChain sAcseBuffer; + BufferChain acseBuffer = &sAcseBuffer; + + acseBuffer->buffer = self->sendBuffer + self->transmitPayloadBuffer->size; + acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acsePayload->length; + + AcseConnection_createAssociateRequestMessage(&(self->acseConnection), self->parameters, acseBuffer, acsePayload, + authParameter); + + struct sBufferChain sPresentationBuffer; + BufferChain presentationBuffer = &sPresentationBuffer; + + presentationBuffer->buffer = self->sendBuffer + acseBuffer->length; + presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length; + + IsoPresentation_init(self->presentation); + IsoPresentation_createConnectPdu(self->presentation, self->parameters, presentationBuffer, acseBuffer); + + struct sBufferChain sSessionBuffer; + BufferChain sessionBuffer = &sSessionBuffer; + sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length; + + IsoSession_init(self->session); + IsoSession_createConnectSpdu(self->session, self->parameters, sessionBuffer, + presentationBuffer); + + CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer); + + Semaphore_post(self->transmitBufferMutex); +} + + +static void +releaseSocket(IsoClientConnection self) +{ + if (self->socket) { #if (CONFIG_MMS_SUPPORT_TLS == 1) if (self->cotpConnection->tlsSocket) TLSSocket_close(self->cotpConnection->tlsSocket); #endif - Socket_destroy(self->socket); + Socket_destroy(self->socket); + self->socket = NULL; + } +} - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT_CONNECTION: exit connection %p\n", self); +/* + * Connection state machine + * + * called by tick function + * + * \return value indicates that connection is currently waiting and calling thread can be suspended + */ +bool +IsoClientConnection_handleConnection(IsoClientConnection self) +{ + Semaphore_wait(self->tickMutex); - /* release buffer to enable reuse of client connection */ - Semaphore_post(self->receiveBufferMutex); + bool waits = false; - self->handlingThreadRunning = false; -} + eIsoClientInternalState currentState = getIntState(self); + eIsoClientInternalState nextState = currentState; -static void* -connectionThreadFunction(void* parameter) -{ - IsoClientConnection self = (IsoClientConnection) parameter; + switch (currentState) { + + case INT_STATE_IDLE: + case INT_STATE_ERROR: + waits = true; + break; - while (self->destroyHandlingThread == false) { + case INT_STATE_TCP_CONNECTING: + { + SocketState socketState = Socket_checkAsyncConnectState(self->socket); + + if (socketState == SOCKET_STATE_CONNECTED) { + if (sendConnectionRequestMessage(self)) { + self->nextReadTimeout = Hal_getTimeInMs() + CONFIG_TCP_READ_TIMEOUT_MS; + nextState = INT_STATE_WAIT_FOR_COTP_CONNECT_RESP; + } + else { + IsoClientConnection_releaseTransmitBuffer(self); + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); + nextState = INT_STATE_CLOSE_ON_ERROR; + } + } + else if (socketState == SOCKET_STATE_FAILED) { + IsoClientConnection_releaseTransmitBuffer(self); + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { + waits = true; + } - if (self->startHandlingThread) { - self->startHandlingThread = false; - connectionHandlingThread(self); } + break; - Thread_sleep(1); - } + case INT_STATE_WAIT_FOR_COTP_CONNECT_RESP: + { + uint64_t currentTime = Hal_getTimeInMs(); - self->destroyHandlingThread = false; + if (currentTime > self->nextReadTimeout) { - return NULL; -} + if (DEBUG_ISO_CLIENT) + printf("Timeout waiting for COTP CR\n"); + IsoClientConnection_releaseTransmitBuffer(self); -IsoClientConnection -IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParameter) -{ - IsoClientConnection self = (IsoClientConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoClientConnection)); + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - if (self == NULL) - return NULL; + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { - self->callback = callback; - self->callbackParameter = callbackParameter; + TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection); - self->state = STATE_IDLE; - self->stateMutex = Semaphore_create(1); + if (packetState == TPKT_PACKET_COMPLETE) { - self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(ISO_CLIENT_BUFFER_SIZE); + CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); - self->transmitPayloadBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); - self->transmitPayloadBuffer->buffer = self->sendBuffer; - self->transmitPayloadBuffer->maxSize = ISO_CLIENT_BUFFER_SIZE; + if (cotpIndication != COTP_CONNECT_INDICATION) { + if (DEBUG_ISO_CLIENT) + printf("Unexpected COTP state (%i)\n", cotpIndication); - self->receivePayloadBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); + IsoClientConnection_releaseTransmitBuffer(self); - self->transmitBufferMutex = Semaphore_create(1); + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - self->receiveBufferMutex = Semaphore_create(1); + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { + sendAcseInitiateRequest(self); - self->receiveBuf = (uint8_t*) GLOBAL_MALLOC(ISO_CLIENT_BUFFER_SIZE); - self->receiveBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); - ByteBuffer_wrap(self->receiveBuffer, self->receiveBuf, 0, ISO_CLIENT_BUFFER_SIZE); + self->nextReadTimeout = Hal_getTimeInMs() + CONFIG_TCP_READ_TIMEOUT_MS; - self->presentation = (IsoPresentation*) GLOBAL_CALLOC(1, sizeof(IsoPresentation)); + nextState = INT_STATE_WAIT_FOR_ACSE_RESP; + } + } + else if (packetState == TPKT_ERROR) { + if (DEBUG_ISO_CLIENT) + printf("Error receiving COTP message\n"); - self->session = (IsoSession*) GLOBAL_CALLOC(1, sizeof(IsoSession)); + IsoClientConnection_releaseTransmitBuffer(self); - self->cotpReadBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); - self->cotpWriteBuf = (uint8_t*) GLOBAL_MALLOC(CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - self->cotpReadBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); - ByteBuffer_wrap(self->cotpReadBuffer, self->cotpReadBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { + waits = true; + } - self->cotpWriteBuffer = (ByteBuffer*) GLOBAL_CALLOC(1, sizeof(ByteBuffer)); - ByteBuffer_wrap(self->cotpWriteBuffer, self->cotpWriteBuf, 0, CONFIG_COTP_MAX_TPDU_SIZE + TPKT_RFC1006_HEADER_SIZE); + } + } + break; - self->cotpConnection = (CotpConnection*) GLOBAL_CALLOC(1, sizeof(CotpConnection)); + case INT_STATE_WAIT_FOR_ACSE_RESP: + { + uint64_t currentTime = Hal_getTimeInMs(); - self->handlingThreadRunning = false; + if (currentTime > self->nextReadTimeout) { - self->stopHandlingThread = false; - self->destroyHandlingThread = false; - self->startHandlingThread = false; + if (DEBUG_ISO_CLIENT) + printf("Timeout waiting for ACSE initiate response\n"); - return self; -} + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); -void -IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters params, - ByteBuffer* payload, uint32_t connectTimeoutInMs) -{ - self->socket = TcpSocket_create(); + nextState = INT_STATE_ERROR; + } + else { - Socket_setConnectTimeout(self->socket, connectTimeoutInMs); + TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection); -#if (CONFIG_ACTIVATE_TCP_KEEPALIVE == 1) - Socket_activateTcpKeepAlive(self->socket, - CONFIG_TCP_KEEPALIVE_IDLE, - CONFIG_TCP_KEEPALIVE_INTERVAL, - CONFIG_TCP_KEEPALIVE_CNT); -#endif - // (1) Function blocks - if (!Socket_connect(self->socket, params->hostname, params->tcpPort)) - goto returnError; + if (packetState == TPKT_PACKET_COMPLETE) { - // (2) Send connection request message + CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); - /* COTP (ISO transport) handshake */ - CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer); + if (cotpIndication != COTP_DATA_INDICATION) { + if (DEBUG_ISO_CLIENT) + printf("Unexpected COTP state (%i)\n", cotpIndication); -#if (CONFIG_MMS_SUPPORT_TLS == 1) - if (params->tlsConfiguration) { + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - /* create TLSSocket and start TLS authentication */ - TLSSocket tlsSocket = TLSSocket_create(self->socket, params->tlsConfiguration, false); + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { - if (tlsSocket) - self->cotpConnection->tlsSocket = tlsSocket; - else { + /* parse ACSE response */ - if (DEBUG_ISO_CLIENT) - printf("TLS handshake failed!\n"); + IsoSessionIndication sessionIndication; - goto returnError; - } - } -#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ + sessionIndication = + IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); + if (sessionIndication != SESSION_CONNECT) { + if (DEBUG_ISO_CLIENT) + printf("IsoClientConnection_associate: no session connect indication\n"); - /* COTP (ISO transport) handshake */ - CotpIndication cotpIndication = - CotpConnection_sendConnectionRequestMessage(self->cotpConnection, params); + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - TpktState packetState; + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { - uint64_t timeout = Hal_getTimeInMs() + CONFIG_TCP_READ_TIMEOUT_MS; + if (IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session)) == false) { - // (3) Waiting for response (blocking) - while (((packetState = CotpConnection_readToTpktBuffer(self->cotpConnection)) == TPKT_WAITING) - && (Hal_getTimeInMs() < timeout)) - { - Thread_sleep(1); - } + if (DEBUG_ISO_CLIENT) + printf("IsoClientConnection_associate: no presentation ok indication\n"); - if (packetState != TPKT_PACKET_COMPLETE) - goto returnError; + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { - if (cotpIndication != COTP_CONNECT_INDICATION) - goto returnError; + AcseIndication acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload); - // (4) Send ACSE Initiate request + if (acseIndication != ACSE_ASSOCIATE) { + if (DEBUG_ISO_CLIENT) + printf("IsoClientConnection_associate: no ACSE_ASSOCIATE indication\n"); - /* Upper layers handshake */ - struct sBufferChain sAcsePayload; - BufferChain acsePayload = &sAcsePayload; - acsePayload->buffer = payload->buffer; - acsePayload->partLength = payload->size; - acsePayload->length = payload->size; - acsePayload->nextPart = NULL; + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - AcseConnection_init(&(self->acseConnection), NULL, NULL, NULL); + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { - AcseAuthenticationParameter authParameter = params->acseAuthParameter; + } - struct sBufferChain sAcseBuffer; - BufferChain acseBuffer = &sAcseBuffer; + } - acseBuffer->buffer = self->sendBuffer + payload->size; - acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acsePayload->length; + ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer, + self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize); - AcseConnection_createAssociateRequestMessage(&(self->acseConnection), params, acseBuffer, acsePayload, - authParameter); + setState(self, STATE_CONNECTED); + nextState = INT_STATE_WAIT_FOR_DATA_MSG; - struct sBufferChain sPresentationBuffer; - BufferChain presentationBuffer = &sPresentationBuffer; + self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer); - presentationBuffer->buffer = self->sendBuffer + acseBuffer->length; - presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length; + CotpConnection_resetPayload(self->cotpConnection); + } + } + } + else if (packetState == TPKT_ERROR) { + if (DEBUG_ISO_CLIENT) + printf("Error receiving COTP message\n"); - IsoPresentation_init(self->presentation); - IsoPresentation_createConnectPdu(self->presentation, params, presentationBuffer, acseBuffer); + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - struct sBufferChain sSessionBuffer; - BufferChain sessionBuffer = &sSessionBuffer; - sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length; + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { + waits = true; + } - IsoSession_init(self->session); - IsoSession_createConnectSpdu(self->session, params, sessionBuffer, - presentationBuffer); + } + } + break; - CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer); + case INT_STATE_WAIT_FOR_DATA_MSG: + { + TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection); - Semaphore_post(self->transmitBufferMutex); + if (packetState == TPKT_ERROR) { + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else if (packetState == TPKT_PACKET_COMPLETE) { - while ((packetState = CotpConnection_readToTpktBuffer(self->cotpConnection)) == TPKT_WAITING) - { - Thread_sleep(1); - } + CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); - cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); + switch (cotpIndication) { - if (cotpIndication != COTP_DATA_INDICATION) - goto returnError; + case COTP_MORE_FRAGMENTS_FOLLOW: + break; - IsoSessionIndication sessionIndication; + case COTP_DISCONNECT_INDICATION: + { + nextState = INT_STATE_CLOSING_CONNECTION; + } + break; - sessionIndication = - IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); + case COTP_DATA_INDICATION: + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT_CONNECTION: parse message\n"); - if (sessionIndication != SESSION_CONNECT) { - if (DEBUG_ISO_CLIENT) - printf("IsoClientConnection_associate: no session connect indication\n"); - goto returnError; - } + IsoSessionIndication sessionIndication = + IsoSession_parseMessage(self->session, + CotpConnection_getPayload(self->cotpConnection)); - if (!IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session))) { - if (DEBUG_ISO_CLIENT) - printf("IsoClientConnection_associate: no presentation ok indication\n"); - goto returnError; - } + if (sessionIndication != SESSION_DATA) { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT_CONNECTION: Invalid session message\n"); - AcseIndication acseIndication; + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { - // (5) Wait for ACSE initiate response message + if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) { - acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload); + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT_CONNECTION: Invalid presentation message\n"); - if (acseIndication != ACSE_ASSOCIATE) { - if (DEBUG_ISO_CLIENT) - printf("IsoClientConnection_associate: no ACSE_ASSOCIATE indication\n"); - goto returnError; - } + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else { + + self->callback(ISO_IND_DATA, self->callbackParameter, + &(self->presentation->nextPayload)); + + CotpConnection_resetPayload(self->cotpConnection); + } + } + } + break; + + default: + { + nextState = INT_STATE_CLOSE_ON_ERROR; + } + break; + + } + } + else { + waits = true; + } + + } + break; - ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer, - self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize); + case INT_STATE_CLOSE_ON_ERROR: + { + setState(self, STATE_ERROR); + + self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL);; + + releaseSocket(self); + + nextState = INT_STATE_ERROR; + } + break; - Semaphore_wait(self->receiveBufferMutex); + case INT_STATE_CLOSING_CONNECTION: + { + setState(self, STATE_IDLE); - self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer); + self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL);; - /* wait for upper layer to release buffer */ - Semaphore_wait(self->receiveBufferMutex); + releaseSocket(self); - setState(self, STATE_ASSOCIATED); + nextState = INT_STATE_IDLE; + } + break; - if (self->thread == NULL) { - self->thread = Thread_create(connectionThreadFunction, self, false); - Thread_start(self->thread); } - self->startHandlingThread = true; + self->callback(ISO_IND_TICK, self->callbackParameter, NULL); + + setIntState(self, nextState); - while (self->handlingThreadRunning == false) - Thread_sleep(1); + Semaphore_post(self->tickMutex); - return; + return waits; +} -returnError: - self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - setState(self, STATE_ERROR); +bool +IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTimeoutInMs) +{ + bool success = true; - Socket_destroy(self->socket); - self->socket = NULL; + /* Create socket and start connect */ + setState(self, STATE_CONNECTING); - Semaphore_post(self->transmitBufferMutex); + Semaphore_wait(self->tickMutex); + + self->socket = TcpSocket_create(); + + Socket_setConnectTimeout(self->socket, connectTimeoutInMs); - return; +#if (CONFIG_ACTIVATE_TCP_KEEPALIVE == 1) + Socket_activateTcpKeepAlive(self->socket, + CONFIG_TCP_KEEPALIVE_IDLE, + CONFIG_TCP_KEEPALIVE_INTERVAL, + CONFIG_TCP_KEEPALIVE_CNT); +#endif + + setIntState(self, INT_STATE_TCP_CONNECTING); + + if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) { + + Socket_destroy(self->socket); + self->socket = NULL; + + setIntState(self, INT_STATE_ERROR); + setState(self, STATE_ERROR); + + IsoClientConnection_releaseTransmitBuffer(self); + + success = false; + } + + Semaphore_post(self->tickMutex); + + return success; } void IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuffer) { + if (getState(self) == STATE_CONNECTED) { + struct sBufferChain payloadBCMemory; + BufferChain payload = &payloadBCMemory; - struct sBufferChain payloadBCMemory; - BufferChain payload = &payloadBCMemory; + BufferChain_init(payload, payloadBuffer->size, payloadBuffer->size, NULL, payloadBuffer->buffer); - BufferChain_init(payload, payloadBuffer->size, payloadBuffer->size, NULL, payloadBuffer->buffer); + struct sBufferChain presentationBCMemory; + BufferChain presentationBuffer = &presentationBCMemory; - struct sBufferChain presentationBCMemory; - BufferChain presentationBuffer = &presentationBCMemory; + presentationBuffer->buffer = self->sendBuffer + payload->length; + presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE; - presentationBuffer->buffer = self->sendBuffer + payload->length; - presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE; + IsoPresentation_createUserData(self->presentation, presentationBuffer, payload); - IsoPresentation_createUserData(self->presentation, presentationBuffer, payload); + struct sBufferChain sessionBufferBCMemory; + BufferChain sessionBuffer = &sessionBufferBCMemory; - struct sBufferChain sessionBufferBCMemory; - BufferChain sessionBuffer = &sessionBufferBCMemory; + IsoSession_createDataSpdu(self->session, sessionBuffer, presentationBuffer); - IsoSession_createDataSpdu(self->session, sessionBuffer, presentationBuffer); + CotpIndication indication = CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer); - CotpIndication indication = CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer); + if (indication != COTP_OK) + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: IsoClientConnection_sendMessage: send message failed!\n"); + } + else { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: Not connected --> cannot send message\n"); + } /* release transmit buffer for use by API client */ Semaphore_post(self->transmitBufferMutex); - - if (indication != COTP_OK) - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT: IsoClientConnection_sendMessage: send message failed!\n"); } void @@ -506,13 +695,21 @@ IsoClientConnection_close(IsoClientConnection self) if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: IsoClientConnection_close\n"); - if (self->handlingThreadRunning) { - self->stopHandlingThread = true; - while (self->handlingThreadRunning) - Thread_sleep(1); - } + Semaphore_wait(self->tickMutex); + + eIsoClientInternalState intState = getIntState(self); + + if ((intState != INT_STATE_IDLE) && (intState != INT_STATE_ERROR) && (intState != INT_STATE_CLOSE_ON_ERROR)) { + setIntState(self, INT_STATE_CLOSING_CONNECTION); - setState(self, STATE_IDLE); + Semaphore_post(self->tickMutex); + + IsoClientConnection_handleConnection(self); + setState(self, STATE_IDLE); + } + else { + Semaphore_post(self->tickMutex); + } } @@ -522,7 +719,9 @@ IsoClientConnection_destroy(IsoClientConnection self) if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: IsoClientConnection_destroy\n"); - if (getState(self) == STATE_ASSOCIATED) { + int state = getState(self); + + if (state == STATE_CONNECTED) { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: call IsoClientConnection_close\n"); @@ -530,16 +729,6 @@ IsoClientConnection_destroy(IsoClientConnection self) IsoClientConnection_close(self); } - /* stop handling thread */ - self->destroyHandlingThread = true; - - if (self->thread != NULL) { - while (self->destroyHandlingThread) - Thread_sleep(1); - - Thread_destroy(self->thread); - } - if (self->receiveBuf != NULL) GLOBAL_FREEMEM(self->receiveBuf); if (self->receiveBuffer != NULL) @@ -567,18 +756,17 @@ IsoClientConnection_destroy(IsoClientConnection self) GLOBAL_FREEMEM(self->transmitPayloadBuffer); GLOBAL_FREEMEM(self->receivePayloadBuffer); - Semaphore_destroy(self->receiveBufferMutex); Semaphore_destroy(self->transmitBufferMutex); Semaphore_destroy(self->stateMutex); + Semaphore_destroy(self->tickMutex); GLOBAL_FREEMEM(self->sendBuffer); GLOBAL_FREEMEM(self); } -bool -IsoClientConnection_abort(IsoClientConnection self) +static void +sendAbortMessage(IsoClientConnection self) { - //TODO block other messages from being sent IsoClientConnection_allocateTransmitBuffer(self); struct sBufferChain sAcseBuffer; @@ -608,15 +796,12 @@ IsoClientConnection_abort(IsoClientConnection self) CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer); Semaphore_post(self->transmitBufferMutex); +} - uint64_t timeout = Hal_getTimeInMs() + CONFIG_TCP_READ_TIMEOUT_MS; - - while ((self->handlingThreadRunning == true) && (Hal_getTimeInMs() < timeout)); - - if (self->handlingThreadRunning) - return false; - else - return true; +void +IsoClientConnection_abortAsync(IsoClientConnection self) +{ + sendAbortMessage(self); } void @@ -669,9 +854,3 @@ IsoClientConnection_releaseTransmitBuffer(IsoClientConnection self) { Semaphore_post(self->transmitBufferMutex); } - -void -IsoClientConnection_releaseReceiveBuffer(IsoClientConnection self) -{ - Semaphore_post(self->receiveBufferMutex); -} diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index 01f504e8..4c117ddb 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -33,7 +33,6 @@ #include "byte_buffer.h" #include "ber_decode.h" -#include #include "tls_config.h" #define CONFIG_MMS_CONNECTION_DEFAULT_TIMEOUT 5000 @@ -41,61 +40,24 @@ #define OUTSTANDING_CALLS 10 static void -setAssociationState(MmsConnection self, AssociationState newState) +setConnectionState(MmsConnection self, MmsConnectionState newState) { Semaphore_wait(self->associationStateLock); - self->associationState = newState; - Semaphore_post(self->associationStateLock); -} - -static AssociationState -getAssociationState(MmsConnection self) -{ - AssociationState state; - - Semaphore_wait(self->associationStateLock); - state = self->associationState; + self->connectionState = newState; Semaphore_post(self->associationStateLock); - return state; + if (self->stateChangedHandler) + self->stateChangedHandler(self, self->stateChangedHandlerParameter, newState); } -static void -setConnectionState(MmsConnection self, ConnectionState newState) -{ - Semaphore_wait(self->connectionStateLock); - self->connectionState = newState; - Semaphore_post(self->connectionStateLock); -} - -static ConnectionState +static MmsConnectionState getConnectionState(MmsConnection self) { - ConnectionState state; + MmsConnectionState state; - Semaphore_wait(self->connectionStateLock); + Semaphore_wait(self->associationStateLock); state = self->connectionState; - Semaphore_post(self->connectionStateLock); - - return state; -} - -static void -setConcludeState(MmsConnection self, int newState) -{ - Semaphore_wait(self->concludeStateLock); - self->concludeState = newState; - Semaphore_post(self->concludeStateLock); -} - -static int -getConcludeState(MmsConnection self) -{ - int state; - - Semaphore_wait(self->concludeStateLock); - state = self->concludeState; - Semaphore_post(self->concludeStateLock); + Semaphore_post(self->associationStateLock); return state; } @@ -149,7 +111,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) GLOBAL_FREEMEM(variableListName); } else { - // Ignore domain and association specific information reports (not used by IEC 61850) + /* Ignore domain and association specific information reports (not used by IEC 61850) */ } } @@ -196,7 +158,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) self->reportHandler(self->reportHandlerParameter, domainId, variableListName, value, false); - // report handler should have deleted the MmsValue! + /* report handler should have deleted the MmsValue! */ if (variableSpecSize != 1) MmsValue_setElement(values, i, NULL); else @@ -239,7 +201,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) self->reportHandler(self->reportHandlerParameter, domainNameStr, itemNameStr, value, false); - // report handler should have deleted the MmsValue! + /* report handler should have deleted the MmsValue! */ if (variableSpecSize != 1) MmsValue_setElement(values, i, NULL); else @@ -254,7 +216,7 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) MmsValue_delete(values); } else { - // Ignore + /* Ignore */ if (DEBUG_MMS_CLIENT) printf("unrecognized information report\n"); } @@ -381,40 +343,6 @@ sendAsyncRequest(MmsConnection self, uint32_t invokeId, ByteBuffer* message, eMm return MMS_ERROR_NONE; } -static void -releaseResponse(MmsConnection self) -{ - Semaphore_wait(self->lastResponseLock); - self->responseInvokeId = 0; - self->lastResponseError = MMS_ERROR_NONE; - Semaphore_post(self->lastResponseLock); - - IsoClientConnection_releaseReceiveBuffer(self->isoClient); -} - -static uint32_t -getResponseInvokeId(MmsConnection self) -{ - uint32_t currentInvokeId; - - Semaphore_wait(self->lastResponseLock); - currentInvokeId = self->responseInvokeId; - Semaphore_post(self->lastResponseLock); - - return currentInvokeId; -} - -static void -waitUntilLastResponseHasBeenProcessed(MmsConnection self) -{ - uint32_t currentInvokeId = getResponseInvokeId(self); - - while (currentInvokeId != 0) { - Thread_sleep(1); - currentInvokeId = getResponseInvokeId(self); - } -} - static MmsError convertRejectCodesToMmsError(int rejectType, int rejectReason) { @@ -700,7 +628,7 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo return bufPos; - exit_error: +exit_error: if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: error parsing reject PDU\n"); @@ -997,8 +925,6 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } removeFromOutstandingCalls(self, outstandingCall->invokeId); - - releaseResponse(self); } static void @@ -1007,7 +933,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) MmsConnection self = (MmsConnection) parameter; if (DEBUG_MMS_CLIENT) - printf("MMS_CLIENT: mmsIsoCallback called with indication %i\n", indication); + if (indication != ISO_IND_TICK) + printf("MMS_CLIENT: mmsIsoCallback called with indication %i\n", indication); if (indication == ISO_IND_TICK) { @@ -1034,14 +961,21 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) Semaphore_post(self->outstandingCallsLock); + if (self->concludeHandler) { + if (currentTime > self->concludeTimeout) { + self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_SERVICE_TIMEOUT, false); + self->concludeHandler = NULL; + } + } + return; } if (indication == ISO_IND_CLOSED) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: Connection lost or closed by client!\n"); - setConnectionState(self, MMS_CON_IDLE); - setAssociationState(self, MMS_STATE_CLOSED); + + setConnectionState(self, MMS_CONNECTION_STATE_CLOSED); /* Call user provided callback function */ if (self->connectionLostHandler != NULL) @@ -1053,14 +987,13 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (indication == ISO_IND_ASSOCIATION_FAILED) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: association failed!\n"); - setConnectionState(self, MMS_CON_ASSOCIATION_FAILED); - setAssociationState(self, MMS_STATE_CLOSED); + + setConnectionState(self, MMS_CONNECTION_STATE_CLOSING); return; } if (payload != NULL) { if (ByteBuffer_getSize(payload) < 1) { - IsoClientConnection_releaseReceiveBuffer(self->isoClient); return; } } @@ -1081,53 +1014,58 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (tag == 0xa9) { /* initiate response PDU */ - if (indication == ISO_IND_ASSOCIATION_SUCCESS) - setConnectionState(self, MMS_CON_ASSOCIATED); - else - setConnectionState(self, MMS_CON_ASSOCIATION_FAILED); + if (indication == ISO_IND_ASSOCIATION_SUCCESS) { - self->lastResponse = payload; + if (mmsClient_parseInitiateResponse(self, payload)) { + setConnectionState(self, MMS_CONNECTION_STATE_CONNECTED); + } + else { + setConnectionState(self, MMS_CONNECTION_STATE_CLOSING); + IsoClientConnection_close(self->isoClient); + } + } + else { + setConnectionState(self, MMS_CONNECTION_STATE_CLOSING); - IsoClientConnection_releaseReceiveBuffer(self->isoClient); + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: Failed to parse initiate response!\n"); + } } else if (tag == 0xa3) { /* unconfirmed PDU */ handleUnconfirmedMmsPdu(self, payload); - IsoClientConnection_releaseReceiveBuffer(self->isoClient); } else if (tag == 0x8b) { /* conclude request PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.request\n"); - setConcludeState(self, CONCLUDE_STATE_REQUESTED); - /* TODO block all new user requests? */ - - IsoClientConnection_releaseReceiveBuffer(self->isoClient); } else if (tag == 0x8c) { /* conclude response PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.reponse+\n"); - setConcludeState(self, CONCLUDE_STATE_ACCEPTED); + if (self->concludeHandler) { + self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_NONE, true); + self->concludeHandler = NULL; + } IsoClientConnection_release(self->isoClient); - - IsoClientConnection_releaseReceiveBuffer(self->isoClient); } else if (tag == 0x8d) { /* conclude error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.reponse-\n"); - setConcludeState(self, CONCLUDE_STATE_REJECTED); - - IsoClientConnection_releaseReceiveBuffer(self->isoClient); + if (self->concludeHandler) { + self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_CONCLUDE_REJECTED, false); + self->concludeHandler = NULL; + } } else if (tag == 0xa2) { /* confirmed error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: Confirmed error PDU!\n"); + uint32_t invokeId; - MmsServiceError serviceError = - { 0, 0 }; + MmsServiceError serviceError = { 0, 0 }; if (mmsMsg_parseConfirmedErrorPDU(payload->buffer, 0, payload->size, &invokeId, &serviceError) < 0) { if (DEBUG_MMS_CLIENT) @@ -1147,19 +1085,14 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) handleAsyncResponse(self, NULL, 0, call, err); } else { - /* wait for application thread to handle last received response */ - waitUntilLastResponseHasBeenProcessed(self); - - Semaphore_wait(self->lastResponseLock); - self->lastResponseError = err; - self->responseInvokeId = invokeId; - Semaphore_post(self->lastResponseLock); + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: internal problem (unexpected call type - error PDU)\n"); } } else { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); - IsoClientConnection_releaseReceiveBuffer(self->isoClient); + return; } } @@ -1187,17 +1120,12 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) handleAsyncResponse(self, NULL, 0, call, err); } else { - /* wait for application thread to handle last received response */ - waitUntilLastResponseHasBeenProcessed(self); - Semaphore_wait(self->lastResponseLock); - self->lastResponseError = err; - self->responseInvokeId = invokeId; - Semaphore_post(self->lastResponseLock); + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: internal problem (unexpected call type - reject PDU)\n"); } } else { - IsoClientConnection_releaseReceiveBuffer(self->isoClient); return; } } @@ -1237,19 +1165,14 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) handleAsyncResponse(self, payload, bufPos, call, MMS_ERROR_NONE); } else { - waitUntilLastResponseHasBeenProcessed(self); - - Semaphore_wait(self->lastResponseLock); - self->lastResponse = payload; - self->lastResponseBufPos = bufPos; - self->responseInvokeId = invokeId; - Semaphore_post(self->lastResponseLock); + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: internal problem (unexpected call type - confirmed response PDU)\n"); } } else { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); - IsoClientConnection_releaseReceiveBuffer(self->isoClient); + return; } } @@ -1303,8 +1226,6 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) mmsClient_handleFileOpenRequest(self, buf, bufPos, bufPos + length, invokeId, response); IsoClientConnection_sendMessage(self->isoClient, response); - - IsoClientConnection_releaseReceiveBuffer(self->isoClient); } break; @@ -1318,8 +1239,6 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) mmsClient_handleFileReadRequest(self, buf, bufPos, bufPos + length, invokeId, response); IsoClientConnection_sendMessage(self->isoClient, response); - - IsoClientConnection_releaseReceiveBuffer(self->isoClient); } break; @@ -1333,8 +1252,6 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) mmsClient_handleFileCloseRequest(self, buf, bufPos, bufPos + length, invokeId, response); IsoClientConnection_sendMessage(self->isoClient, response); - - IsoClientConnection_releaseReceiveBuffer(self->isoClient); } break; #endif /* MMS_FILE_SERVICE == 1 */ @@ -1344,8 +1261,6 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); - IsoClientConnection_releaseReceiveBuffer(self->isoClient); - break; } } @@ -1356,7 +1271,9 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) invokeId = BerDecoder_decodeUint32(buf, length, bufPos); if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received request with invokeId: %i\n", invokeId); + self->lastInvokeId = invokeId; + break; default: @@ -1364,8 +1281,6 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); - IsoClientConnection_releaseReceiveBuffer(self->isoClient); - goto exit_with_error; break; @@ -1377,89 +1292,139 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ + else { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: unknown message type\n"); + + goto exit_with_error; + } if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: LEAVE mmsIsoCallback - OK\n"); return; - exit_with_error: +exit_with_error: if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received malformed message from server!\n"); - IsoClientConnection_releaseReceiveBuffer(self->isoClient); - - if (DEBUG_MMS_CLIENT) - printf("MMS_CLIENT: LEAVE mmsIsoCallback - NOT OK!\n"); return; } -MmsConnection -MmsConnection_create() +static void* +connectionHandlingThread(void* parameter) { - MmsConnection self = (MmsConnection) GLOBAL_CALLOC(1, sizeof(struct sMmsConnection)); + MmsConnection self = (MmsConnection) parameter; - self->parameters.dataStructureNestingLevel = -1; - self->parameters.maxServOutstandingCalled = -1; - self->parameters.maxServOutstandingCalling = -1; - self->parameters.maxPduSize = -1; + while (self->connectionThreadRunning) { + if (MmsConnection_tick(self)) + Thread_sleep(10); + } - self->parameters.maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; + return NULL; +} - self->requestTimeout = CONFIG_MMS_CONNECTION_DEFAULT_TIMEOUT; - self->lastInvokeIdLock = Semaphore_create(1); - self->lastResponseLock = Semaphore_create(1); - self->outstandingCallsLock = Semaphore_create(1); +static MmsConnection +MmsConnection_createInternal(TLSConfiguration tlsConfig, bool createThread) +{ +#if (CONFIG_MMS_THREADLESS_STACK == 1) + if (createThread) + return NULL; +#endif - self->connectionStateLock = Semaphore_create(1); - self->concludeStateLock = Semaphore_create(1); - self->associationStateLock = Semaphore_create(1); + MmsConnection self = (MmsConnection) GLOBAL_CALLOC(1, sizeof(struct sMmsConnection)); - self->lastResponseError = MMS_ERROR_NONE; + if (self) { - self->outstandingCalls = (MmsOutstandingCall) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(struct sMmsOutstandingCall)); + self->parameters.dataStructureNestingLevel = -1; + self->parameters.maxServOutstandingCalled = -1; + self->parameters.maxServOutstandingCalling = -1; + self->parameters.maxPduSize = -1; - self->isoParameters = IsoConnectionParameters_create(); + self->parameters.maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; - /* Load default values for connection parameters */ - TSelector tSelector = { 2, { 0, 1 } }; - SSelector sSelector = { 2, { 0, 1 } }; + self->requestTimeout = CONFIG_MMS_CONNECTION_DEFAULT_TIMEOUT; - IsoConnectionParameters_setLocalAddresses(self->isoParameters, 1, sSelector, tSelector); - IsoConnectionParameters_setLocalApTitle(self->isoParameters, "1.1.1.999", 12); - IsoConnectionParameters_setRemoteAddresses(self->isoParameters, 1, sSelector, tSelector); - IsoConnectionParameters_setRemoteApTitle(self->isoParameters, "1.1.1.999.1", 12); + self->lastInvokeIdLock = Semaphore_create(1); + self->outstandingCallsLock = Semaphore_create(1); - self->connectTimeout = CONFIG_MMS_CONNECTION_DEFAULT_CONNECT_TIMEOUT; + self->associationStateLock = Semaphore_create(1); + self->connectionState = MMS_CONNECTION_STATE_CLOSED; - self->isoClient = IsoClientConnection_create((IsoIndicationCallback) mmsIsoCallback, (void*) self); + self->concludeHandler = NULL; + self->concludeHandlerParameter = NULL; + self->concludeTimeout = 0; - return self; -} + self->outstandingCalls = (MmsOutstandingCall) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(struct sMmsOutstandingCall)); -MmsConnection -MmsConnection_createSecure(TLSConfiguration tlsConfig) -{ - MmsConnection self = MmsConnection_create(); + self->isoParameters = IsoConnectionParameters_create(); + + /* Load default values for connection parameters */ + TSelector tSelector = { 2, { 0, 1 } }; + SSelector sSelector = { 2, { 0, 1 } }; + + IsoConnectionParameters_setLocalAddresses(self->isoParameters, 1, sSelector, tSelector); + IsoConnectionParameters_setLocalApTitle(self->isoParameters, "1.1.1.999", 12); + IsoConnectionParameters_setRemoteAddresses(self->isoParameters, 1, sSelector, tSelector); + IsoConnectionParameters_setRemoteApTitle(self->isoParameters, "1.1.1.999.1", 12); + + self->connectTimeout = CONFIG_MMS_CONNECTION_DEFAULT_CONNECT_TIMEOUT; + + self->isoClient = IsoClientConnection_create(self->isoParameters, (IsoIndicationCallback) mmsIsoCallback, (void*) self); #if (CONFIG_MMS_SUPPORT_TLS == 1) - if (self != NULL) { - IsoConnectionParameters connectionParameters = MmsConnection_getIsoConnectionParameters(self); + if (tlsConfig) { + TLSConfiguration_setClientMode(tlsConfig); - TLSConfiguration_setClientMode(tlsConfig); + IsoConnectionParameters_setTlsConfiguration(self->isoParameters, tlsConfig); + } +#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ - IsoConnectionParameters_setTlsConfiguration(connectionParameters, tlsConfig); +#if (CONFIG_MMS_THREADLESS_STACK == 0) + self->createThread = createThread; + self->connectionHandlingThread = NULL; + self->connectionThreadRunning = false; +#endif } -#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ return self; } +MmsConnection +MmsConnection_create() +{ + return MmsConnection_createInternal(NULL, true); +} + +MmsConnection +MmsConnection_createSecure(TLSConfiguration tlsConfig) +{ + return MmsConnection_createInternal(tlsConfig, true); +} + +MmsConnection +MmsConnection_createNonThreaded(TLSConfiguration tlsConfig) +{ + return MmsConnection_createInternal(tlsConfig, false); +} + void MmsConnection_destroy(MmsConnection self) { +#if (CONFIG_MMS_THREADLESS_STACK == 0) + if (self->createThread) { + if (self->connectionHandlingThread) { + if (self->connectionThreadRunning) { + self->connectionThreadRunning = false; + Thread_destroy(self->connectionHandlingThread); + self->connectionHandlingThread = NULL; + } + } + } +#endif + if (self->isoClient != NULL) IsoClientConnection_destroy(self->isoClient); @@ -1467,12 +1432,10 @@ MmsConnection_destroy(MmsConnection self) IsoConnectionParameters_destroy(self->isoParameters); Semaphore_destroy(self->lastInvokeIdLock); - Semaphore_destroy(self->lastResponseLock); + Semaphore_destroy(self->outstandingCallsLock); Semaphore_destroy(self->associationStateLock); - Semaphore_destroy(self->connectionStateLock); - Semaphore_destroy(self->concludeStateLock); GLOBAL_FREEMEM(self->outstandingCalls); @@ -1536,6 +1499,13 @@ MmsConnection_setConnectionLostHandler(MmsConnection self, MmsConnectionLostHand self->connectionLostHandlerParameter = handlerParameter; } +void +MmsConnection_setConnectionStateChangedHandler(MmsConnection self, MmsConnectionStateChangedHandler handler, void* parameter) +{ + self->stateChangedHandler = handler; + self->stateChangedHandlerParameter = parameter; +} + void MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs) { @@ -1572,25 +1542,82 @@ MmsConnection_getMmsConnectionParameters(MmsConnection self) return self->parameters; } -static void -waitForConnectResponse(MmsConnection self) +struct connectParameters { - uint64_t currentTime = Hal_getTimeInMs(); - - uint64_t waitUntilTime = currentTime + self->requestTimeout; + Semaphore sem; + MmsConnectionState state; + MmsConnectionStateChangedHandler originalHandler; + void* originalParameter; +}; - while (currentTime < waitUntilTime) { - if (getConnectionState(self) != MMS_CON_WAITING) - break; +static void +internalConnectionStateChangedHandler (MmsConnection connection, void* parameter, MmsConnectionState newState) +{ + struct connectParameters* conParams = (struct connectParameters*) parameter; - Thread_sleep(10); + if ((newState == MMS_CONNECTION_STATE_CLOSED) || (newState == MMS_CONNECTION_STATE_CONNECTED)) + { + conParams->state = newState; - currentTime = Hal_getTimeInMs(); + /* unblock user thread */ + Semaphore_post(conParams->sem); + } + else { + if (conParams->originalHandler) + conParams->originalHandler(connection, conParams->originalParameter, newState); } } bool MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort) +{ + bool success = false; + + struct connectParameters conParams; + + conParams.sem = Semaphore_create(1); + conParams.state = MMS_CONNECTION_STATE_CONNECTING; + conParams.originalHandler = self->stateChangedHandler; + conParams.originalParameter = self->stateChangedHandlerParameter; + + Semaphore_wait(conParams.sem); + + self->stateChangedHandler = internalConnectionStateChangedHandler; + self->stateChangedHandlerParameter = &conParams; + + MmsError err; + + MmsConnection_connectAsync(self, &err, serverName, serverPort); + + if (err == MMS_ERROR_NONE) { + Semaphore_wait(conParams.sem); + + if (conParams.state == MMS_CONNECTION_STATE_CONNECTED) { + *mmsError = MMS_ERROR_NONE; + success = true; + } + else { + *mmsError = MMS_ERROR_CONNECTION_REJECTED; + } + + if (conParams.originalHandler) + conParams.originalHandler(self, conParams.originalParameter, conParams.state); + + } + else { + *mmsError = err; + } + + Semaphore_destroy(conParams.sem); + + self->stateChangedHandler = conParams.originalHandler; + self->stateChangedHandlerParameter = conParams.originalParameter; + + return success; +} + +void +MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort) { if (serverPort == -1) { #if (CONFIG_MMS_SUPPORT_TLS == 1) @@ -1603,6 +1630,17 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server #endif } +#if (CONFIG_MMS_THREADLESS_STACK == 0) + if (self->createThread) { + if (self->connectionHandlingThread == NULL) { + + self->connectionHandlingThread = Thread_create(connectionHandlingThread, self, false); + self->connectionThreadRunning = true; + Thread_start(self->connectionHandlingThread); + } + } +#endif + IsoConnectionParameters_setTcpParameters(self->isoParameters, serverName, serverPort); if (self->parameters.maxPduSize == -1) @@ -1619,39 +1657,18 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server } #endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ - setConnectionState(self, MMS_CON_WAITING); - - IsoClientConnection_associate(self->isoClient, self->isoParameters, payload, - self->connectTimeout); - - waitForConnectResponse(self); - - if (DEBUG_MMS_CLIENT) - printf("MmsConnection_connect: received response conState: %i\n", getConnectionState(self)); - - if (getConnectionState(self) == MMS_CON_ASSOCIATED) { - mmsClient_parseInitiateResponse(self); - - releaseResponse(self); - - setAssociationState(self, MMS_STATE_CONNECTED); + if (IsoClientConnection_associateAsync(self->isoClient, self->connectTimeout)) { + setConnectionState(self, MMS_CONNECTION_STATE_CONNECTING); + *mmsError = MMS_ERROR_NONE; } else - setAssociationState(self, MMS_STATE_CLOSED); - - setConnectionState(self, MMS_CON_IDLE); - - if (DEBUG_MMS_CLIENT) - printf("MmsConnection_connect: states: con %i ass %i\n", getConnectionState(self), getAssociationState(self)); + *mmsError = MMS_ERROR_OTHER; +} - if (getAssociationState(self) == MMS_STATE_CONNECTED) { - *mmsError = MMS_ERROR_NONE; - return true; - } - else { - *mmsError = MMS_ERROR_CONNECTION_REJECTED; - return false; - } +bool +MmsConnection_tick(MmsConnection self) +{ + return IsoClientConnection_handleConnection(self->isoClient); } void @@ -1659,10 +1676,24 @@ MmsConnection_close(MmsConnection self) { self->connectionLostHandler = NULL; - if (getAssociationState(self) == MMS_STATE_CONNECTED) + if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) IsoClientConnection_close(self->isoClient); } +void +MmsConnection_abortAsync(MmsConnection self, MmsError* mmsError) +{ + self->connectionLostHandler = NULL; + + if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) { + IsoClientConnection_abortAsync(self->isoClient); + *mmsError = MMS_ERROR_NONE; + } + else { + *mmsError = MMS_ERROR_CONNECTION_LOST; + } +} + void MmsConnection_abort(MmsConnection self, MmsError* mmsError) { @@ -1670,90 +1701,95 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError) self->connectionLostHandler = NULL; - bool success = true; + bool success = false; - if (getAssociationState(self) == MMS_STATE_CONNECTED) - success = IsoClientConnection_abort(self->isoClient); + if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) { + IsoClientConnection_abortAsync(self->isoClient); + + uint64_t timeout = Hal_getTimeInMs() + self->requestTimeout; + + while (Hal_getTimeInMs() < timeout) { + if (getConnectionState(self) == MMS_CONNECTION_STATE_CLOSED) { + success = true; + break; + } + } + + } + if (success == false) { - IsoClientConnection_close(self->isoClient); *mmsError = MMS_ERROR_SERVICE_TIMEOUT; } + + IsoClientConnection_close(self->isoClient); } -static void -sendConcludeRequestAndWaitForResponse(MmsConnection self) +struct concludeParameters { - uint64_t startTime = Hal_getTimeInMs(); - - uint64_t waitUntilTime = startTime + self->requestTimeout; - - uint64_t currentTime = startTime; + Semaphore sem; + MmsError err; + bool success; +}; - bool success = false; +static void +concludeHandler(void* parameter, MmsError mmsError, bool success) +{ + struct concludeParameters* parameters = (struct concludeParameters*) parameter; - ByteBuffer* concludeMessage = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + parameters->err = mmsError; + parameters->success = success; - mmsClient_createConcludeRequest(self, concludeMessage); + /* unblock user thread */ + Semaphore_post(parameters->sem); +} - setConcludeState(self, CONCLUDE_STATE_REQUESTED); - IsoClientConnection_sendMessage(self->isoClient, concludeMessage); +void +MmsConnection_conclude(MmsConnection self, MmsError* mmsError) +{ + MmsError err = MMS_ERROR_NONE; - while (currentTime < waitUntilTime) { + struct concludeParameters parameter; - if (getAssociationState(self) == MMS_STATE_CLOSED) - goto exit_function; + parameter.sem = Semaphore_create(1);; + parameter.success = false; + parameter.err = MMS_ERROR_NONE; - if (getConcludeState(self) != CONCLUDE_STATE_REQUESTED) { - success = true; - break; - } + Semaphore_wait(parameter.sem); - Thread_sleep(1); + MmsConnection_concludeAsync(self, &err, concludeHandler, ¶meter); - currentTime = Hal_getTimeInMs(); + if (err == MMS_ERROR_NONE) { + Semaphore_wait(parameter.sem); + err = parameter.err; } - if (!success) { - if (DEBUG_MMS_CLIENT) - printf("TIMEOUT for conclude request\n"); - self->lastResponseError = MMS_ERROR_SERVICE_TIMEOUT; - } + Semaphore_destroy(parameter.sem); - exit_function: - return; + if (mmsError) + *mmsError = err; } void -MmsConnection_conclude(MmsConnection self, MmsError* mmsError) +MmsConnection_concludeAsync(MmsConnection self, MmsError* mmsError, MmsConnection_ConcludeAbortHandler handler, void* parameter) { - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } - *mmsError = MMS_ERROR_NONE; - - sendConcludeRequestAndWaitForResponse(self); - - if (self->lastResponseError != MMS_ERROR_NONE) - *mmsError = self->lastResponseError; - - releaseResponse(self); - - if (getConcludeState(self) != CONCLUDE_STATE_ACCEPTED) { + ByteBuffer* concludeMessage = IsoClientConnection_allocateTransmitBuffer(self->isoClient); - if (getAssociationState(self) == MMS_STATE_CLOSED) - *mmsError = MMS_ERROR_CONNECTION_LOST; + mmsClient_createConcludeRequest(self, concludeMessage); - if (getConcludeState(self) == CONCLUDE_STATE_REJECTED) - *mmsError = MMS_ERROR_CONCLUDE_REJECTED; - } + self->concludeHandler = handler; + self->concludeHandlerParameter = parameter; + self->concludeTimeout = Hal_getTimeInMs() + self->requestTimeout; - self->connectionLostHandler = NULL; + IsoClientConnection_sendMessage(self->isoClient, concludeMessage); - exit_function: +exit_function: return; } @@ -1779,7 +1815,7 @@ mmsClient_getNameListSingleRequestAsync( { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2003,9 +2039,10 @@ MmsConnection_readVariableAsync(MmsConnection self, MmsError* mmsError, const ch { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; } @@ -2097,7 +2134,7 @@ MmsConnection_readArrayElementsAsync(MmsConnection self, MmsError* mmsError, con { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2160,7 +2197,7 @@ MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, MmsEr { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2222,7 +2259,7 @@ MmsConnection_readMultipleVariablesAsync(MmsConnection self, MmsError* mmsError, { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2285,7 +2322,7 @@ MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, MmsError* mms { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2351,7 +2388,7 @@ MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2437,7 +2474,7 @@ MmsConnection_readNamedVariableListDirectoryAsync(MmsConnection self, MmsError* { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2502,7 +2539,7 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(MmsConnecti { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2577,7 +2614,7 @@ MmsConnection_defineNamedVariableListAsync(MmsConnection self, MmsError* mmsErro { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2633,7 +2670,7 @@ MmsConnection_defineNamedVariableListAssociationSpecificAsync(MmsConnection self { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2692,7 +2729,7 @@ MmsConnection_deleteNamedVariableListAsync(MmsConnection self, MmsError* mmsErro { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2749,7 +2786,7 @@ MmsConnection_deleteAssociationSpecificNamedVariableListAsync(MmsConnection self { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2827,7 +2864,7 @@ MmsConnection_getVariableAccessAttributesAsync(MmsConnection self, MmsError* mms { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2909,7 +2946,7 @@ MmsConnection_identifyAsync(MmsConnection self, MmsError* mmsError, { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2992,7 +3029,7 @@ MmsConnection_getServerStatusAsync(MmsConnection self, MmsError* mmsError, bool { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3127,7 +3164,7 @@ MmsConnection_readJournalTimeRangeAsync(MmsConnection self, MmsError* mmsError, { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3195,7 +3232,7 @@ MmsConnection_readJournalStartAfterAsync(MmsConnection self, MmsError* mmsError, { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3301,7 +3338,7 @@ MmsConnection_fileOpenAsync(MmsConnection self, MmsError* mmsError, const char* #if (MMS_FILE_SERVICE == 1) uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3390,7 +3427,7 @@ MmsConnection_fileCloseAsync(MmsConnection self, MmsError* mmsError, uint32_t fr #if (MMS_FILE_SERVICE == 1) uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3461,7 +3498,7 @@ MmsConnection_fileDeleteAsync(MmsConnection self, MmsError* mmsError, const char #if (MMS_FILE_SERVICE == 1) uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3565,7 +3602,7 @@ MmsConnection_fileReadAsync(MmsConnection self, MmsError* mmsError, int32_t frsm #if (MMS_FILE_SERVICE == 1) uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3669,7 +3706,7 @@ MmsConnection_getFileDirectoryAsync(MmsConnection self, MmsError* mmsError, cons #if (MMS_FILE_SERVICE == 1) uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3739,7 +3776,7 @@ MmsConnection_fileRenameAsync(MmsConnection self, MmsError* mmsError, const char #if (MMS_FILE_SERVICE == 1) uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3817,7 +3854,7 @@ MmsConnection_obtainFileAsync(MmsConnection self, MmsError* mmsError, const char #if (MMS_FILE_SERVICE == 1) uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3895,7 +3932,7 @@ MmsConnection_writeVariableAsync(MmsConnection self, MmsError* mmsError, { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3982,7 +4019,7 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, MmsError* mmsError { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4043,7 +4080,7 @@ MmsConnection_writeArrayElementsAsync(MmsConnection self, MmsError* mmsError, { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4110,7 +4147,7 @@ MmsConnection_writeNamedVariableListAsync(MmsConnection self, MmsError* mmsError { uint32_t invokeId = 0; - if (getAssociationState(self) != MMS_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; diff --git a/src/mms/iso_mms/client/mms_client_initiate.c b/src/mms/iso_mms/client/mms_client_initiate.c index 10810ec5..2721f5a8 100644 --- a/src/mms/iso_mms/client/mms_client_initiate.c +++ b/src/mms/iso_mms/client/mms_client_initiate.c @@ -160,10 +160,8 @@ parseInitResponseDetail(MmsConnection self, uint8_t* buffer, int bufPos, int max } bool -mmsClient_parseInitiateResponse(MmsConnection self) +mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response) { - bool result = false; - self->parameters.maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; self->parameters.dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL; self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; @@ -171,8 +169,8 @@ mmsClient_parseInitiateResponse(MmsConnection self) int bufPos = 1; /* ignore tag - already checked */ - int maxBufPos = ByteBuffer_getSize(self->lastResponse); - uint8_t* buffer = ByteBuffer_getBuffer(self->lastResponse); + int maxBufPos = ByteBuffer_getSize(response); + uint8_t* buffer = ByteBuffer_getBuffer(response); int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); @@ -237,5 +235,5 @@ mmsClient_parseInitiateResponse(MmsConnection self) } - return result; + return true; }