479 lines
15 KiB
C
479 lines
15 KiB
C
//
|
|
// --------------------------------------------------------------------------
|
|
// Gurux Ltd
|
|
//
|
|
//
|
|
//
|
|
// Filename: $HeadURL$
|
|
//
|
|
// Version: $Revision$,
|
|
// $Date$
|
|
// $Author$
|
|
//
|
|
// Copyright (c) Gurux Ltd
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This file is a part of Gurux Device Framework.
|
|
//
|
|
// Gurux Device Framework is Open Source 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; version 2 of the License.
|
|
// Gurux Device Framework 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.
|
|
//
|
|
// This code is licensed under the GNU General Public License v2.
|
|
// Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "gxmem.h"
|
|
#include "dlmssettings.h"
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(__linux__)
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
// Server sender frame sequence starting number.
|
|
static const unsigned char SERVER_START_SENDER_FRAME_SEQUENCE = 0x1E;
|
|
// Server receiver frame sequence starting number.
|
|
static const unsigned char SERVER_START_RECEIVER_FRAME_SEQUENCE = 0xFE;
|
|
// Client sender frame sequence starting number.
|
|
static const unsigned char CLIENT_START_SENDER_FRAME_SEQUENCE = 0xFE;
|
|
// Client receiver frame sequence starting number.
|
|
static const unsigned char CLIENT_START_RCEIVER_FRAME_SEQUENCE = 0xE;
|
|
|
|
//Initialize server.
|
|
void svr_init(
|
|
dlmsServerSettings* settings,
|
|
unsigned char useLogicalNameReferencing,
|
|
DLMS_INTERFACE_TYPE interfaceType,
|
|
uint16_t frameSize,
|
|
uint16_t pduSize,
|
|
unsigned char* frameBuffer,
|
|
uint16_t frameBufferSize,
|
|
unsigned char* pduBuffer,
|
|
uint16_t pduBufferSize)
|
|
{
|
|
cl_init(&settings->base, useLogicalNameReferencing, 0, 0, DLMS_AUTHENTICATION_NONE, NULL, interfaceType);
|
|
settings->base.proposedConformance |= DLMS_CONFORMANCE_GENERAL_PROTECTION;
|
|
bb_attach(&settings->receivedData, frameBuffer, 0, frameBufferSize);
|
|
reply_init(&settings->info);
|
|
bb_attach(&settings->info.data, pduBuffer, 0, pduBufferSize);
|
|
settings->base.maxInfoRX = settings->base.maxInfoTX = frameSize;
|
|
settings->base.maxServerPDUSize = pduSize;
|
|
settings->base.server = 1;
|
|
settings->initialized = 0;
|
|
trans_init(&settings->transaction);
|
|
#ifndef DLMS_IGNORE_TCP_UDP_SETUP
|
|
settings->wrapper = NULL;
|
|
#endif // DLMS_IGNORE_TCP_UDP_SETUP
|
|
#ifndef DLMS_IGNORE_IEC_HDLC_SETUP
|
|
settings->hdlc = NULL;
|
|
#endif //DLMS_IGNORE_IEC_HDLC_SETUP
|
|
#ifndef DLMS_IGNORE_IEC_LOCAL_PORT_SETUP
|
|
settings->localPortSetup = NULL;
|
|
#endif //DLMS_IGNORE_IEC_LOCAL_PORT_SETUP
|
|
#ifndef DLMS_IGNORE_CLOCK
|
|
settings->defaultClock = NULL;
|
|
#endif //DLMS_IGNORE_CLOCK
|
|
settings->dataReceived = 0;
|
|
settings->frameReceived = 0;
|
|
resetFrameSequence(&settings->base);
|
|
settings->pushClientAddress = 0;
|
|
}
|
|
|
|
//Initialize client.
|
|
void cl_init(
|
|
dlmsSettings* settings,
|
|
unsigned char useLogicalNameReferencing,
|
|
uint16_t clientAddress,
|
|
uint32_t serverAddress,
|
|
DLMS_AUTHENTICATION authentication,
|
|
const char* password,
|
|
DLMS_INTERFACE_TYPE interfaceType)
|
|
{
|
|
settings->autoIncreaseInvokeID = 0;
|
|
settings->qualityOfService = 0;
|
|
settings->protocolVersion = 0;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
settings->preEstablishedSystemTitle = NULL;
|
|
#else
|
|
memset(settings->preEstablishedSystemTitle, 0, 8);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
settings->blockIndex = 1;
|
|
settings->clientAddress = clientAddress;
|
|
settings->serverAddress = serverAddress;
|
|
settings->dlmsVersionNumber = 6;
|
|
settings->useLogicalNameReferencing = useLogicalNameReferencing;
|
|
settings->interfaceType = interfaceType;
|
|
settings->authentication = authentication;
|
|
BYTE_BUFFER_INIT(&settings->password);
|
|
bb_addString(&settings->password, password);
|
|
memset(settings->sourceSystemTitle, 0, sizeof(settings->sourceSystemTitle));
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
memset(settings->kek, 0, sizeof(settings->kek));
|
|
#else
|
|
BYTE_BUFFER_INIT(&settings->kek);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
settings->maxServerPDUSize = 1024;
|
|
settings->maxPduSize = 0xFFFF;
|
|
settings->server = 0;
|
|
if (useLogicalNameReferencing)
|
|
{
|
|
settings->proposedConformance = (DLMS_CONFORMANCE)
|
|
(DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_ACTION |
|
|
DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_SET_OR_WRITE |
|
|
DLMS_CONFORMANCE_BLOCK_TRANSFER_WITH_GET_OR_READ |
|
|
DLMS_CONFORMANCE_SET |
|
|
DLMS_CONFORMANCE_SELECTIVE_ACCESS |
|
|
DLMS_CONFORMANCE_ACTION |
|
|
DLMS_CONFORMANCE_MULTIPLE_REFERENCES |
|
|
DLMS_CONFORMANCE_GET |
|
|
DLMS_CONFORMANCE_GENERAL_PROTECTION);
|
|
}
|
|
else
|
|
{
|
|
settings->proposedConformance = (DLMS_CONFORMANCE)(DLMS_CONFORMANCE_INFORMATION_REPORT |
|
|
DLMS_CONFORMANCE_READ | DLMS_CONFORMANCE_UN_CONFIRMED_WRITE |
|
|
DLMS_CONFORMANCE_WRITE | DLMS_CONFORMANCE_PARAMETERIZED_ACCESS |
|
|
DLMS_CONFORMANCE_MULTIPLE_REFERENCES);
|
|
}
|
|
settings->longInvokeID = 0;
|
|
settings->maxInfoTX = settings->maxInfoRX = 0x80;
|
|
settings->windowSizeTX = settings->windowSizeRX = 1;
|
|
settings->connected = DLMS_CONNECTION_STATE_NONE;
|
|
oa_init(&settings->objects);
|
|
settings->connected = DLMS_CONNECTION_STATE_NONE;
|
|
settings->customChallenges = 0;
|
|
settings->invokeID = 1;
|
|
BYTE_BUFFER_INIT(&settings->ctoSChallenge);
|
|
BYTE_BUFFER_INIT(&settings->stoCChallenge);
|
|
settings->priority = DLMS_PRIORITY_HIGH;
|
|
settings->serviceClass = DLMS_SERVICE_CLASS_CONFIRMED;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
cip_init(&settings->cipher);
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
settings->userId = -1;
|
|
resetFrameSequence(settings);
|
|
settings->serializedPdu = NULL;
|
|
oa_init(&settings->releasedObjects);
|
|
settings->expectedSecurityPolicy = 0xFF;
|
|
settings->expectedSecuritySuite = 0xFF;
|
|
settings->expectedInvocationCounter = NULL;
|
|
settings->expectedClientSystemTitle = NULL;
|
|
#ifndef DLMS_IGNORE_PLC
|
|
plc_reset(settings);
|
|
#endif //DLMS_IGNORE_PLC
|
|
oa_init(&settings->internalObjects);
|
|
}
|
|
|
|
void cl_clear(
|
|
dlmsSettings* settings)
|
|
{
|
|
settings->protocolVersion = 0;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
if (settings->preEstablishedSystemTitle != NULL)
|
|
{
|
|
bb_clear(settings->preEstablishedSystemTitle);
|
|
gxfree(settings->preEstablishedSystemTitle);
|
|
settings->preEstablishedSystemTitle = NULL;
|
|
}
|
|
#else
|
|
memset(settings->preEstablishedSystemTitle, 0, 8);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
memset(settings->sourceSystemTitle, 0, sizeof(settings->sourceSystemTitle));
|
|
bb_clear(&settings->password);
|
|
#ifdef DLMS_IGNORE_MALLOC
|
|
memset(settings->kek, 0, sizeof(settings->kek));
|
|
#else
|
|
bb_clear(&settings->kek);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
oa_clear(&settings->objects, !settings->server);
|
|
settings->connected = DLMS_CONNECTION_STATE_NONE;
|
|
settings->customChallenges = 0;
|
|
settings->invokeID = 1;
|
|
bb_clear(&settings->ctoSChallenge);
|
|
bb_clear(&settings->stoCChallenge);
|
|
settings->priority = DLMS_PRIORITY_HIGH;
|
|
settings->serviceClass = DLMS_SERVICE_CLASS_CONFIRMED;
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
cip_clear(&settings->cipher);
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
settings->maxPduSize = 0xFFFF;
|
|
settings->userId = -1;
|
|
oa_clear(&settings->releasedObjects, 1);
|
|
oa_clear(&settings->internalObjects, 0);
|
|
resetFrameSequence(settings);
|
|
settings->expectedInvocationCounter = NULL;
|
|
}
|
|
|
|
void svr_clear(
|
|
dlmsServerSettings* settings)
|
|
{
|
|
cl_clear(&settings->base);
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_PLC
|
|
void plc_reset(
|
|
dlmsSettings* settings)
|
|
{
|
|
settings->plcSettings.initialCredit = 7;
|
|
settings->plcSettings.currentCredit = 7;
|
|
settings->plcSettings.deltaCredit = 0;
|
|
//New device addresses are used.
|
|
if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC)
|
|
{
|
|
if (settings->server)
|
|
{
|
|
settings->plcSettings.macSourceAddress = DLMS_PLC_SOURCE_ADDRESS_NEW;
|
|
settings->plcSettings.macDestinationAddress = DLMS_PLC_SOURCE_ADDRESS_INITIATOR;
|
|
}
|
|
else
|
|
{
|
|
settings->plcSettings.macSourceAddress = DLMS_PLC_SOURCE_ADDRESS_INITIATOR;
|
|
settings->plcSettings.macDestinationAddress = DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL;
|
|
}
|
|
settings->plcSettings.allowedTimeSlots = 10;
|
|
}
|
|
else
|
|
{
|
|
if (settings->server)
|
|
{
|
|
settings->plcSettings.macSourceAddress = DLMS_PLC_SOURCE_ADDRESS_NEW;
|
|
settings->plcSettings.macDestinationAddress = DLMS_PLC_HDLC_SOURCE_ADDRESS_INITIATOR;
|
|
}
|
|
else
|
|
{
|
|
settings->plcSettings.macSourceAddress = DLMS_PLC_HDLC_SOURCE_ADDRESS_INITIATOR;
|
|
settings->plcSettings.macDestinationAddress = DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL;
|
|
}
|
|
settings->plcSettings.allowedTimeSlots = 0x14;
|
|
}
|
|
settings->plcSettings.responseProbability = 100;
|
|
}
|
|
#endif //DLMS_IGNORE_PLC
|
|
|
|
void resetBlockIndex(
|
|
dlmsSettings* settings)
|
|
{
|
|
settings->blockIndex = 1;
|
|
}
|
|
|
|
void resetFrameSequence(
|
|
dlmsSettings* settings)
|
|
{
|
|
if (settings->server)
|
|
{
|
|
settings->senderFrame = SERVER_START_SENDER_FRAME_SEQUENCE;
|
|
settings->receiverFrame = SERVER_START_RECEIVER_FRAME_SEQUENCE;
|
|
}
|
|
else
|
|
{
|
|
settings->senderFrame = CLIENT_START_SENDER_FRAME_SEQUENCE;
|
|
settings->receiverFrame = CLIENT_START_RCEIVER_FRAME_SEQUENCE;
|
|
}
|
|
}
|
|
|
|
unsigned char increaseReceiverSequence(
|
|
unsigned char value)
|
|
{
|
|
return ((value + 0x20) | 0x10 | (value & 0xE));
|
|
}
|
|
|
|
// Increase sender sequence.
|
|
//
|
|
// @param value
|
|
// Frame value.
|
|
// Increased sender frame sequence.
|
|
unsigned char increaseSendSequence(
|
|
unsigned char value)
|
|
{
|
|
return (unsigned char)((value & 0xF0) | ((value + 0x2) & 0xE));
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HDLC_CHECK
|
|
unsigned char checkFrame(
|
|
dlmsSettings* settings,
|
|
unsigned char frame)
|
|
{
|
|
//If notify
|
|
if (frame == 0x13)
|
|
{
|
|
return 1;
|
|
}
|
|
// If U frame.
|
|
if ((frame & 0x3) == 3)
|
|
{
|
|
if (frame == 0x93)
|
|
{
|
|
unsigned char isEcho = !settings->server && frame == 0x93 &&
|
|
(settings->senderFrame == 0x10 || settings->senderFrame == 0xfe) &&
|
|
settings->receiverFrame == 0xE;
|
|
resetFrameSequence(settings);
|
|
return !isEcho;
|
|
}
|
|
if (frame == 0x73 && !settings->server)
|
|
{
|
|
return settings->senderFrame == 0xFE && settings->receiverFrame == 0xE;
|
|
}
|
|
return 1;
|
|
}
|
|
// If S -frame
|
|
if ((frame & 0x1) == 1)
|
|
{
|
|
//If echo.
|
|
if (frame == (settings->senderFrame & 0xF1))
|
|
{
|
|
return 0;
|
|
}
|
|
settings->receiverFrame = increaseReceiverSequence(settings->receiverFrame);
|
|
return 1;
|
|
}
|
|
//Handle I-frame.
|
|
unsigned char expected;
|
|
if ((settings->senderFrame & 0x1) == 0)
|
|
{
|
|
expected = increaseReceiverSequence(increaseSendSequence(settings->receiverFrame));
|
|
if (frame == expected)
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
//If the final bit is not set.
|
|
if (frame == (expected & ~0x10) && settings->windowSizeRX != 1)
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
//If Final bit is not set for the previous message.
|
|
if ((settings->receiverFrame & 0x10) == 0 && settings->windowSizeRX != 1)
|
|
{
|
|
expected = (unsigned char)(0x10 | increaseSendSequence(settings->receiverFrame));
|
|
if (frame == expected)
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
//If the final bit is not set.
|
|
if (frame == (expected & ~0x10))
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
//If answer for RR.
|
|
else
|
|
{
|
|
expected = increaseSendSequence(settings->receiverFrame);
|
|
if (frame == expected)
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
if (frame == (expected & ~0x10))
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
if (settings->windowSizeRX != 1)
|
|
{
|
|
//If HDLC window size is bigger than one.
|
|
if (frame == (expected | 0x10))
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
//Pre-established connections needs this.
|
|
if ((!settings->server && settings->receiverFrame == SERVER_START_RECEIVER_FRAME_SEQUENCE) ||
|
|
(settings->server && settings->receiverFrame == CLIENT_START_RCEIVER_FRAME_SEQUENCE))
|
|
{
|
|
settings->receiverFrame = frame;
|
|
return 1;
|
|
}
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(__linux__)//If Windows or Linux
|
|
printf("Invalid frame %X. Expected %X.\r\n", frame, expected);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif //DLMS_IGNORE_HDLC_CHECK
|
|
|
|
unsigned char getNextSend(
|
|
dlmsSettings* settings, unsigned char first)
|
|
{
|
|
if (first)
|
|
{
|
|
settings->senderFrame = increaseReceiverSequence(increaseSendSequence((unsigned char)settings->senderFrame));
|
|
}
|
|
else
|
|
{
|
|
settings->senderFrame = increaseSendSequence((unsigned char)settings->senderFrame);
|
|
}
|
|
return (unsigned char)settings->senderFrame;
|
|
}
|
|
|
|
unsigned char getReceiverReady(
|
|
dlmsSettings* settings)
|
|
{
|
|
settings->senderFrame = increaseReceiverSequence((unsigned char)(settings->senderFrame | 1));
|
|
return (unsigned char)(settings->senderFrame & 0xF1);
|
|
}
|
|
|
|
unsigned char getKeepAlive(
|
|
dlmsSettings* settings)
|
|
{
|
|
settings->senderFrame = (unsigned char)(settings->senderFrame | 1);
|
|
return (unsigned char)(settings->senderFrame & 0xF1);
|
|
}
|
|
|
|
#ifndef DLMS_IGNORE_HIGH_GMAC
|
|
unsigned char isCiphered(
|
|
ciphering* cipher)
|
|
{
|
|
return cipher->security != DLMS_SECURITY_NONE;
|
|
}
|
|
#endif //DLMS_IGNORE_HIGH_GMAC
|
|
|
|
void trans_init(gxLongTransaction* trans)
|
|
{
|
|
trans->command = DLMS_COMMAND_NONE;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
BYTE_BUFFER_INIT(&trans->data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
vec_init(&trans->targets);
|
|
}
|
|
|
|
void trans_clear(gxLongTransaction* trans)
|
|
{
|
|
trans->command = DLMS_COMMAND_NONE;
|
|
#ifndef DLMS_IGNORE_MALLOC
|
|
bb_clear(&trans->data);
|
|
#endif //DLMS_IGNORE_MALLOC
|
|
vec_clear(&trans->targets);
|
|
}
|
|
|
|
void updateInvokeId(
|
|
dlmsServerSettings* settings,
|
|
unsigned char value)
|
|
{
|
|
if ((value & 0x80) != 0) {
|
|
settings->base.priority = DLMS_PRIORITY_HIGH;
|
|
}
|
|
else {
|
|
settings->base.priority = DLMS_PRIORITY_NORMAL;
|
|
}
|
|
if ((value & 0x40) != 0) {
|
|
settings->base.serviceClass = DLMS_SERVICE_CLASS_CONFIRMED;
|
|
}
|
|
else {
|
|
settings->base.serviceClass = DLMS_SERVICE_CLASS_UN_CONFIRMED;
|
|
}
|
|
settings->base.invokeID = (unsigned char)(value & 0xF);
|
|
}
|