/*
* client_connection.c
*
* Copyright 2013-2025 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see .
*
* See COPYING file for the complete license text.
*/
#include "iec61850_server.h"
#include "iso_server.h"
#include "mms_mapping.h"
#include "control.h"
#include "stack_config.h"
#include "hal_thread.h"
#include "ied_server_private.h"
#include "libiec61850_platform_includes.h"
#if __STDC_VERSION__ >= 201112L
#include
#else
#define _TLS_OWN_CNT_SEM 1
#endif
struct sClientConnection
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore accessMutex;
#endif
int tasksCount; /* protected by accessMutex */
void* serverConnectionHandle; /* protected by accessMutex */
#ifdef _TLS_OWN_CNT_SEM
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore ownerCountMutex;
#endif /*#if (CONFIG_MMS_THREADLESS_STACK != 1) */
int ownerCount;
#else
_Atomic(int) ownerCount;
#endif /* _TLS_OWN_CNT_SEM */
};
ClientConnection
private_ClientConnection_create(void* serverConnectionHandle)
{
ClientConnection self = (ClientConnection) GLOBAL_MALLOC(sizeof(struct sClientConnection));
if (self)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->accessMutex = Semaphore_create(1);
#ifdef _TLS_OWN_CNT_SEM
self->ownerCount = Semaphore_create(1);
#endif
#endif
self->ownerCount = 1;
self->tasksCount = 0;
self->serverConnectionHandle = serverConnectionHandle;
}
return self;
}
static void
private_ClientConnection_destroy(ClientConnection self)
{
if (self)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->accessMutex);
#ifdef _TLS_OWN_CNT_SEM
Semaphore_destroy(self->ownerCountMutex);
#endif
#endif
GLOBAL_FREEMEM(self);
}
}
int
private_ClientConnection_getTasksCount(ClientConnection self)
{
int tasksCount;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
tasksCount = self->tasksCount;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
return tasksCount;
}
void
private_ClientConnection_increaseTasksCount(ClientConnection self)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
self->tasksCount++;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
}
void
private_ClientConnection_decreaseTasksCount(ClientConnection self)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
self->tasksCount--;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
}
void*
private_ClientConnection_getServerConnectionHandle(ClientConnection self)
{
void* handle = NULL;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
handle = self->serverConnectionHandle;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
return handle;
}
const char*
ClientConnection_getPeerAddress(ClientConnection self)
{
char* peerAddress = NULL;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
if (self->serverConnectionHandle)
{
MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle;
peerAddress = MmsServerConnection_getClientAddress(mmsConnection);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
return peerAddress;
}
const char*
ClientConnection_getLocalAddress(ClientConnection self)
{
char* localAddress = NULL;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
if (self->serverConnectionHandle)
{
MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle;
localAddress = MmsServerConnection_getLocalAddress(mmsConnection);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
return localAddress;
}
void*
ClientConnection_getSecurityToken(ClientConnection self)
{
void* secToken = NULL;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
if (self->serverConnectionHandle)
{
MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle;
secToken = MmsServerConnection_getSecurityToken(mmsConnection);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
return secToken;
}
bool
ClientConnection_abort(ClientConnection self)
{
bool aborted = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->accessMutex);
#endif
if (self->serverConnectionHandle)
{
MmsServerConnection mmsConnection = (MmsServerConnection) self->serverConnectionHandle;
if (mmsConnection)
{
MmsServer mmsServer = MmsServerConnection_getServer(mmsConnection);
aborted = MmsServer_abortConnection(mmsServer, mmsConnection);
if (aborted)
{
/* remove reference to underlying connection. Instance cannot be used any longer */
self->serverConnectionHandle = NULL;
}
}
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->accessMutex);
#endif
return aborted;
}
ClientConnection
ClientConnection_claimOwnership(ClientConnection self)
{
#ifdef _TLS_OWN_CNT_SEM
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->ownerCountMutex);
#endif
self->ownerCount++;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->ownerCountMutex);
#endif
#else
atomic_fetch_add(&(self->ownerCount), 1);
#endif
return self;
}
void
ClientConnection_release(ClientConnection self)
{
if (self)
{
#ifdef _TLS_OWN_CNT_SEM
int cnt;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->ownerCountMutex);
#endif
cnt = self->ownerCount;
self->ownerCount--;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->ownerCountMutex);
#endif
if (cnt == 1) {
private_ClientConnection_destroy(self);
}
#else
if (atomic_fetch_sub(&(self->ownerCount), 1) == 1)
{
private_ClientConnection_destroy(self);
}
#endif /* #ifdef _TLS_OWN_CNT_SEM */
}
}