From 8c5f2324d7aa61669324aec1a0ad091fe1379489 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Thu, 8 Dec 2016 13:51:04 +0100 Subject: src: unicens: import sources This patch adds the source tree of the NetworkManager v3.0.4. Additionally, it provides the needed configuration scripts. Change-Id: I23778b51423b51a4f87741957e0fb208bceb79b3 Signed-off-by: Christian Gromm --- .dep.inc | 5 + Makefile | 128 + Src/ConnectionInfo.cpp | 712 + Src/ConnectionInfo.h | 316 + Src/Console.c | 209 + Src/Console.h | 113 + Src/DoxyGenStartPage.h | 375 + Src/IP/MostIpc.cpp | 510 + Src/IP/MostIpc.h | 164 + Src/IP/MostMsg.cpp | 405 + Src/IP/MostMsg.h | 184 + Src/IP/MostMsgTx.cpp | 86 + Src/IP/MostMsgTx.h | 96 + Src/IP/MsgAddr.cpp | 71 + Src/IP/MsgAddr.h | 73 + Src/IP/MsgFilter.cpp | 47 + Src/IP/MsgFilter.h | 72 + Src/MacAddr.cpp | 165 + Src/MacAddr.h | 153 + Src/Main.cpp | 1255 ++ Src/Network/IndustrialStack.cpp | 275 + Src/Network/IndustrialStack.h | 238 + Src/Network/IndustrialStack_ApiGeneric.h | 125 + Src/Network/IndustrialStack_ApiV1.h | 712 + Src/Network/IndustrialStack_ApiV3.h | 1123 ++ Src/Network/IndustrialStack_LLD.cpp | 278 + Src/Network/IndustrialStack_LLD.h | 148 + Src/Network/IndustrialStack_MNS.cpp | 414 + Src/Network/IndustrialStack_MNS.h | 84 + Src/Network/IndustrialStack_Types.h | 159 + Src/Network/Network.cpp | 433 + Src/Network/Network.h | 498 + Src/Network/NetworkDevice.cpp | 903 + Src/Network/NetworkDevice.h | 862 + Src/Network/NetworkDeviceListener.cpp | 306 + Src/Network/NetworkDeviceListener.h | 568 + Src/Network/Network_CB.cpp | 1823 ++ Src/Network/Network_Private.cpp | 695 + Src/Network/NodeDatabase.cpp | 287 + Src/Network/NodeDatabase.h | 379 + Src/Network/base/.svn/dir-prop-base | 14 + Src/Network/base/.svn/entries | 164 + .../.svn/prop-base/DriverConfiguration.c.svn-base | 5 + .../.svn/prop-base/DriverConfiguration.h.svn-base | 5 + Src/Network/base/.svn/text-base/Board.c.svn-base | 51 + Src/Network/base/.svn/text-base/Board.h.svn-base | 63 + .../.svn/text-base/DriverConfiguration.c.svn-base | 608 + .../.svn/text-base/DriverConfiguration.h.svn-base | 239 + Src/Network/base/Board.c | 51 + Src/Network/base/Board.h | 63 + Src/Network/base/DriverConfiguration.c | 608 + Src/Network/base/DriverConfiguration.h | 239 + Src/SafeVector.h | 192 + Src/ScriptXml.cpp | 200 + Src/ScriptXml.h | 88 + Src/Thread.cpp | 129 + Src/Thread.h | 88 + Src/Types.h | 230 + Src/VodXml.cpp | 1112 ++ Src/VodXml.h | 361 + Src/Xml.cpp | 251 + Src/Xml.h | 179 + buildX86/Makefile-Release.mk | 368 + buildX86/Makefile-impl.mk | 133 + buildX86/Makefile-variables.mk | 27 + buildX86/Package-Release.bash | 76 + buildX86/project.xml | 27 + mnsl/mns_alm.c | 277 + mnsl/mns_alm.h | 113 + mnsl/mns_ams.c | 665 + mnsl/mns_ams.h | 163 + mnsl/mns_ams_pb.h | 262 + mnsl/mns_amsmessage.c | 656 + mnsl/mns_amsmessage.h | 217 + mnsl/mns_amspool.c | 336 + mnsl/mns_amspool.h | 100 + mnsl/mns_base.c | 70 + mnsl/mns_base.h | 93 + mnsl/mns_cfg.h | 67 + mnsl/mns_dl.c | 392 + mnsl/mns_dl.h | 132 + mnsl/mns_eh.c | 155 + mnsl/mns_eh.h | 130 + mnsl/mns_eh_pb.h | 68 + mnsl/mns_encoder.c | 254 + mnsl/mns_encoder.h | 118 + mnsl/mns_lld_pb.h | 221 + mnsl/mns_lldpool.c | 101 + mnsl/mns_lldpool.h | 112 + mnsl/mns_memory.h | 112 + mnsl/mns_memory_pb.h | 72 + mnsl/mns_message.c | 331 + mnsl/mns_message.h | 238 + mnsl/mns_message_pb.h | 121 + mnsl/mns_misc.c | 82 + mnsl/mns_misc.h | 157 + mnsl/mns_obs.c | 453 + mnsl/mns_obs.h | 196 + mnsl/mns_pmchannel.c | 311 + mnsl/mns_pmchannel.h | 179 + mnsl/mns_pmcmd.c | 157 + mnsl/mns_pmcmd.h | 92 + mnsl/mns_pmfifo.c | 1368 ++ mnsl/mns_pmfifo.h | 232 + mnsl/mns_pmfifos.c | 447 + mnsl/mns_pmfifos.h | 131 + mnsl/mns_pmp.c | 352 + mnsl/mns_pmp.h | 211 + mnsl/mns_pool.c | 127 + mnsl/mns_pool.h | 82 + mnsl/mns_ret.h | 115 + mnsl/mns_scheduler.c | 259 + mnsl/mns_scheduler.h | 148 + mnsl/mns_segmentation.c | 555 + mnsl/mns_segmentation.h | 146 + mnsl/mns_telqueue.c | 119 + mnsl/mns_telqueue.h | 93 + mnsl/mns_timer.c | 454 + mnsl/mns_timer.h | 146 + mnsl/mns_trace.h | 87 + mnsl/mns_transceiver.c | 292 + mnsl/mns_transceiver.h | 135 + mnsl/mns_types_cfg.h | 70 + mnsl/mnsl.c | 435 + mnsl/mnsl.h | 195 + nbproject/private/Makefile-variables.mk | 0 scripts/camera-os88122-ts.script | 1542 ++ scripts/camera-os88133-ap0100-flash.script | 17020 +++++++++++++++++++ scripts/camera-os88133-ap0100.script | 928 + scripts/config-agl.xml | 419 + scripts/i2c-slim-amplifier-v2.3.script | 92 + scripts/loadDriver.sh | 174 + 132 files changed, 54597 insertions(+) create mode 100644 .dep.inc create mode 100644 Makefile create mode 100644 Src/ConnectionInfo.cpp create mode 100644 Src/ConnectionInfo.h create mode 100644 Src/Console.c create mode 100644 Src/Console.h create mode 100644 Src/DoxyGenStartPage.h create mode 100644 Src/IP/MostIpc.cpp create mode 100644 Src/IP/MostIpc.h create mode 100644 Src/IP/MostMsg.cpp create mode 100644 Src/IP/MostMsg.h create mode 100644 Src/IP/MostMsgTx.cpp create mode 100644 Src/IP/MostMsgTx.h create mode 100644 Src/IP/MsgAddr.cpp create mode 100644 Src/IP/MsgAddr.h create mode 100644 Src/IP/MsgFilter.cpp create mode 100644 Src/IP/MsgFilter.h create mode 100644 Src/MacAddr.cpp create mode 100644 Src/MacAddr.h create mode 100644 Src/Main.cpp create mode 100644 Src/Network/IndustrialStack.cpp create mode 100644 Src/Network/IndustrialStack.h create mode 100644 Src/Network/IndustrialStack_ApiGeneric.h create mode 100644 Src/Network/IndustrialStack_ApiV1.h create mode 100644 Src/Network/IndustrialStack_ApiV3.h create mode 100644 Src/Network/IndustrialStack_LLD.cpp create mode 100644 Src/Network/IndustrialStack_LLD.h create mode 100644 Src/Network/IndustrialStack_MNS.cpp create mode 100644 Src/Network/IndustrialStack_MNS.h create mode 100644 Src/Network/IndustrialStack_Types.h create mode 100644 Src/Network/Network.cpp create mode 100644 Src/Network/Network.h create mode 100644 Src/Network/NetworkDevice.cpp create mode 100644 Src/Network/NetworkDevice.h create mode 100644 Src/Network/NetworkDeviceListener.cpp create mode 100644 Src/Network/NetworkDeviceListener.h create mode 100644 Src/Network/Network_CB.cpp create mode 100644 Src/Network/Network_Private.cpp create mode 100644 Src/Network/NodeDatabase.cpp create mode 100644 Src/Network/NodeDatabase.h create mode 100644 Src/Network/base/.svn/dir-prop-base create mode 100644 Src/Network/base/.svn/entries create mode 100644 Src/Network/base/.svn/prop-base/DriverConfiguration.c.svn-base create mode 100644 Src/Network/base/.svn/prop-base/DriverConfiguration.h.svn-base create mode 100644 Src/Network/base/.svn/text-base/Board.c.svn-base create mode 100644 Src/Network/base/.svn/text-base/Board.h.svn-base create mode 100644 Src/Network/base/.svn/text-base/DriverConfiguration.c.svn-base create mode 100644 Src/Network/base/.svn/text-base/DriverConfiguration.h.svn-base create mode 100644 Src/Network/base/Board.c create mode 100644 Src/Network/base/Board.h create mode 100644 Src/Network/base/DriverConfiguration.c create mode 100644 Src/Network/base/DriverConfiguration.h create mode 100644 Src/SafeVector.h create mode 100644 Src/ScriptXml.cpp create mode 100644 Src/ScriptXml.h create mode 100644 Src/Thread.cpp create mode 100644 Src/Thread.h create mode 100644 Src/Types.h create mode 100644 Src/VodXml.cpp create mode 100644 Src/VodXml.h create mode 100644 Src/Xml.cpp create mode 100644 Src/Xml.h create mode 100644 buildX86/Makefile-Release.mk create mode 100644 buildX86/Makefile-impl.mk create mode 100644 buildX86/Makefile-variables.mk create mode 100644 buildX86/Package-Release.bash create mode 100644 buildX86/project.xml create mode 100644 mnsl/mns_alm.c create mode 100644 mnsl/mns_alm.h create mode 100644 mnsl/mns_ams.c create mode 100644 mnsl/mns_ams.h create mode 100644 mnsl/mns_ams_pb.h create mode 100644 mnsl/mns_amsmessage.c create mode 100644 mnsl/mns_amsmessage.h create mode 100644 mnsl/mns_amspool.c create mode 100644 mnsl/mns_amspool.h create mode 100644 mnsl/mns_base.c create mode 100644 mnsl/mns_base.h create mode 100644 mnsl/mns_cfg.h create mode 100644 mnsl/mns_dl.c create mode 100644 mnsl/mns_dl.h create mode 100644 mnsl/mns_eh.c create mode 100644 mnsl/mns_eh.h create mode 100644 mnsl/mns_eh_pb.h create mode 100644 mnsl/mns_encoder.c create mode 100644 mnsl/mns_encoder.h create mode 100644 mnsl/mns_lld_pb.h create mode 100644 mnsl/mns_lldpool.c create mode 100644 mnsl/mns_lldpool.h create mode 100644 mnsl/mns_memory.h create mode 100644 mnsl/mns_memory_pb.h create mode 100644 mnsl/mns_message.c create mode 100644 mnsl/mns_message.h create mode 100644 mnsl/mns_message_pb.h create mode 100644 mnsl/mns_misc.c create mode 100644 mnsl/mns_misc.h create mode 100644 mnsl/mns_obs.c create mode 100644 mnsl/mns_obs.h create mode 100644 mnsl/mns_pmchannel.c create mode 100644 mnsl/mns_pmchannel.h create mode 100644 mnsl/mns_pmcmd.c create mode 100644 mnsl/mns_pmcmd.h create mode 100644 mnsl/mns_pmfifo.c create mode 100644 mnsl/mns_pmfifo.h create mode 100644 mnsl/mns_pmfifos.c create mode 100644 mnsl/mns_pmfifos.h create mode 100644 mnsl/mns_pmp.c create mode 100644 mnsl/mns_pmp.h create mode 100644 mnsl/mns_pool.c create mode 100644 mnsl/mns_pool.h create mode 100644 mnsl/mns_ret.h create mode 100644 mnsl/mns_scheduler.c create mode 100644 mnsl/mns_scheduler.h create mode 100644 mnsl/mns_segmentation.c create mode 100644 mnsl/mns_segmentation.h create mode 100644 mnsl/mns_telqueue.c create mode 100644 mnsl/mns_telqueue.h create mode 100644 mnsl/mns_timer.c create mode 100644 mnsl/mns_timer.h create mode 100644 mnsl/mns_trace.h create mode 100644 mnsl/mns_transceiver.c create mode 100644 mnsl/mns_transceiver.h create mode 100644 mnsl/mns_types_cfg.h create mode 100644 mnsl/mnsl.c create mode 100644 mnsl/mnsl.h create mode 100644 nbproject/private/Makefile-variables.mk create mode 100644 scripts/camera-os88122-ts.script create mode 100644 scripts/camera-os88133-ap0100-flash.script create mode 100644 scripts/camera-os88133-ap0100.script create mode 100644 scripts/config-agl.xml create mode 100644 scripts/i2c-slim-amplifier-v2.3.script create mode 100755 scripts/loadDriver.sh diff --git a/.dep.inc b/.dep.inc new file mode 100644 index 0000000..4560e55 --- /dev/null +++ b/.dep.inc @@ -0,0 +1,5 @@ +# This code depends on make tool being used +DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES})) +ifneq (${DEPFILES},) +include ${DEPFILES} +endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d92f5af --- /dev/null +++ b/Makefile @@ -0,0 +1,128 @@ +# +# There exist several targets which are by default empty and which can be +# used for execution of your targets. These targets are usually executed +# before and after some main targets. They are: +# +# .build-pre: called before 'build' target +# .build-post: called after 'build' target +# .clean-pre: called before 'clean' target +# .clean-post: called after 'clean' target +# .clobber-pre: called before 'clobber' target +# .clobber-post: called after 'clobber' target +# .all-pre: called before 'all' target +# .all-post: called after 'all' target +# .help-pre: called before 'help' target +# .help-post: called after 'help' target +# +# Targets beginning with '.' are not intended to be called on their own. +# +# Main targets can be executed directly, and they are: +# +# build build a specific configuration +# clean remove built files from a configuration +# clobber remove all built files +# all build all configurations +# help print help mesage +# +# Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and +# .help-impl are implemented in nbproject/makefile-impl.mk. +# +# Available make variables: +# +# CND_BASEDIR base directory for relative paths +# CND_DISTDIR default top distribution directory (build artifacts) +# CND_BUILDDIR default top build directory (object files, ...) +# CONF name of current configuration +# CND_PLATFORM_${CONF} platform name (current configuration) +# CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) +# CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) +# CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) +# CND_PACKAGE_DIR_${CONF} directory of package (current configuration) +# CND_PACKAGE_NAME_${CONF} name of package (current configuration) +# CND_PACKAGE_PATH_${CONF} path to package (current configuration) +# +# NOCDDL + + +# Environment +MKDIR=mkdir +CP=cp +CCADMIN=CCadmin + + +# build +build: .build-post + +.build-pre: +# Add your pre 'build' code here... + +.build-post: .build-impl +# Add your post 'build' code here... + + +# clean +clean: .clean-post + +.clean-pre: +# Add your pre 'clean' code here... + +.clean-post: .clean-impl +# Add your post 'clean' code here... + + +# clobber +clobber: .clobber-post + +.clobber-pre: +# Add your pre 'clobber' code here... + +.clobber-post: .clobber-impl +# Add your post 'clobber' code here... + + +# all +all: .all-post + +.all-pre: +# Add your pre 'all' code here... + +.all-post: .all-impl +# Add your post 'all' code here... + + +# build tests +build-tests: .build-tests-post + +.build-tests-pre: +# Add your pre 'build-tests' code here... + +.build-tests-post: .build-tests-impl +# Add your post 'build-tests' code here... + + +# run tests +test: .test-post + +.test-pre: build-tests +# Add your pre 'test' code here... + +.test-post: .test-impl +# Add your post 'test' code here... + + +# help +help: .help-post + +.help-pre: +# Add your pre 'help' code here... + +.help-post: .help-impl +# Add your post 'help' code here... + + + +# include project implementation makefile +include buildX86/Makefile-impl.mk + +# include project make variables +include buildX86/Makefile-variables.mk diff --git a/Src/ConnectionInfo.cpp b/Src/ConnectionInfo.cpp new file mode 100644 index 0000000..927dcb3 --- /dev/null +++ b/Src/ConnectionInfo.cpp @@ -0,0 +1,712 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include +#include "SafeVector.h" +#include "MacAddr.h" +#include "Types.h" +#include "Console.h" +#include "ConnectionInfo.h" + +#define MY_IPC_HEADER "most mac dev-type instance channel-id type bandwidth direction con-label buffer state offset offset-max p-xact dev-name" +#define MY_IPC_HEADER_LINE_END MY_IPC_HEADER"\n" + +CConnectionInfo::CConnectionInfo() : deviceType( 0 ), deviceInstance( 0 ), channelId( 0 ), mostInstance( 0 ), + dataType( EP_Unknown ), reservedBandwidth( 0 ), bufferSize( 0 ), + isTX( false), inSocketCreated( false ), outSocketCreated( false ), socketsConnected( false ), + mostConnectionLabel( 0xFFFFFFFF ), splittedOffset( 0 ), splittedMaxBandwidth( 0 ), packetsPerXact( 0 ) +{ + macAddr = new CMacAddr(); + deviceName[0] = '\0'; +} + +CConnectionInfo::CConnectionInfo( CMacAddr *macAddr, TChannelId cId, uint8_t mInst, + bool isTx ) : deviceType( 0 ), deviceInstance( 0 ), channelId( cId ), mostInstance(mInst), + dataType( EP_Unknown ), reservedBandwidth( 0 ), bufferSize( 0 ), isTX( isTx ), + inSocketCreated( false ), outSocketCreated( false ), socketsConnected( false ), + mostConnectionLabel( 0xFFFFFFFF ), splittedOffset( -1 ), splittedMaxBandwidth( -1 ), + packetsPerXact( 0 ) +{ + this->macAddr = new CMacAddr( macAddr ); //Copy the object + deviceName[0] = '\0'; +} + +CConnectionInfo::~CConnectionInfo() +{ + if( NULL != macAddr ) + { + delete macAddr; + macAddr = NULL; + } +} + +CConnectionInfoContainer::CConnectionInfoContainer() +{ +} + +CConnectionInfoContainer::~CConnectionInfoContainer() +{ + DestroyAllInfos(); +} + +uint32_t CConnectionInfoContainer::GetAllInfoAmount() +{ + return allInfos.Size(); +} + +CConnectionInfo *CConnectionInfoContainer::GetInfo( uint32_t index ) +{ + if( index >= allInfos.Size() ) + { + ConsolePrintf( PRIO_ERROR, + RED"CConnectionInfoContainer::GetInfo was called with index out of range"RESETCOLOR"\n" ); + return NULL; + } + return allInfos[index]; +} + +bool CConnectionInfoContainer::Compare( CConnectionInfo *c, CConnectionInfo *t ) +{ + if( NULL == c || NULL == t ) + { + ConsolePrintf( PRIO_ERROR, RED"Compare parameter error"RESETCOLOR"\n" ); + return false; + } + return ( ( c->bufferSize == t->bufferSize ) + && ( c->channelId == t->channelId ) + && ( c->dataType == t->dataType ) + && ( c->deviceType == t->deviceType ) + && ( c->mostConnectionLabel == t->mostConnectionLabel ) + && ( c->mostInstance == t->mostInstance ) + && ( c->reservedBandwidth == t->reservedBandwidth ) + && ( 0 == strcmp( c->deviceName, t->deviceName ) ) + && ( c->isTX == t->isTX ) + && ( *c->macAddr == *t->macAddr ) + && ( c->packetsPerXact == t->packetsPerXact ) + ); +} + +bool CConnectionInfoContainer::Compare( CConnectionInfoContainer *remote ) +{ + if( NULL == remote || + ( remote->GetAllInfoAmount() != GetAllInfoAmount() ) ) + return false; + bool equal = true; + for( uint32_t i = 0; equal && i < allInfos.Size(); i++ ) + { + equal = false; + CConnectionInfo *t = allInfos[i]; + if( NULL == t ) + break; + for( uint32_t j = 0; !equal && j < remote->GetAllInfoAmount(); j++ ) + { + CConnectionInfo *c = remote->GetInfo( j ); + if( NULL == c ) + break; + if( Compare( c, t ) ) + { + equal = true; + break; + } + } + } + return equal; +} + +bool CConnectionInfoContainer::ExistsEntry( CConnectionInfo *c ) +{ + if( NULL == c ) + return false; + bool exists = false; + for( uint32_t i = 0; !false && i < allInfos.Size(); i++ ) + { + CConnectionInfo *t = allInfos[i]; + if( NULL == t ) + break; + if( Compare( c, t ) ) + { + exists = true; + break; + } + } + return exists; +} + +CConnectionInfo *CConnectionInfoContainer::GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ) +{ + CConnectionInfo *instance = NULL; + if( NULL == macAddr ) + { + return NULL; + } + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && *temp->macAddr == *macAddr + && temp->channelId == channelId + && temp->mostInstance == mostInstance ) + { + instance = temp; + break; + } + } + return instance; +} + +uint8_t CConnectionInfoContainer::GetAmountOfDevices( TMostInstace mostInstance, TDeviceId deviceId ) +{ + uint8_t amount = 0; + CSafeVector allMacs; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && temp->deviceType == deviceId + && temp->mostInstance == mostInstance + && NULL != temp->macAddr ) + { + bool update = true; + for( uint32_t j = 0; j < allMacs.Size(); j++ ) + { + if( *allMacs[j] == *temp->macAddr ) + { + update = false; + break; + } + } + if( update ) + { + allMacs.PushBack( temp->macAddr ); + ++amount; + } + } + } + return amount; +} + +CConnectionInfo *CConnectionInfoContainer::GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance, + bool isTx ) +{ + CConnectionInfo *instance = GetInfo( macAddr, channelId, mostInstance ); + if( NULL == instance ) + { + instance = new CConnectionInfo( macAddr, channelId, mostInstance, isTx ); + allInfos.PushBack( instance ); + } + return instance; +} + +void CConnectionInfoContainer::AddInfo( CConnectionInfo *info ) +{ + if( NULL == info ) + { + ConsolePrintf( PRIO_ERROR, RED"AddInfo parameter error"RESETCOLOR"\n" ); + return; + } + allInfos.PushBack( info ); +} + +void CConnectionInfoContainer::DestroyAllInfos() +{ + allInfos.RemoveAll(true); +} + +void CConnectionInfoContainer::DestroyInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ) +{ + if( NULL == macAddr ) + { + ConsolePrintf( PRIO_ERROR, RED"DestroyInstance was called with invalid parameters."RESETCOLOR"\n" ); + return; + } + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && *temp->macAddr == *macAddr + && temp->channelId == channelId + && temp->mostInstance == mostInstance ) + { + allInfos.Remove(temp); + delete temp; + break; + } + } +} + +void CConnectionInfoContainer::PrintTable( bool hideTx, bool hideRx ) +{ + char macString[32]; + char sizeString[10]; + char stateString[32]; + char offsetString[32]; + char lastDeviceName[64]; + const char *typeString; + int16_t lastMaxOffset = -1; + + lastDeviceName[0] = '\0'; + ConsolePrintfStart( PRIO_HIGH, + "Index | MOST | MAC-ADDRESS | DEV | INST | ID | Type | BW | Dir | Con-Label | Buffer | State |DEVICE-NAME \n" ); + ConsolePrintfContinue( + "------|------|-------------------|------|------|-----|-------|-----|-----|-----------|--------|--------|-------------\n" ); + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if ( NULL == temp ) + continue; + + if( hideTx && temp->isTX ) + continue; + + if( hideRx && !temp->isTX ) + continue; + + switch( temp->dataType ) + { + case EP_Synchron: + typeString = "SYNC "; + break; + case EP_Asynchron: + typeString = "ASYNC"; + break; + case EP_Control: + typeString = "CTRL "; + break; + case EP_Isochron: + typeString = "ISOC "; + break; + case EP_Unused: + typeString = "EMPTY"; + break; + case EP_Unknown: + default: + typeString = "ERROR"; + break; + } + + if( NULL == temp->macAddr || NULL == temp->macAddr->ToString() ) + strncpy( macString, "Unknown MAC Addr.", sizeof( macString ) ); + else + strncpy( macString, temp->macAddr->ToString(), sizeof( macString ) ); + + if( 0 > temp->bufferSize ) + strncpy( sizeString, "None", sizeof( sizeString ) ); + else + snprintf( sizeString, sizeof ( sizeString ), "%04d", temp->bufferSize ); + + + const char *goodStr = GREEN"X"; + const char *badStr = RED"o"; + snprintf( stateString, sizeof ( stateString ), "%s%s%s"RESETCOLOR, ( temp->inSocketCreated ? goodStr : + badStr ), ( temp->outSocketCreated ? goodStr : badStr ), ( temp->socketsConnected ? goodStr : + badStr ) ); + + offsetString[0] = '\0'; + if( -1 != temp->splittedOffset && -1 != temp->splittedMaxBandwidth ) + { + if( 0 == temp->splittedOffset ) + { + lastMaxOffset = temp->splittedMaxBandwidth; + if( 0 != strlen( temp->deviceName ) ) + strncpy( lastDeviceName, temp->deviceName, sizeof( lastDeviceName ) ); + } + snprintf( offsetString, sizeof( offsetString ), " (offset %d/%d)", + temp->splittedOffset, ( 0 == temp->splittedOffset ? temp->splittedMaxBandwidth : lastMaxOffset ) ); + } + + ConsolePrintfContinue( "%02d ", i ); + ConsolePrintfContinue( "| %02d ", temp->mostInstance ); + ConsolePrintfContinue( "| %s ", macString ); + ConsolePrintfContinue( "| %03X ", temp->deviceType ); + ConsolePrintfContinue( "| %02d ", temp->deviceInstance ); + ConsolePrintfContinue( "| %03d ", temp->channelId ); + ConsolePrintfContinue( "| %s ", typeString ); + ConsolePrintfContinue( "| %03d ", temp->reservedBandwidth ); + ConsolePrintfContinue( "| %s ", ( temp->isTX ? "TX" : "RX" ) ); + ConsolePrintfContinue( "| %03X ", ( temp->mostConnectionLabel != 0xFFFFFFFF ) ? temp-> + mostConnectionLabel : 0xFFF ); + ConsolePrintfContinue( "| %s ", sizeString ); + ConsolePrintfContinue( "| %s ", stateString ); + ConsolePrintfContinue( "| %s", ( '\0' == offsetString[0] ) ? temp->deviceName : lastDeviceName ); + ConsolePrintfContinue( "%s\n", offsetString ); + } + ConsolePrintfExit( + "------|------|-------------------|------|------|-----|-------|-----|-----|-----------|--------|--------|-------------\n" ); +} + +uint32_t CConnectionInfoContainer::SerializeToString( char *pBuffer, uint32_t bufferLen ) +{ + if( NULL == pBuffer || 0 == bufferLen ) + { + ConsolePrintf( PRIO_ERROR, RED"SerializeToString was called with wrong parameters"RESETCOLOR"\n" ); + return 0; + } + uint32_t bytesUsed = strlen( pBuffer ); + if (bytesUsed > bufferLen) + { + ConsolePrintf(PRIO_ERROR, RED"CConnectionInfoContainer::SerializeToString given buffer"\ + " is too small!"RESETCOLOR); + return 0; + } + const char *typeString = NULL; + char stringBuffer[200]; + strncpy( pBuffer, MY_IPC_HEADER_LINE_END, bufferLen ); + + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *sourceInfo = allInfos[i]; + if ( NULL == sourceInfo ) + continue; + switch( sourceInfo->dataType ) + { + case EP_Synchron: + typeString = "SYNC"; + break; + case EP_Asynchron: + typeString = "ASYNC"; + break; + case EP_Control: + typeString = "CTRL"; + break; + case EP_Isochron: + typeString = "ISOC"; + break; + case EP_Unused: + typeString = "EMPTY"; + break; + case EP_Unknown: + default: + typeString = "ERROR"; + break; + } + + snprintf( stringBuffer, sizeof ( stringBuffer ), "%d %s %d %d %d %s %d %s %d %d %c%c%c %d %d %d %s\n", + sourceInfo->mostInstance, sourceInfo->macAddr->ToString(), sourceInfo->deviceType, + sourceInfo->deviceInstance, sourceInfo->channelId, typeString, + sourceInfo->reservedBandwidth, ( sourceInfo->isTX ? "TX" : "RX" ), sourceInfo->mostConnectionLabel, + sourceInfo->bufferSize, ( sourceInfo->inSocketCreated ? 'X' : '0' ), ( sourceInfo->outSocketCreated ? 'X' : + '0' ), ( sourceInfo->socketsConnected ? 'X' : '0' ), sourceInfo->splittedOffset, + sourceInfo->splittedMaxBandwidth, sourceInfo->packetsPerXact, sourceInfo->deviceName ); + strncat( pBuffer, stringBuffer, ( bufferLen - bytesUsed ) ); + bytesUsed = strlen( pBuffer ); + } + + return strlen( pBuffer ) + 1; +} + +bool CConnectionInfoContainer::DeserializeFromString( char *pBuffer ) +{ + if( NULL == pBuffer || 0 == strlen( pBuffer ) ) + { + ConsolePrintf( PRIO_ERROR, RED"DeserializeFromString was called with wrong parameters"RESETCOLOR"\n" ); + return false; + } + DestroyAllInfos(); + char *helpPtr; + char *token = strtok_r( pBuffer, "\n", &helpPtr ); + bool first = true; + while( NULL != token ) + { + if( first ) + { + first = false; + if( 0 != strcmp( MY_IPC_HEADER, token ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"DeserializeFromString: Incompatible header found, aborting\n"\ + "Expected:'%s'\n"\ + "Got: '%s'"\ + RESETCOLOR"\n", MY_IPC_HEADER, token ); + return false; + } + } + else + { + CConnectionInfo *info = new CConnectionInfo(); + uint32_t mostInstance; + char macString[20]; + uint32_t deviceType; + uint32_t deviceInstance; + uint32_t channelId; + char typeString[10]; + uint32_t reservedBandwidth; + char deviceDirection[10]; + uint32_t mostConnectionLabel; + uint32_t bufferSize; + int32_t offset; + int32_t maxOffset; + uint32_t xact; + char connectString[10]; + sscanf( token, "%d %s %d %d %d %s %d %s %d %d %s %d %d %d %s\n", &mostInstance, + macString, &deviceType, &deviceInstance, &channelId, typeString, &reservedBandwidth, + deviceDirection, &mostConnectionLabel, &bufferSize, connectString, &offset, &maxOffset, + &xact, info->deviceName ); + + info->mostInstance = mostInstance; + info->deviceType = deviceType; + info->deviceInstance = deviceInstance; + info->channelId = channelId; + info->reservedBandwidth = reservedBandwidth; + info->mostConnectionLabel = mostConnectionLabel; + info->bufferSize = bufferSize; + info->splittedOffset = offset; + info->splittedMaxBandwidth = maxOffset; + info->packetsPerXact = xact; + + info->isTX = strstr( deviceDirection, "TX" ); + + if( NULL != connectString && 3 == strlen( connectString ) ) + { + info->inSocketCreated = connectString[0] == 'X'; + info->outSocketCreated = connectString[1] == 'X'; + info->socketsConnected = connectString[2] == 'X'; + } + + if( NULL == info->macAddr ) + info->macAddr = new CMacAddr(); + info->macAddr->CopyValuesFromString( macString ); + + if( strstr( typeString, "ASYNC" ) ) + { + info->dataType = EP_Asynchron; + } + else if( strstr( typeString, "SYNC" ) ) + { + info->dataType = EP_Synchron; + } + else if( strstr( typeString, "CTRL" ) ) + { + info->dataType = EP_Control; + } + else if( strstr( typeString, "ISOC" ) ) + { + info->dataType = EP_Isochron; + } + else if( strstr( typeString, "EMPTY" ) ) + { + info->dataType = EP_Unused; + } + else + { + info->dataType = EP_Unknown; + } + allInfos.PushBack( info ); + } + token = strtok_r( NULL, "\n", &helpPtr ); + } + return true; +} + +uint32_t CConnectionInfoContainer::GetAmountOfCDevs() +{ + uint32_t amount = 0; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->deviceName && 0 != strlen( temp->deviceName ) ) + ++amount; + } + return amount; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoOfCdev( uint32_t cdevInst ) +{ + uint32_t amount = 0; + CConnectionInfo *info = NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->deviceName && 0 != strlen( temp->deviceName ) ) + { + if( cdevInst == amount ) + { + info = temp; + break; + } + ++amount; + } + } + return info; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoOfCdev( uint32_t cdevInst, uint32_t splittedIndex ) +{ + uint32_t amountCdev = 0; + uint32_t amountSplit = 0; + CConnectionInfo *infoCdev = NULL; + CConnectionInfo *infoSplitted = NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL == infoCdev ) + { + if( NULL != temp && NULL != temp->deviceName && 0 != strlen( temp->deviceName ) ) + { + if( cdevInst == amountCdev ) + { + infoCdev = temp; + if( 0 != infoCdev->splittedOffset ) + { + ConsolePrintf( PRIO_ERROR, + RED"CConnectionInfoContainer::GetConnectionInfoOfCdev"\ + " was called for a non splitted CDEV"RESETCOLOR"\n" ); + break; + } + } + ++amountCdev; + } + } + else + { + if( ( 0 == temp->splittedOffset ) || ( -1 == temp->splittedOffset ) ) + break; + if( amountSplit == splittedIndex ) + { + infoSplitted = temp; + break; + } + ++amountSplit; + } + } + return infoSplitted; +} + +uint32_t CConnectionInfoContainer::GetAmountOfSinks() +{ + uint32_t amount = 0; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *sourceInfo = allInfos[i]; + if( NULL == sourceInfo || NULL == sourceInfo->macAddr || NULL == sourceInfo->macAddr->ToString() ) + continue; + if( NULL == sourceInfo->deviceName || 0 == strlen( sourceInfo->deviceName ) ) + continue; + if( !sourceInfo->socketsConnected ) + continue; + for( uint32_t j = 0; j < allInfos.Size(); j++ ) + { + CConnectionInfo *sinkInfo = allInfos[j]; + if( i == j ) + continue; + if( NULL == sinkInfo->macAddr || NULL == sinkInfo->macAddr->ToString() ) + continue; + if( !sinkInfo->socketsConnected ) + continue; + if( sourceInfo->mostConnectionLabel != sinkInfo->mostConnectionLabel ) + continue; + if( sourceInfo->mostInstance != sinkInfo->mostInstance ) + continue; + ++amount; + } + } + return amount; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoOfSink( uint32_t sinkInst ) +{ + uint32_t amount = 0; + CConnectionInfo *info = NULL; + for( uint32_t i = 0; NULL == info && i < allInfos.Size(); i++ ) + { + CConnectionInfo *sourceInfo = allInfos[i]; + if( NULL == sourceInfo || NULL == sourceInfo->macAddr || NULL == sourceInfo->macAddr->ToString() ) + continue; + if( NULL == sourceInfo->deviceName || 0 == strlen( sourceInfo->deviceName ) ) + continue; + if( !sourceInfo->socketsConnected ) + continue; + for( uint32_t j = 0; NULL == info && j < allInfos.Size(); j++ ) + { + CConnectionInfo *sinkInfo = allInfos[j]; + if( i == j ) + continue; + if( NULL == sinkInfo->macAddr || NULL == sinkInfo->macAddr->ToString() ) + continue; + if( !sinkInfo->socketsConnected ) + continue; + if( sourceInfo->mostConnectionLabel != sinkInfo->mostConnectionLabel ) + continue; + if( sourceInfo->mostInstance != sinkInfo->mostInstance ) + continue; + if( sinkInst == amount ) + { + info = sinkInfo; + } + ++amount; + } + } + return info; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoByMacAddress( const uint8_t *mac ) +{ + if( NULL == mac ) + return NULL; + char macAddr[20]; + snprintf( ( char * )&macAddr, sizeof( macAddr ), "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5] ); + CConnectionInfo *info = NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->macAddr ) + { + if( 0 == strcmp( temp->macAddr->ToString(), macAddr ) ) + { + info = temp; + break; + } + } + } + return info; +} + +CConnectionInfo *CConnectionInfoContainer::GetRemoteConnectionInfoByMacAddress( const uint8_t *mac ) +{ + if( NULL == mac ) + return NULL; + char macAddr[20]; + snprintf( ( char * )&macAddr, sizeof( macAddr ), "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5] ); + CConnectionInfo *remoteInfo = NULL; + CConnectionInfo *info = GetConnectionInfoByMacAddress( mac ); + if( NULL == info ) + return NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->macAddr ) + { + if( 0 == strcmp( temp->macAddr->ToString(), macAddr ) ) + continue; + if( ( temp->mostInstance == info->mostInstance ) && + ( temp->mostConnectionLabel == info->mostConnectionLabel ) && + temp->socketsConnected ) + { + remoteInfo = temp; + break; + } + } + } + return remoteInfo; +} diff --git a/Src/ConnectionInfo.h b/Src/ConnectionInfo.h new file mode 100644 index 0000000..13016f5 --- /dev/null +++ b/Src/ConnectionInfo.h @@ -0,0 +1,316 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains a helper class to store MOST connections + */ +/*----------------------------------------------------------*/ +#ifndef CONNECTIONINFO_H +#define CONNECTIONINFO_H + +#include +#include +#include +#include "SafeVector.h" +#include "Types.h" +#include "MacAddr.h" + +/*! + * \brief This is a helper class for the CNetworkManagerHandler. It keeps track of the reported connections + * and the instanced CMultiplexer classes. + */ +class CConnectionInfo +{ +public: + CConnectionInfo(); + CConnectionInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance, bool isTx ); + ~CConnectionInfo(); + + ///The Ethernet MAC address of this connection. + CMacAddr *macAddr; + + ///The device type as specified in XML file and configured in the config string (Group-Address) + TDeviceId deviceType; + + ///The instance count of this device class. Starting at 0 for the first instance. Incremented with every occurrence. + uint8_t deviceInstance; + + ///The channel ID as declared in the configuration XML file. + TChannelId channelId; + + ///The MOST ring instance. Starting at 0 for the first MOST ring, on the first OS81110. + TMostInstace mostInstance; + + ///The MOST data used by this channel. + EPDataType_t dataType; + + ///The reserved MOST bandwidth in bytes for this channel. + TBlockwidth reservedBandwidth; + + ///The amount of bytes reserved for this channel in Driver. If no buffers were allocated, this value is -1. + int32_t bufferSize; + + ///In case this is a local connection, the path of the character device is stored in this variable. (e.g. /dev/mdev0). + char deviceName[64]; + + ///TRUE if this connections is a source. FALSE if this connection is a sink, + bool isTX; + + //TRUE, if the input socket of this connection was successfully created. + bool inSocketCreated; + + //TRUE, if the output socket of this connection was successfully created. + bool outSocketCreated; + + //TRUE, if the input socket and the output socket of this connection were successfully connected. + bool socketsConnected; + + ///The MOST connection label. + uint32_t mostConnectionLabel; + + ///If this socket is a splitted / combined socket, this value holds the offset in byte. -1 if this is a normal socket. + int16_t splittedOffset; + + ///If this socket is a splitted / combined socket, this value holds the maximum bandwidth of all splitted sockets in byte. -1 if this is a normal socket. + int16_t splittedMaxBandwidth; + + ///This is USB specific. It describes how many sub-frames are concatenated into a single USB microframe. + uint16_t packetsPerXact; +}; + +/*! + * \brief This is a storage class holding multiple CConnectionInfo in a container. + * It supports adding, removing, serializing and deserializing + */ +class CConnectionInfoContainer +{ +private: + CSafeVector allInfos; + bool Compare( CConnectionInfo *c, CConnectionInfo *t ); +public: + + /*----------------------------------------------------------*/ + /*! \brief Default Constructor of CConnectionInfoContainer + */ + /*----------------------------------------------------------*/ + CConnectionInfoContainer(); + + /*----------------------------------------------------------*/ + /*! \brief Default Destructor of CConnectionInfoContainer + */ + /*----------------------------------------------------------*/ + ~CConnectionInfoContainer(); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of entries stored in the whole database. + * \return The amount of entries. + */ + /*----------------------------------------------------------*/ + uint32_t GetAllInfoAmount(); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the information at the corresponding position. + * \param index - the position inside the database + * \note Use GetAllInfoAmount() to get the max index (n-1). + * \return The information if found, NULL if out of range + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetInfo( uint32_t index ); + + + /*----------------------------------------------------------*/ + /*! \brief Compares to container. + * \param containerToCompare - The container to compare. + * \return true, if both containers stores the same information. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool Compare( CConnectionInfoContainer *containerToCompare ); + + + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given information is already stored inside the database. + * \param checkInfo - the information object to be checked + * \return true, if the information is already stored inside the database. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool ExistsEntry( CConnectionInfo *checkInfo ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets an info object with the given parameters + * \param macAddr - The MAC address of the device + * \param channelId - The channel identifier as used in the XML file. + * \param mostInstance - The MOST ring instance, starting 0 for the first MOST instance. + * \return The info object if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of devices with the same + * \param mostInstance - The MOST ring instance. Starting at 0 for the first MOST ring. + * \param deviceId - The device ID as declared in the configuration XML file. + * \return The Amount of devices found, with the given deviceId + */ + /*----------------------------------------------------------*/ + uint8_t GetAmountOfDevices( TMostInstace mostInstance, TDeviceId deviceId ); + + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which delivers a CConnectionInfo object to the given search parameters. + * \param macAddr - Searches for the given MAC address. + * \param channelId - Search for the given Channel address. + * \param mostInstance - Search on the given MOST ring instance. + * \param isTx - true, search for a transmission connection. false, search for a reception connection. + * \return An pointer to the matching CConnectionInfo object if found. NULL otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance, bool isTx ); + + + /*----------------------------------------------------------*/ + /*! \brief Stores the given information inside the database. + * \param info - The information to be stored. + */ + /*----------------------------------------------------------*/ + void AddInfo( CConnectionInfo *info ); + + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which deletes all stored informations retrieved so far. + */ + /*----------------------------------------------------------*/ + void DestroyAllInfos(); + + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which delete the information matching to the given parameters. + * \param macAddr - Searches for the given MAC address. + * \param channelId - Search for the given Channel address. + * \param mostInstance - Search on the given MOST ring instance. + */ + /*----------------------------------------------------------*/ + void DestroyInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which prints all available information to the console. + * \param hideTx - true, when transmission connections shall not be shown in the table. + * \param hideRx - true, when reception connections shall not be shown in the table. + */ + /*----------------------------------------------------------*/ + void PrintTable( bool hideTx, bool hideRx ); + + /*----------------------------------------------------------*/ + /*! \brief Serializes all connection informations to multi line string. + * \param pBuffer, buffer which then will hold the serialized string. + * \param bufferLen, the size of the given buffer. + * \return The amount of bytes used from the given buffer. + */ + /*----------------------------------------------------------*/ + uint32_t SerializeToString( char *pBuffer, uint32_t bufferLen ); + + /*----------------------------------------------------------*/ + /*! \brief Deserializes all connection informations to multi line string. + * \param pBuffer, buffer which will read of the serialized string. + * \return True, if the string could be used to deserialize all data. False, error occurred. + * \note The given buffer will be manipulated by strtok operation! + */ + /*----------------------------------------------------------*/ + bool DeserializeFromString( char *pBuffer ); + + + /*----------------------------------------------------------*/ + /*! \brief Returns the amount of entries, which contains a CDEV device name + * \return The amount of devices. + */ + /*----------------------------------------------------------*/ + uint32_t GetAmountOfCDevs(); + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the information of the specific CDEV. + * \param cdevInst - The index of the CDEV device, starting with 0 for the first device. + * \note Use GetAmountOfCDevs() the get the maximum possible index (n-1). + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoOfCdev( uint32_t cdevInst ); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the information of the specific CDEV. + * \param cdevInst - The index of the CDEV device, starting with 0 for the first device. + * \param splittedIndex - Index of splitted socket entry, starting with 0 for the first splitted entry which has an offset greater than zero. + * \note Use GetAmountOfCDevs() the get the maximum possible index (n-1). + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoOfCdev( uint32_t cdevInst, uint32_t splittedIndex ); + + + /*----------------------------------------------------------*/ + /*! \brief Returns the amount of entries, which acts as a sink channel. + * \return The amount of devices. + */ + /*----------------------------------------------------------*/ + uint32_t GetAmountOfSinks(); + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the information of the specific sink. + * \param cdevInst - The index of the sink, starting with 0 for the sink. + * \note Use GetAmountOfSinks() the get the maximum possible index (n-1). + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoOfSink( uint32_t sinkInst ); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the information object by the given MAC address. + * \param mac - The MAC address of the searched device. + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoByMacAddress( const uint8_t *mac ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets the opposite device information connected with given MAC address. + * \param mac - The MAC address of the device, which then the information of connected opposite device is returned. + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetRemoteConnectionInfoByMacAddress( const uint8_t *mac ); +}; + +#endif //CONNECTIONINFO_H diff --git a/Src/Console.c b/Src/Console.c new file mode 100644 index 0000000..4ae52a3 --- /dev/null +++ b/Src/Console.c @@ -0,0 +1,209 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Console.h" + +///Our shared memory key +static int shmkey = 072162537; +//The minimum priority +static ConsolePrio_t minPrio = PRIO_LOW; + +/*! \cond PRIVATE */ +typedef struct +{ + ///The first process will setup the critical section and set this variable to true. + bool initialized; + ///If set to true, multiple processeses are synced via shared memory. + bool processSynced; + ///If set to true, there is an segmented print ongoing (Start, Continue, Exit). + bool criticalSection; + //If is in a critical segmented print, this variable will hold the prio for Start, Continue, Exit. + ConsolePrio_t criticalSectionPrio; + ///Handle of the shared mutex. + pthread_mutex_t mutex; +} sharedData_t; +/*! \endcond */ + +/*----------------------------------------------------------*/ +/*! \brief Pointer to the shared memory instance. + */ +/*----------------------------------------------------------*/ +static sharedData_t *data = NULL; + +void ConsoleInit( bool synchronizeProcesses ) +{ + pthread_mutexattr_t attr; + + if( synchronizeProcesses ) + { + int shmid = shmget( shmkey, sizeof( sharedData_t ), IPC_CREAT | 0666 ); + if( ( sharedData_t * )-1 == ( data = ( sharedData_t* )shmat( shmid, NULL, 0 ) ) ) + { + data = NULL; + fprintf( stderr, RED"ConsoleInit failed, because shared memory could not be accessed."RESETCOLOR"\n" ); + return; + } + } + else + { + data = ( sharedData_t * )calloc( 1, sizeof( sharedData_t ) ); + } + if( ( NULL != data ) && !data->initialized ) + { + data->processSynced = synchronizeProcesses; + data->initialized = true; + data->criticalSection = false; + + pthread_mutexattr_init( &attr ); + if( synchronizeProcesses ) + pthread_mutexattr_setpshared( &attr, PTHREAD_PROCESS_SHARED ); + else + pthread_mutexattr_setpshared( &attr, PTHREAD_PROCESS_PRIVATE ); + pthread_mutex_init( &data->mutex, &attr ); + } +} + +void ConsoleDeinit( void ) +{ + if( NULL != data && !data->processSynced ) + { + free( data ); + data = NULL; + } +} + +void ConsoleSetPrio( ConsolePrio_t prio ) +{ + minPrio = prio; +} + +void ConsolePrintf( ConsolePrio_t prio, const char *statement, ... ) +{ + int err; + if( prio < minPrio || NULL == statement ) + return; + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintf data was null"RESETCOLOR"\n" ); + return; + } + if( 0 != ( err = pthread_mutex_lock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintf, pthread_mutex_lock error: %d"RESETCOLOR"\n", err ); + return; + } + + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + + if( 0 != ( err = pthread_mutex_unlock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintf, pthread_mutex_unlock error: %d"RESETCOLOR"\n", err ); + return; + } +} + +void ConsolePrintfStart( ConsolePrio_t prio, const char *statement, ... ) +{ + int err; + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintfStart data was null"RESETCOLOR"\n" ); + return; + } + if( 0 != ( err = pthread_mutex_lock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintfStart, pthread_mutex_lock error: %d"RESETCOLOR"\n", err ); + return; + } + data->criticalSection = true; + data->criticalSectionPrio = prio; + + if( data->criticalSectionPrio >= minPrio && NULL != statement ) + { + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + } +} + +void ConsolePrintfContinue( const char *statement, ... ) +{ + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintfContinue data was null"RESETCOLOR"\n" ); + return; + } + if( !data->criticalSection ) + { + fprintf( stderr, RED"ConsolePrintfContinue not in critical section"RESETCOLOR"\n" ); + return; + } + + if( data->criticalSectionPrio >= minPrio && NULL != statement ) + { + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + } +} + +void ConsolePrintfExit( const char *statement, ... ) +{ + int err; + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintfExit data was null"RESETCOLOR"\n" ); + return; + } + if( !data->criticalSection ) + { + fprintf( stderr, RED"ConsolePrintfExit not in critical section"RESETCOLOR"\n" ); + return; + } + if( data->criticalSectionPrio >= minPrio && NULL != statement ) + { + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + } + data->criticalSection = false; + if( 0 != ( err = pthread_mutex_unlock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintfExit, pthread_mutex_unlock error: %d"RESETCOLOR"\n", err ); + } +} diff --git a/Src/Console.h b/Src/Console.h new file mode 100644 index 0000000..aab7e71 --- /dev/null +++ b/Src/Console.h @@ -0,0 +1,113 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains C-functions starting with "Console" to provide + * process and thread safe access to the console output. + */ +/*----------------------------------------------------------*/ +#ifndef _CONSOLE_H_ +#define _CONSOLE_H_ + +#define RESETCOLOR "\033[0m" +#define GREEN "\033[0;32m" +#define RED "\033[0;31m" +#define YELLOW "\033[1;33m" +#define BLUE "\033[0;34m" + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef enum + { + PRIO_LOW = 0, + PRIO_MEDIUM = 1, + PRIO_HIGH = 2, + PRIO_ERROR = 0xFF + } ConsolePrio_t; + + /*----------------------------------------------------------*/ + /*! \brief Initializes the resources needed to synchronize between processes and threads. + * \note This function must be called before any other function of this component. + * \param synchronizeProcesses - If set to true, multiple processes using this component will synchronize their output. + * + */ + /*----------------------------------------------------------*/ + void ConsoleInit( bool synchronizeProcesses ); + + /*----------------------------------------------------------*/ + /*! \brief Destroys the resources needed to synchronize between processes and threads. + * \note After this function, any other function (except ConsoleInit) must not be called. + * + */ + /*----------------------------------------------------------*/ + void ConsoleDeinit( void ); + + + /*----------------------------------------------------------*/ + /*! \brief Sets the minimum priority to be displayed. Lower priority messages are discarded + * \param prio - The minimum priority to display + */ + /*----------------------------------------------------------*/ + void ConsoleSetPrio( ConsolePrio_t prio ); + + /*----------------------------------------------------------*/ + /*! \brief Uses the board specific PRINT mechanism and provides thread and process safety. + * + */ + /*----------------------------------------------------------*/ + void ConsolePrintf( ConsolePrio_t prio, const char *statement, ... ); + + + /*----------------------------------------------------------*/ + /*! \brief Starts to print and stay blocked after exit of this function + * + */ + /*----------------------------------------------------------*/ + void ConsolePrintfStart( ConsolePrio_t prio, const char *statement, ... ); + + /*----------------------------------------------------------*/ + /*! \brief Continue to print and stay blocked after exit of this function + * \note ConsolePrintfStart must be called before and when finished ConsolePrintfExit must be called. + * \note This function may be called multiple times. + */ + /*----------------------------------------------------------*/ + void ConsolePrintfContinue( const char *statement, ... ); + + /*----------------------------------------------------------*/ + /*! \brief Continue to print and unblock after finishing. + * \note ConsolePrintfStart must be called before. ConsolePrintfContinue may have been called before multiple times. + */ + /*----------------------------------------------------------*/ + void ConsolePrintfExit( const char *statement, ... ); + +#ifdef __cplusplus +} +#endif + +#endif //_CONSOLE_H_ diff --git a/Src/DoxyGenStartPage.h b/Src/DoxyGenStartPage.h new file mode 100644 index 0000000..1eb98b2 --- /dev/null +++ b/Src/DoxyGenStartPage.h @@ -0,0 +1,375 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file generates the start page of the document you are currently reading. + */ +/*----------------------------------------------------------*/ +#ifndef DOXYGEN_START_PAGE_H +#define DOXYGEN_START_PAGE_H + + +/*----------------------------------------------------------*/ +/*! \mainpage + * + * \section intro_sec Introduction + * + * This package holds the sample application and the Linux driver to realize a certain use cases like MOST based Video on Demand Service, Slim-Audio Applications and Slim-Camera Applications. \n + * All components where written for and tested for the on various Linux distributions on FreeScale i.MX6q, Ubuntu 14.4 LTS 64Bit on PC, Raspbian on Raspbery Pi 2. \n + * + * \section Package Contents + * + * \subsection examples Folder: "./Examples" + * - All the examples + * \subsubsection networkmanager Folder: "./Examples/NetworkManager" + * - The main application, which starts up MOST and configure all connections. \n + * \subsubsection videoondemand Folder: "./Examples/VideoOnDemand" + * - The Video On Demand Streaming Server and client examples \n + * Click here to see the documentation of the VideoOnDemand Example. + * \subsubsection audiomixer Folder: "./Examples/AudioMixer" + * - The synchronous Audio Mixer example\n + * Click here to see the documentation of the AudioMixer Example. + * \subsubsection PatternCheck Folder: "./Examples/PatternCheck" + * - Pattern Generator and Pattern Checker example\n + * \subsubsection SlimCamera Folder: "./Examples/SlimCamera" + * - SlimCamera example, with helper tool to stream from MOST isochronous channel to Ethernet UDP.\n + * \subsection linuxdriver Folder: "./LinuxDriver" + * - The Linux driver modules as source code. \n + * Click here to see the documentation of the MOST Linux Driver. + * \subsection resources Folder:"./Resources" + * - Shell scripts to convert media typess. + * - INIC Explorer Windows tool. + * - INIC firmware. + * - ConfigString sample setups. + * - Shell scripts to convert Audio and Video. + * + * \section install_sec Setup + * \note General hint: Do not use white spaces in the path to this package, as some of the commands + * listed here need to be adapted otherwise. + * + * \subsection step1 Step 1: Unpack the delivered file + * - Enter: + * \code tar xvfz NM-Vx.x.x.tar.gz \endcode + * to unpack the folder. Replace the 'x' characters with you version. + * + * \subsection step2 Step 2: Edit the cross compiling environment variables and load them + * Use your favorite text editor to edit "crosscompile.sh".: + * - Example: + * \code vi crosscompile.sh \endcode + * - Or: + * \code gedit crosscompile.sh \endcode + * - Set your cross-compiler-toolchain path to the "CROSS_COMPILE" variable. + * - Set the target Linux kernel sources path to the "KDIR" variable. + * - Set the compiler flags (such as platform specific variables or setting up the path where the libraries shall be searched) to the "PROJECT_C_FLAGS". + * - Set the path to search for header files to the INCLUDE_PATH variable. + * - Set variables needed by the linker to LIBRARIES_PATH. + * + * - Example configuration made for a Yocto image on iMX6: + * \code + * #!/bin/sh + * source /opt/poky/1.6.2/environment-setup-cortexa9hf-vfp-neon-poky-linux-gnueabi + * export ARCH=arm + * export CC=${CROSS_COMPILE}gcc + * export KDIR=/home/x/fsl-community-bsp/build/tmp/work/imx6qsabreauto-poky-linux-gnueabi/linux-imx/3.10.53-r0/git + * export PROJECT_C_FLAGS="-static -L /home/x/fsl-community-bsp/build/tmp/sysroots/imx6qsabreauto/usr/lib -lm -lz -lcrypt -pthread" + * export INCLUDE_PATH="-I/home/x/fsl-community-bsp/build/tmp/sysroots/imx6qsabreauto/usr/include/libxml2" + * export LIBRARIES_FLAGS="-lm -lz -lcrypt" + * \endcode + *
+ * - Example configuration made for a LTIB image on iMX6: + * \code + * #!/bin/sh + * export CROSS_COMPILE=/opt/freescale/usr/local/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro-toolchain/bin/arm-none-linux-gnueabi- + * export ARCH=arm + * export CC=${CROSS_COMPILE}gcc + * export KDIR=/home/x/imx6/sdk/ltib/rpm/BUILD/linux + * export PROJECT_C_FLAGS="-L /home/x/imx6/sdk/ltib/rootfs/usr/lib -lm -lz -lcrypt -pthread" + * export INCLUDE_PATH="-I/home/x/imx6/sdk/ltib/rootfs/usr/include/libxml2" + * export LIBRARIES_FLAGS="-lm -lz -lcrypt" + * \endcode + *
+ * + * Next load the variables. + * - Enter: + * \code source crosscompile.sh \endcode + * - To check if the variables are set, at any time for a certain terminal, enter: + * \code echo $ARCH \endcode + * When "arm" is printed, the variables are loaded. + * \note The variables are only loaded in the current shell. If you open a new shell or a new tab, the variables have then loaded again. + * \note If you want to run the binaries on the same host and you are on a Debian based system, you may skip this step. If you encounter problems anyway, you may have to set the PROJECT_C_FLAGS, INCLUDE_PATH and LIBRARIES_PATH also. + * + * \subsection step3 Step 3: Compile driver and examples + * The VoD server sample application needs the libxml2 library, this may cause problems while compiling and linking. In this case, you have to find the library and the headers for your target. Maybe your platform builder (LTIB, Yocto) can create the needed library.\n + * On a Debian based system you can easily install these packages by typing: + * \code sudo apt-get install build-essential libxml2-dev \endcode + * + * Compiling all the examples and the MOST Linux Driver is done by calling make in the root folder of the package. + * \code make \endcode + * + * It may be necessary to patch the Linux Driver source code, depending on the Kernel version you use. + * For Kernel Version 3.16.x, enter: + * \code + * make patch-kernel-3.16 + * make + * \endcode + * For Kernel Version 3.18.x, enter: + * \code + * make patch-kernel-3.18 + * make + * \endcode + * If you are facing problems with a different kernel version, please let us know: support-ais-de@microchip.com + * + * Compiling just the examples, without the MOST Linux Driver, enter: + * \code make examples\endcode + * + * If you want to build also the driver for DIM2-IP, enter: + * \code make dim2\endcode + * + * If you want to clean up the folder, enter: + * \code make clean \endcode + * \subsection step4 Step 4: Setup Streaming parameters and deploy configuration file + * Use your favorite text editor to edit "config.xml". \n + * Example: + * \code vi config.xml \endcode + * or: + * \code gedit config.xml \endcode + * + * The given file is ready to work, but it may be changed to reflect the real use cases. + * + * The parameters that may be changed by the user are: + * - \code network_manager.network_config \endcode Must be instanced only once. + * + * - \code network_manager.network_config.timing_master \endcode + * - 1 = VoD Server will act as timing master on all connected INICs. + * - 0 = VoD Server will act as timing slave on all connected INICs. + * + * - \code network_manager.network_config.async_bandwidth \endcode = The amount of bytes reserved for Ethernet data. \n + * - 0 = no Ethernet traffic possible, max isochronous Bandwidth. \n + * - 372 = Max Ethernet Bandwidth, no isochronous traffic possible. \n + * + * - \code network_manager.device \endcode May be instanced multiple times. + * + * - \code network_manager.device.device_type \endcode Identifier for a specific types of hardware. \n + * The implementation uses the INIC group address to hold this value. See Step 5. \n + * So make sure to set the value correctly in the ConfigString with INIC Explorer tool. \n + * + * Here some examples: \n + * - 784 (0x310): Server with OS81118 \n + * - 810 (0x320): Seat with OS81110 \n + * - 816 (0x330): Seat with OS81118 \n + * - 832 (0x340): Tuner with OS81110 \n + * \note This value must be specified in decimal way. \n Setting hex value with leading 0x will not work. \n + * + * - \code network_manager.device.device_api_ver \endcode The Port Message Protocol Version number. \n + * - OS81110 uses API version 1, so set value to 1. \n + * - OS81118 uses API version 2, so set value to 2. \n + * + * - \code network_manager.device.channel \endcode May be instanced multiple times. + * + * - \code network_manager.device.channel.channel_id \endcode Unique identifier for this channel. + * May be incremented with every new instance inside "network_manager.device". + * + * - \code network_manager.device.channel.socket \endcode" Must be instanced two times for all data types except of asynchronous channel. \n + * One instance is the input socket and the other is the output socket. \n + * \note Asynchronous channel must be one channel, specify the bus to the EHC (e.g. MLB). \n + * The asynchronous MOST channel will be automatically created. + * + * - \code network_manager.device.channel.socket.dir \endcode Specifies the direction of the current socket. \n + * Possible values are: \n + * - "IN" - The current socket will act as source. + * - "OUT" - The current socket will act as sink. + * + * - \code network_manager.device.channel.socket.port_id \endcode Enumeration declaring the type of physical port to use. \n + * Possible values are currently: \n + * - "MLB" - Media Local Bus, OS81110 and OS81118 \n + * - "USB" - Universal Serial Bus, OS81118 \n + * - "I2S" - Streaming port, OS81110 and OS81118 \n + * - "MOST" + * + * - \code network_manager.device.channel.socket.data_type \endcode Enumeration declaring the type of data type transmitted. \n + * Possible value currently only: \n + * - "ISOC" - Isochronous data. \n + * - "ASYNC" - Asynchronous data. + * - "SYNC" - Synchronous data. + * + * - \code network_manager.device.channel.socket.channel_addr \endcode Is only valid for non MOST physical ports. \n + * - In case of MLB this is the MLB-channel address. \n + * - In case of USB this is the USB endpoint address. \n + * \note This value must be specified in decimal way. \n Setting hex value with leading 0x will not work. \n + * + * - \code network_manager.device.channel.socket.blockwidth \endcode Is only valid for the MOST and MLB physical ports. + * The value is given as amount of bytes, reserved specific for this socket. + * + * - \code network_manager.device.channel.socket.number_of_buffers \endcode Is only valid for local attached INICs. + * The value is given as amount of buffers used in the driver. + * \note This property is optional. If not set, the application will try to use default values. + * + * - \code network_manager.device.channel.socket.buffer_size \endcode Is only valid for local attached INICs. + * The value is given as amount of bytes of a single buffer used in the driver. + * \note This property is optional. If not set, the application will try to use default values. + * + * - \code network_manager.device.channel.socket.packets_per_xact \endcode Is only valid for local attached INICs. + * The value is given as amount packets transmitted in one USB frame which is max. 512 byte long. + * For datatype ISOC and SYNC stuffing will be used,to fill up the unused bytes of the USB frame. + * \note This property is optional. If not set, the application will try to use default values. + * + * - \code network_manager.device.channel.socket.subbuffer_size \endcode + * The value is given as amount of bytes. It measures the width of a subbuffer. + * For datatype ISOC this may be for example 188 or 196 byte. + * \note This property is optional. If not set, the application will try to use default values. + * + * Copy the configuration to the target: \n + * Example: \n + * \code scp config.xml root@192.168.1.100:/root \endcode + * \note If you do not cross compile, you can skip the copy process. Otherwise you need to adapt the IP address according to your targets address. The folder is freely choosable, here: "/root". + * + * \subsection step5 Step 5: Setup INICs on all platforms + * \note InicExplorer V1 and Target Manager (InicExplorer V2) will run under Microsoft Windows operating systems only. + * + * The latest version of InicExplorer V1 can be downloaded from this URL: \n + * http://www.k2l.de/products/analysis-and-verification-tools/inic-medialb-tools/inic-explorer/inic-explorer-downloads + * + * The Target Manager (InicExplorer V2) installer is included in this package in the sub-folder "Resources". + * + * - The configuration of OS81110 is done with the InicExplorer V1 tool. + * - The configuration of OS81118 is done with the Target Manager (InicExplorer V2) tool. + * + * \note Important hint: The MOST group address set in the ConfigString is interpreted as DeviceType! \n + * This means certain types of devices share the same group address. \n + * Here some examples, configured by the config.xml shipped along with this package: \n + * - 784 (0x310): Server with OS81118 + * - 810 (0x320): Seat with OS81110 + * - 816 (0x330): Seat with OS81118 + * - 832 (0x340): Tuner with OS81110 + * + * Also there are example configuration files, for the Target Manager (InicExplorer V2) (FileEnding.jscs). \n + * \n + * Currently the VoD server application expects the OS8118 USB endpoints to be configured as following: \n + * Receive endpoints: + * - EP-81 = RX CONTROL + * - EP-82 = RX ASYNC + * - EP-83 = RX + * - EP-84 = RX + * - EP-85 = RX + * - EP-86 = RX + * - EP-87 = RX + * + * Transmit endpoints: + * - EP-08 = TX CONTROL + * - EP-09 = TX ASYNC + * - EP-0A = TX + * - EP-0B = TX + * - EP-0C = TX + * - EP-0D = TX + * - EP-0E = TX + * - EP-0F = TX + * + * For the OS81118 INICs attached to the server device do following steps: + * - Attach InicExplorer hardware to UART and connect it to the INIC connector. + * - Open Target Manager (InicExplorer V2) + * - Press the "Detect"-button. (If not found, try different COM instance) + * - Flash INIC firmware, provided by this package, by pressing "Flash"-button. + * - Select "OS81118_Vx_x_x_x_PV.mchpkg" and press the "Open"-button. Replace the 'x' characters with you version. + * - Make sure that in the next window there is "configstring" and "firmware"-check-box is checked. Then press "Ok"-Button. + * - Flashing takes some minutes. + * - After successful flashing, press "Explore"-button. + * - Press "Ok"-Button in the upcoming Package Selector dialogue. + * - Press "Load"-Button in the Configuration String Editor Window. + * - Select "inic-config-ga310.jscs", and press "Open"-Button. + * - Verify the settings, if they match to your needs. + * - Press "Write"-Button to write the configuration into the INIC. + * + * \subsection step6 Step 6: Load Linux driver on target + * Login into your target. This step will be necessary both for server and clients. \n + * Switch to the directory, where you have copied all outputs of the previous steps. \n + * + * In order to load all drivers and set them up in one step.\n + * Enter: \n + * \code sudo ./loadDriver.sh &\endcode + * \note The trailing "&" will run the script as daemon. It will run in background and set the driver up, whenever a new INIC is detected. \n + * + * \note If the device does not appear in the /dev folder, check if the USB device was registered to the system by typing: \code lsusb \endcode + * + * In order to unload the driver and the background daemon.\n + * Enter: \n + * \code + * sudo killall loadDriver.sh + * sudo ./unloadDriver.sh + * \endcode + * + * + * \subsection step7 Step 7: Enable Ethernet communication on MOST + * + * The "loadDriver.sh" shell script, started in the previous script, already started the MOST Ethernet device.\n + * But in order to get a correct network setup, you have to assign unique IP-addresses to each of your target. + * + * Login to the client target device. \n + * Switch to the directory, where you have copied all outputs of the previous steps. \n + * \n + * Check if the meth0 device is created. \n + * Enter:\n + * \code ifconfig\endcode + * \n + * Display Output:\n + * \code + * meth0 Link encap:Ethernet HWaddr 02:00:00:00:00:01 + * inet addr:10.0.0.1 Mask:255.255.255.0 + * BROADCAST MULTICAST MTU:1500 Metric:1 + * RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + * TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + * collisions:0 txqueuelen:1000 + * RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) + * \endcode + * \n + * The IP address 10.0.0.1 is already set in the shell script mentioned above.\n + * \n + * Use your favorite text editor to edit "loadDriver.sh".: + * - Example: + * \code vi loadDriver.sh \endcode + * - Or: + * \code gedit loadDriver.sh \endcode + * + * Scroll down to the StartEthernetUsb function, you will find the ifconfig statement which sets the + * IP address to 10.0.0.1. \n + * + * You have to adapt the address in this way, so it is unique in the whole network. This may be done + * by incrementing the last number with every new target (10.0.$i.1). + * + * \code + * #ETHERNET + * SetEthChannel $i ep82 rx 2048 + * SetEthChannel $i ep09 tx 2048 10.0.$i.1 + * \endcode + * \n + * \note Do this step for any client.\n + * + * \subsection Appendix Appendix: + * Timing sequence of NetworkManager startup: + * NetworkManager startup sequence\n + */ +/*----------------------------------------------------------*/ + + + +#endif //DOXYGEN_START_PAGE_H diff --git a/Src/IP/MostIpc.cpp b/Src/IP/MostIpc.cpp new file mode 100644 index 0000000..86e7341 --- /dev/null +++ b/Src/IP/MostIpc.cpp @@ -0,0 +1,510 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Console.h" +#include "MostIpc.h" + + +#define TCP_BUFFER_SIZE (1024*1024) + +CMostIpc::CMostIpc( int port, bool server ) : isServer( server ), allowThreadRun( true ), + udpRxThreadIsRunning( false ), txThreadIsRunning (false), acceptThreadIsRunning( false ) +{ + if( isServer && 0 >= port ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Server port must be greater than 0!"RESETCOLOR"\n" ); + return; + } + pthread_mutex_init( &critical_mutex_tcp, NULL ); + sem_init( &semTx, 0, 0 ); //Mutex, initialized to 0 => sem_wait will block instantly + + struct sockaddr_in listenAddress; + /* Receive address configuration */ + memset( &listenAddress, 0, sizeof( listenAddress ) ); + listenAddress.sin_family = AF_INET; + listenAddress.sin_addr.s_addr = htonl( INADDR_ANY ); + listenAddress.sin_port = htons( port ); + + if( 0 > ( udpSock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Failed to create UDP socket"RESETCOLOR"\n" ); + return; + } + if( 0 > ( tcpSock = socket( PF_INET, SOCK_STREAM, 0 ) ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Failed to create TCP socket"RESETCOLOR"\n" ); + return; + } + + //Avoid hanging socket after killing application, only works with Linux kernel > 3.9 +#if defined (SO_REUSEADDR) && defined (SO_REUSEPORT) + int one = 1; + setsockopt(udpSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one)); + setsockopt(tcpSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one)); +#endif + + // this call is what allows broadcast UDP packets to be sent: + int enableSocketFlag = 1; + if( 0 > setsockopt( udpSock, SOL_SOCKET, SO_BROADCAST, &enableSocketFlag, sizeof( enableSocketFlag ) ) ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostIpc: setsockopt (SO_BROADCAST) failed, tried to set to: %d"RESETCOLOR"\n", enableSocketFlag ); + } + if( 0 < port ) + { + if( 0 > bind( udpSock, ( struct sockaddr * )&listenAddress, sizeof listenAddress ) ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to bind UDP socket"RESETCOLOR"\n" ); + } + if( 0 > bind( tcpSock, ( struct sockaddr * )&listenAddress, sizeof listenAddress ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Failed to bind TCP socket"RESETCOLOR"\n" ); + } + } + if( isServer ) + { + pthread_t acceptThread; + pthread_create( &acceptThread, NULL, CMostIpc::TcpAcceptThread, ( void * )this ); + } + pthread_t udpRxThread, txThread; + pthread_create( &udpRxThread, NULL, CMostIpc::UdpWorkerRXThread, ( void * )this ); + pthread_create( &txThread, NULL, CMostIpc::TXThread, ( void * )this ); +} + +CMostIpc::~CMostIpc() +{ + allowThreadRun = false; + close( udpSock ); + close( tcpSock ); + sem_post(&semTx); + int32_t timeout = 100; + while( ( udpRxThreadIsRunning || txThreadIsRunning || acceptThreadIsRunning ) && ( --timeout > 0 ) ) + usleep( 10000 ); + pthread_mutex_lock( &critical_mutex_tcp ); + for( uint32_t i = 0; i < m_TcpClientConnections.Size(); i++ ) + { + TcpClientInfo_t *var = m_TcpClientConnections[i]; + if( NULL != var ) + { + if( var->clientSock >= 0 ) + close( var->clientSock ); + var->clientSock = -1; + } + } + m_TcpClientConnections.RemoveAll(false); + pthread_mutex_unlock( &critical_mutex_tcp ); +} + +void CMostIpc::SendMsg( CMostMsgTx *Msg ) +{ + if( NULL != Msg ) + { + Msg->AddReference(); + m_MsgTxQueue.PushBack( Msg ); + sem_post(&semTx); + } +} + +void CMostIpc::RegisterMessageHandler( CMsgFilter *Filter ) +{ + m_MsgFilterQueue.PushBack( Filter ); +} + +void CMostIpc::UnregisterMessageHandler( CMsgFilter *Filter ) +{ + m_MsgFilterQueue.Remove(Filter); +} + +void *CMostIpc::TcpWorkerThread( void *pInst ) +{ + bool allowWorker = true; + TcpClientInfo_t *clientInfo = ( TcpClientInfo_t * )pInst; + if( NULL == clientInfo || NULL == clientInfo->ipc || 0 > clientInfo->clientSock ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpWorkerThread: Invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + uint8_t *buff = ( uint8_t * )malloc( TCP_BUFFER_SIZE ); + if( NULL == buff ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpWorkerThread: Failed to allocate buffer"RESETCOLOR"\n" ); + return NULL; + } + + ConsolePrintf( PRIO_MEDIUM, "TcpWorkerThread: Start for IP:%s, Port:%d\n", clientInfo->clientIP, clientInfo->clientPort ); + + CMsgAddr *adr = new CMsgAddr( clientInfo->clientIP, clientInfo->clientPort, IpcTcp_V2_0 ); + CMostMsg *msg = NULL; + + while( clientInfo->ipc->allowThreadRun && allowWorker ) + { + int nRead; + if( 0 >= ( nRead = read( clientInfo->clientSock, buff, TCP_BUFFER_SIZE ) ) ) + { + if( clientInfo->ipc->allowThreadRun ) + { + if( 0 == nRead ) + ConsolePrintf( PRIO_MEDIUM, GREEN"TcpWorkerThread: TCP Client disconnected (IP:%s)"RESETCOLOR"\n", + clientInfo->clientIP ); + else + ConsolePrintf( PRIO_ERROR, YELLOW"TcpWorkerThread: Read error (return=%d), aborting .."RESETCOLOR"\n", nRead ); + } + allowWorker = false; + break; + } + for( int i = 0; i < nRead; i++ ) + { + if( NULL == msg ) + msg = new CMostMsg(); + if( !msg->Parse( buff[i] ) ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpWorkerThread: Parse error, aborting.."RESETCOLOR"\n" ); + allowWorker = false; + break; + } + if( msg->IsValid() ) + { + clientInfo->ipc->OnMsgReceived( adr, msg ); + msg->RemoveReference(); + msg = NULL; + } + } + } + ConsolePrintf( PRIO_LOW, "TcpWorkerThread: End\n" ); + if( clientInfo->clientSock >= 0 ) + close( clientInfo->clientSock ); + + pthread_mutex_t *pMutex = &clientInfo->ipc->critical_mutex_tcp; + pthread_mutex_lock( pMutex ); + clientInfo->ipc->m_TcpClientConnections.Remove(clientInfo); + free( clientInfo ); + pthread_mutex_unlock( pMutex ); + + adr->RemoveReference(); + free( buff ); + return NULL; +} + +void *CMostIpc::TcpAcceptThread( void *pInst ) +{ + CMostIpc *ipc = ( CMostIpc * )pInst; + if( NULL == ipc || 0 > ipc->tcpSock ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpAcceptThread was called with invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + + ConsolePrintf( PRIO_LOW, "TcpAcceptThread starts\n" ); + ipc->acceptThreadIsRunning = true; + while( ipc->allowThreadRun ) + { + listen( ipc->tcpSock, 5 ); + socklen_t size = sizeof( struct sockaddr_in ); + TcpClientInfo_t *var = ( TcpClientInfo_t * )calloc( 1, sizeof( TcpClientInfo_t ) ); + if( NULL == var ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to allocate memory in TCP accept Thread, aborting.."RESETCOLOR"\n" ); + break; + } + struct sockaddr_in clientAddr; + var->clientSock = accept( ipc->tcpSock, ( struct sockaddr * )&clientAddr, &size ); + if( !ipc->allowThreadRun ) + break; + if( -1 == var->clientSock ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to accept connection"RESETCOLOR"\n" ); + free( var ); + continue; + } + var->ipc = ipc; + inet_ntop( AF_INET, ( struct sockaddr_in * )&clientAddr.sin_addr, var->clientIP, sizeof ( var->clientIP ) ); + var->clientPort = ntohs( clientAddr.sin_port ); + ConsolePrintf( PRIO_MEDIUM, GREEN"TCP Client connected: IP=%s, Port=%d"RESETCOLOR"\n", + var->clientIP, var->clientPort ); + pthread_mutex_lock( &var->ipc->critical_mutex_tcp ); + var->ipc->m_TcpClientConnections.PushBack( var ); + pthread_mutex_unlock( &var->ipc->critical_mutex_tcp ); + pthread_create( &var->workerThread, NULL, CMostIpc::TcpWorkerThread, ( void * )var ); + } + ConsolePrintf( PRIO_LOW, "TcpAcceptThread ends\n" ); + close( ipc->tcpSock ); + ipc->acceptThreadIsRunning = false; + return NULL; +} + +void CMostIpc::OnMsgReceived( CMsgAddr *Addr, CMostMsg *msg ) +{ + if( NULL == Addr || NULL == msg ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostIpc::OnMsgReceived was called with invalid parameters"RESETCOLOR"\n" ); + return; + } + ConsolePrintf( PRIO_LOW, "Received MOST-IPC message IP=%s, Protocol:%s, "\ + "FBlock:0x%X, Func:0x%X, OP:0x%X, PL:%d Bytes\n", Addr->GetInetAddress(), ( Addr->GetProtocol() == + IpcUdp_V2_0 ? "UDP" : "TCP" ), msg->GetFBlock(), msg->GetFunc(), msg->GetOpType(), msg->GetPayloadLen() ); + + for( uint32_t i = 0; i < m_MsgFilterQueue.Size(); i++ ) + { + m_MsgFilterQueue[i]->Filter( Addr, msg ); + } +} + +bool CMostIpc::SendUdp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ) +{ + struct sockaddr_in destination; + memset( &destination, 0, sizeof ( destination ) ); + destination.sin_family = AF_INET; + destination.sin_addr.s_addr = inet_addr( ipAddress ); + destination.sin_port = htons( port ); + ssize_t bytesSent = sendto( udpSock, buffer, bufferLen, + 0, ( struct sockaddr * )&destination, sizeof ( destination ) ); + return ( ( uint32_t )bytesSent == bufferLen ); +} + +bool CMostIpc::SendTcp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ) +{ + bool success = false; + TcpClientInfo_t *tcp = NULL; + + //Find the correct client connection + pthread_mutex_lock( &critical_mutex_tcp ); + for( uint32_t i = 0; i < m_TcpClientConnections.Size(); i++ ) + { + TcpClientInfo_t *t = m_TcpClientConnections[i]; + if( NULL != t && t->clientPort == port && 0 == strcmp( t->clientIP, ipAddress ) ) + { + tcp = t; + break; + } + } + //There is no connection matching to the given address and port, try to connect if we are not master + if( !isServer && NULL == tcp ) + { + TcpClientInfo_t *var = ( TcpClientInfo_t * )calloc( 1, sizeof( TcpClientInfo_t ) ); + if( NULL != var ) + { + var->ipc = this; + var->clientPort = port; + var->clientSock = tcpSock; + strncpy( var->clientIP, ipAddress, sizeof( var->clientIP ) ); + + struct hostent *hostp; + hostp = gethostbyname( var->clientIP ); + if( NULL != hostp ) + { + struct sockaddr_in addr; + memset( &addr, 0x00, sizeof( struct sockaddr_in ) ); + addr.sin_family = AF_INET; + addr.sin_port = htons( var->clientPort ); + memcpy( &addr.sin_addr, hostp->h_addr, sizeof( addr.sin_addr ) ); + if( 0 == connect( tcpSock, ( struct sockaddr * )&addr, sizeof( addr ) ) ) + { + tcp = var; + m_TcpClientConnections.PushBack( tcp ); + pthread_create( &tcp->workerThread, NULL, CMostIpc::TcpWorkerThread, ( void * )tcp ); + ConsolePrintf( PRIO_MEDIUM, GREEN"Connect to TCP host succeeded: IP=%s Port=%d"RESETCOLOR"\n", tcp->clientIP, + tcp->clientPort ); + + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Could not connect to host: '%s'"RESETCOLOR"\n", var->clientIP ); + free( var ); + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Could not find host: '%s'"RESETCOLOR"\n", var->clientIP ); + free( var ); + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Failed to allocate memory in SendTcp method"RESETCOLOR"\n" ); + } + } + + if( NULL != tcp ) + { + int result; + uint32_t written = 0; + success = true; + while( written < bufferLen ) + { + result = write( tcp->clientSock, &buffer[written], bufferLen - written ); + if( result < 0 ) + { + ConsolePrintf( PRIO_ERROR, RED"SendTcp: Write error"RESETCOLOR"\n" ); + success = false; + break; + } + else if( result == 0 ) + { + //Socket is non blocking, sleep to avoid 100% CPU load + usleep( 1000 ); + } + else + { + written += result; + } + } + pthread_mutex_unlock( &critical_mutex_tcp ); + } + return success; +} + +void *CMostIpc::UdpWorkerRXThread( void *pInst ) +{ + CMostIpc *ipc = ( CMostIpc * )pInst; + if( NULL == ipc || 0 > ipc->udpSock ) + { + ConsolePrintf( PRIO_ERROR, RED"UdpWorkerRXThread was called with invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + ipc->udpRxThreadIsRunning = true; + while( ipc->allowThreadRun ) + { + //Receive + if( ipc->allowThreadRun ) + { + uint8_t tempBuffer[8*1024]; + char remoteIP[INET6_ADDRSTRLEN]; + uint32_t port; + struct sockaddr_in fromAddr; + socklen_t fromAddrLen = sizeof( fromAddr ); + ssize_t receivedLength; + receivedLength = recvfrom + ( ipc->udpSock, tempBuffer, sizeof( tempBuffer ), + 0, ( struct sockaddr * )&fromAddr, &fromAddrLen ); + if( receivedLength > 0 ) + { + inet_ntop( AF_INET, ( struct sockaddr_in * )&fromAddr.sin_addr, remoteIP, sizeof( remoteIP ) ); + port = ntohs( fromAddr.sin_port ); + CMsgAddr *adr = new CMsgAddr( remoteIP, port ); + CMostMsg *msg = new CMostMsg(); + for( int32_t i = 0; i < receivedLength; i++ ) + { + if( !msg->Parse( tempBuffer[i] ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc UDP received unparsable message"RESETCOLOR"\n" ); + break; + } + } + if( msg->IsValid() ) + ipc->OnMsgReceived( adr, msg ); + adr->RemoveReference(); + msg->RemoveReference(); + } + } + } + ipc->udpRxThreadIsRunning = false; + return NULL; +} + +void *CMostIpc::TXThread( void *pInst ) +{ + CMostIpc *ipc = ( CMostIpc * )pInst; + if( NULL == ipc || 0 > ipc->udpSock ) + { + ConsolePrintf( PRIO_ERROR, RED"TXThread was called with invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + ipc->txThreadIsRunning = true; + + while( ipc->allowThreadRun ) + { + sem_wait(&ipc->semTx); + if (!ipc->allowThreadRun) + break; + CMostMsgTx *MsgTx = ipc->m_MsgTxQueue.PopFront(); + assert (NULL != MsgTx); + if( NULL != MsgTx ) + { + uint8_t *tempBuffer = NULL; + uint32_t bytesUsed = MsgTx->ToByteArray( &tempBuffer ); + if( NULL == tempBuffer || 0 == bytesUsed ) + { + ConsolePrintf( PRIO_ERROR, RED"MOST-IP: Failed to serialize message"RESETCOLOR"\n" ); + if (NULL != tempBuffer) + free(tempBuffer); + MsgTx->MsgSent( false ); + continue; + } + CMsgAddr *addr = MsgTx->GetAddr(); + if( 0 != bytesUsed && NULL != addr ) + { + char *ipAddress = addr->GetInetAddress(); + uint32_t port = addr->GetPort(); + if( NULL != ipAddress ) + { + bool success = false; + switch( addr->GetProtocol() ) + { + case IpcUdp_V2_0: + success = ipc->SendUdp( ipAddress, port, tempBuffer, bytesUsed ); + break; + case IpcTcp_V2_0: + success = ipc->SendTcp( ipAddress, port, tempBuffer, bytesUsed ); + break; + default: + ConsolePrintf( PRIO_ERROR, RED"MOST-IP: Unknown Protocol"RESETCOLOR"\n" ); + break; + } + MsgTx->MsgSent( success ); + } + else + { + ConsolePrintf( PRIO_ERROR, RED"MOST-IP: Can not resolve IP address"RESETCOLOR"\n" ); + MsgTx->MsgSent( false ); + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Error while sending in MOST IPC."RESETCOLOR"\n" ); + MsgTx->MsgSent( false ); + } + if (NULL != tempBuffer) + free( tempBuffer ); + MsgTx->RemoveReference(); + } + } + ipc->txThreadIsRunning = false; + return NULL; +} + diff --git a/Src/IP/MostIpc.h b/Src/IP/MostIpc.h new file mode 100644 index 0000000..0f8596d --- /dev/null +++ b/Src/IP/MostIpc.h @@ -0,0 +1,164 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMostIpc class. + */ +/*----------------------------------------------------------*/ +#ifndef IPC_H +#define IPC_H + +#include +#include +#include +#include "SafeVector.h" +#include "MsgAddr.h" +#include "MostMsgTx.h" +#include "MsgAddr.h" +#include "MsgFilter.h" + +class CMostIpc; + +/*! \cond PRIVATE */ +typedef struct +{ + pthread_t workerThread; + CMostIpc *ipc; + int clientSock; + char clientIP[46]; + uint32_t clientPort; +} TcpClientInfo_t; +/*! \endcond */ + +/*----------------------------------------------------------*/ +/*! + * \brief Inter Process Communication + * 1. sends messages using MOST protocol over UDP + * 2. receives messages and forwards them to their + * handlers + */ +/*----------------------------------------------------------*/ +class CMostIpc +{ +private: + bool isServer; + bool allowThreadRun; + bool udpRxThreadIsRunning; + bool txThreadIsRunning; + bool acceptThreadIsRunning; + CSafeVector m_MsgTxQueue; + CSafeVector m_MsgFilterQueue; + CSafeVector m_TcpClientConnections; + pthread_mutex_t critical_mutex_rx; + pthread_mutex_t critical_mutex_tcp; + sem_t semTx; + int udpSock; + int tcpSock; + + /*----------------------------------------------------------*/ + /*! \brief TCP accept thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *TcpAcceptThread( void *pInst ); + + /*----------------------------------------------------------*/ + /*! \brief TCP read / write thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *TcpWorkerThread( void *pInst ); + + + /*----------------------------------------------------------*/ + /*! \brief UDP receiving thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *UdpWorkerRXThread( void *pInst ); + + /*----------------------------------------------------------*/ + /*! \brief Sending thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *TXThread( void *pInst ); + + /*----------------------------------------------------------*/ + /*! \brief handle a received message + */ + /*----------------------------------------------------------*/ + void OnMsgReceived( CMsgAddr *Addr, CMostMsg *msg ); + + bool SendUdp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ); + bool SendTcp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ); + +public: + + + /*----------------------------------------------------------*/ + /*! \brief constructs Inter Process Communication, which acts as Server + * \param port - If set to server by isServer=true, this value is the server port which the UDP and TCP sockets are listening. + * If set to client by isServer=false, this is the client socket port. If set to 0 or below, a random port will be chosen. + * Otherwise the given port will be used as source port of the connection. The server always can handle random and fixed client port connections. + * \param isServer - true, if this component shall act as "the" server. false, if this component shall act as one of many clients. + */ + /*----------------------------------------------------------*/ + CMostIpc( int port, bool isServer ); + + /*----------------------------------------------------------*/ + /*! \brief Deletes every resources used by Inter Process Communication + */ + /*----------------------------------------------------------*/ + ~CMostIpc(); + + + /*----------------------------------------------------------*/ + /*! \brief send a MOST message + */ + /*----------------------------------------------------------*/ + void SendMsg( CMostMsgTx *Msg ); + + + + + /*----------------------------------------------------------*/ + /*! \brief adds a message handler + */ + /*----------------------------------------------------------*/ + void RegisterMessageHandler( CMsgFilter *Filter ); + + + + + /*----------------------------------------------------------*/ + /*! \brief removes a message handler + */ + /*----------------------------------------------------------*/ + void UnregisterMessageHandler( CMsgFilter *Filter ); + +}; + +#endif //IPC_H + diff --git a/Src/IP/MostMsg.cpp b/Src/IP/MostMsg.cpp new file mode 100644 index 0000000..3ca043f --- /dev/null +++ b/Src/IP/MostMsg.cpp @@ -0,0 +1,405 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "Console.h" +#include "MostMsg.h" +#include + +static uint32_t crc32_tab[] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static uint32_t CalcCRC32( const uint8_t *p, uint32_t size ) +{ + uint32_t crc = ~0U; + + while( size-- ) + crc = crc32_tab[( crc ^ *p++ ) & 0xFF] ^ ( crc >> 8 ); + + return crc ^ ~0U; +} + + +CMostMsg::CMostMsg() : m_nIsValid( false ), m_nRefCount( 1 ), m_nFBlock( 0 ), m_nFunc( 0 ), m_nInst( 0 ), + m_nOpType( 0 ), m_nPayloadLen( 0 ), m_Payload( NULL ), m_nParsePos( 0 ), + m_nHeaderCrc( 0 ), m_nPayloadCrc( 0 ) +{ +} + +CMostMsg::CMostMsg( uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, + const uint8_t *Payload ) : m_nIsValid( true ), m_nRefCount( 1 ), m_Payload( NULL ), + m_nParsePos( 0xFFFFFFFF ), m_nHeaderCrc( 0 ), m_nPayloadCrc( 0 ) +{ + m_nFBlock = nFBlock; + m_nInst = nInst; + m_nFunc = nFunc; + m_nOpType = nOpType; + m_nPayloadLen = nPayloadLen; + if( nPayloadLen > 0 ) + { + m_Payload = ( uint8_t * )malloc( m_nPayloadLen ); + if( NULL == m_Payload ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg Constructor failed to allocated %d bytes of memory"RESETCOLOR"\n", m_nPayloadLen ); + m_nIsValid = false; + m_nPayloadLen = 0; + return; + } + memcpy( m_Payload, Payload, nPayloadLen ); + } +} + +CMostMsg::~CMostMsg() +{ + m_nIsValid = false; + m_nPayloadLen = 0; + if( NULL != m_Payload ) + { + free( m_Payload ); + m_Payload = NULL; + } +} + +void CMostMsg::AddReference() +{ + ++m_nRefCount; +} + +void CMostMsg::RemoveReference() +{ + if( 0 == --m_nRefCount ) + delete this; +} + +uint32_t CMostMsg::ToByteArray( uint8_t **ppBuffer ) +{ + if( NULL == ppBuffer ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::ToByteArray ppBuffer is NULL < %d"RESETCOLOR"\n" ); + return 0; + } + uint32_t serializedLen = 20; + if( 0 != m_nPayloadLen ) + serializedLen += m_nPayloadLen + 4; //If there is payload, it will be CRC checked + uint8_t *b = ( uint8_t * )malloc( serializedLen ); + if( NULL == b ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::ToByteArray failed to allocate %d bytes"RESETCOLOR"\n", ( m_nPayloadLen + 24 ) ); + *ppBuffer = NULL; + return 0; + } + *ppBuffer = b; + b[0] = ( uint8_t )0x49; + b[1] = ( uint8_t )0x72; + b[2] = ( uint8_t )0x16; + b[3] = ( uint8_t )0x25; + b[4] = ( uint8_t )( m_nFBlock & 0xFF ); + b[5] = ( uint8_t )( m_nInst & 0xFF ); + b[6] = ( uint8_t )( ( m_nFunc >> 8 ) & 0xFF ); + b[7] = ( uint8_t )( ( m_nFunc )&0xFF ); + b[8] = ( uint8_t )( m_nOpType ); + b[9] = ( uint8_t )0x0; + b[10] = ( uint8_t )0x0; + b[11] = ( uint8_t )0x0; + b[12] = ( uint8_t )( ( m_nPayloadLen >> 24 ) & 0xFF ); + b[13] = ( uint8_t )( ( m_nPayloadLen >> 16 ) & 0xFF ); + b[14] = ( uint8_t )( ( m_nPayloadLen >> 8 ) & 0xFF ); + b[15] = ( uint8_t )( m_nPayloadLen & 0xFF ); + + uint32_t headerCrc = CalcCRC32( b, 16 ); + + b[16] = ( uint8_t )( ( headerCrc >> 24 ) & 0xFF ); + b[17] = ( uint8_t )( ( headerCrc >> 16 ) & 0xFF ); + b[18] = ( uint8_t )( ( headerCrc >> 8 ) & 0xFF ); + b[19] = ( uint8_t )( headerCrc & 0xFF ); + + if( 0 != m_nPayloadLen ) + { + for( uint32_t i = 0; i < m_nPayloadLen; i++ ) + b[20 + i] = m_Payload[i]; + + uint32_t payloadCrc = CalcCRC32( &b[20], m_nPayloadLen ); + + b[20 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 24 ) & 0xFF ); + b[21 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 16 ) & 0xFF ); + b[22 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 8 ) & 0xFF ); + b[23 + m_nPayloadLen] = ( uint8_t )( payloadCrc & 0xFF ); + } + return serializedLen; +} + +bool CMostMsg::Parse( uint8_t receivedByte ) +{ + if( 0xFFFFFFFF == m_nParsePos ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::Parse was called, but the parse state is invalid"RESETCOLOR"\n" ); + return false; + } + bool valid = true; + if( m_nParsePos <= 19 ) + { + if( m_nParsePos <= 15 ) + m_zHeader[m_nParsePos] = receivedByte; + + //Check Header + switch( m_nParsePos ) + { + case 0: + if( 0x49 != receivedByte ) + valid = false; + break; + case 1: + if( 0x72 != receivedByte ) + valid = false; + break; + case 2: + if( 0x16 != receivedByte ) + valid = false; + break; + case 3: + if( 0x25 != receivedByte ) + valid = false; + break; + case 4: + m_nFBlock = receivedByte; + break; + case 5: + m_nInst = receivedByte; + break; + case 6: + m_nFunc = receivedByte << 8; + break; + case 7: + m_nFunc += receivedByte; + break; + case 8: + m_nOpType = receivedByte; + break; + case 9: + case 10: + case 11: + //Reserved for future use cases + break; + case 12: + m_nPayloadLen = receivedByte << 24; + break; + case 13: + m_nPayloadLen += receivedByte << 16; + break; + case 14: + m_nPayloadLen += receivedByte << 8; + break; + case 15: + m_nPayloadLen += receivedByte; + break; + case 16: + m_nHeaderCrc = receivedByte << 24; + break; + case 17: + m_nHeaderCrc += receivedByte << 16; + break; + case 18: + m_nHeaderCrc += receivedByte << 8; + break; + case 19: + m_nHeaderCrc += receivedByte; + if( m_nHeaderCrc == CalcCRC32( m_zHeader, 16 ) ) + { + if( 0 != m_nPayloadLen ) + { + //Continue to parse the payload + m_Payload = ( uint8_t * )malloc( m_nPayloadLen ); + if( NULL == m_Payload ) + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, could not allocate %d bytes of memory"RESETCOLOR + "\n", m_nPayloadLen ); + valid = false; + m_nPayloadLen = 0; + } + } + else + { + //No payload, we are finished + m_nIsValid = true; + m_nParsePos = 0xFFFFFFFF - 1; + } + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol error, header CRC does not match"RESETCOLOR"\n" ); + valid = false; + } + break; + } + } + else + { + if( m_nParsePos < ( m_nPayloadLen + 20 ) ) + { + //Check Payload and store it + if( NULL != m_Payload ) + { + m_Payload[m_nParsePos - 20] = receivedByte; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, payload buffer is NULL"RESETCOLOR"\n" ); + valid = false; + m_nPayloadLen = 0; + } + } + else + { + //Finally check CRC of payload + uint32_t crcState = m_nParsePos - m_nPayloadLen - 20; + switch( crcState ) + { + case 0: + m_nPayloadCrc = receivedByte << 24; + break; + case 1: + m_nPayloadCrc += receivedByte << 16; + break; + case 2: + m_nPayloadCrc += receivedByte << 8; + break; + case 3: + m_nPayloadCrc += receivedByte; + if( m_nPayloadCrc == CalcCRC32( m_Payload, m_nPayloadLen ) ) + { + //Payload is successfully received + m_nIsValid = true; + m_nParsePos = 0xFFFFFFFF - 1; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, Payload CRC mismatch"RESETCOLOR"\n" ); + valid = false; + } + break; + default: + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, Payload CRC state is out of range"RESETCOLOR"\n" ); + valid = false; + } + } + } + + if( valid ) + { + ++m_nParsePos; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::Parse failed at parse position %d"RESETCOLOR"\n", m_nParsePos ); + m_nParsePos = 0xFFFFFFFF; + } + return valid; +} + +bool CMostMsg::IsValid() +{ + return m_nIsValid; +} + +uint32_t CMostMsg::GetFBlock() +{ + return m_nFBlock; +} + +uint32_t CMostMsg::GetFunc() +{ + return m_nFunc; +} + +uint32_t CMostMsg::GetInst() +{ + return m_nInst; +} + +uint8_t CMostMsg::GetOpType() +{ + return m_nOpType; +} + +uint8_t *CMostMsg::GetPayload() +{ + return m_Payload; +} + +uint32_t CMostMsg::GetPayloadLen() +{ + return m_nPayloadLen; +} diff --git a/Src/IP/MostMsg.h b/Src/IP/MostMsg.h new file mode 100644 index 0000000..fb4bdd8 --- /dev/null +++ b/Src/IP/MostMsg.h @@ -0,0 +1,184 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMostMsg class. + */ +/*----------------------------------------------------------*/ +#ifndef MOSTMSG_H +#define MOSTMSG_H + +#include +#include +#include + + +/*----------------------------------------------------------*/ +/*! + * \brief class holding a MOST message, it support serializing and deserializing + * \note Serialize-Protocol is:
+ * p[0] = 0x49 (Start)
+ * p[1] = 0x72 (Start)
+ * p[2] = 0x16 (Start)
+ * p[3] = 0x25 (Start)
+ * p[4] = FBlock Identifier
+ * p[5] = Instance Identifier
+ * p[6] = Function Identifier << 8
+ * p[7] = Function Identifier << 0
+ * p[8] = OP Type
+ * p[9] = 0x0 (Reserved)
+ * p[10] = 0x0 (Reserved)
+ * p[11] = 0x0 (Reserved)
+ * p[12] = Payload Length << 24
+ * p[13] = Payload Length << 16
+ * p[14] = Payload Length << 8
+ * p[15] = Payload Length << 0
+ * p[16] = Header CRC32(p[0] - p[15]) << 24
+ * p[17] = Header CRC32(p[0] - p[15]) << 16
+ * p[18] = Header CRC32(p[0] - p[15]) << 8
+ * p[19] = Header CRC32(p[0] - p[15]) << 0
+ * p[20] - p[n] = Payload
+ * p[n+1] = Payload CRC32(p[20] - p[n]) << 24
+ * p[n+2] = Payload CRC32(p[20] - p[n]) << 16
+ * p[n+3] = Payload CRC32(p[20] - p[n]) << 8
+ * p[n+4] = Payload CRC32(p[20] - p[n]) << 0
+ */ + +/*----------------------------------------------------------*/ +class CMostMsg +{ +private: + bool m_nIsValid; + int32_t m_nRefCount; + uint32_t m_nFBlock; + uint32_t m_nFunc; + uint32_t m_nInst; + uint8_t m_nOpType; + uint32_t m_nPayloadLen; + uint8_t *m_Payload; + uint32_t m_nParsePos; + uint32_t m_nHeaderCrc; + uint32_t m_nPayloadCrc; + uint8_t m_zHeader[16]; + +protected: + /*----------------------------------------------------------*/ + /*! \brief Default Destructor + */ + /*----------------------------------------------------------*/ + virtual ~CMostMsg(); + +public: + + static const uint8_t OP_SET = 0x0; + static const uint8_t OP_GET = 0x1; + static const uint8_t OP_SETGET = 0x2; + static const uint8_t OP_INC = 0x3; + static const uint8_t OP_DEC = 0x4; + static const uint8_t OP_GETINTERFACE = 0x5; + static const uint8_t OP_STATUS = 0xC; + static const uint8_t OP_INTERFACE = 0xE; + static const uint8_t OP_ERROR = 0xF; + static const uint8_t OP_START = 0x0; + static const uint8_t OP_ABORT = 0x1; + static const uint8_t OP_STARTRESULT = 0x2; + static const uint8_t OP_STARTRESULTACK = 0x6; + static const uint8_t OP_ABORTACK = 0x7; + static const uint8_t OP_STARTACK = 0x8; + static const uint8_t OP_ERRORACK = 0x9; + static const uint8_t OP_PROCESSINGACK = 0xA; + static const uint8_t OP_PROCESSING = 0xB; + static const uint8_t OP_RESULT = 0xC; + static const uint8_t OP_RESULTACK = 0xD; + static const uint8_t OP_REPORTS = 0x9; + + + /*----------------------------------------------------------*/ + /*! \brief constructs a new message, which is initial not valid. + * \note It has to fed by the Parse method until IsValid method reports true. + */ + /*----------------------------------------------------------*/ + CMostMsg(); + + + /*----------------------------------------------------------*/ + /*! \brief constructs a new message which is fully set up and valid + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + CMostMsg( uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, + const uint8_t *Payload ); + + /*----------------------------------------------------------*/ + /*! \brief Increments the reference counter + */ + /*----------------------------------------------------------*/ + void AddReference(); + + /*----------------------------------------------------------*/ + /*! \brief Decrements the reference counter, if the value reaches 0, this object destroys it self. + */ + /*----------------------------------------------------------*/ + void RemoveReference(); + + /*----------------------------------------------------------*/ + /*! \brief Fills out an empty CMostMsg (created with default constructor) + * \return true, if parsing was valid. false, parser error, no way to continue. + * \note Check with IsValid method if parsing is finished. + */ + /*----------------------------------------------------------*/ + bool Parse( uint8_t receivedByte ); + + + + /*----------------------------------------------------------*/ + /*! \brief serializes a message to an byte array + * \note The byte array will be created inside this method and has to be freed after usage! + * \return The bytes used inside the given buffer. + */ + /*----------------------------------------------------------*/ + uint32_t ToByteArray( uint8_t **ppBuffer ); + + + bool IsValid(); + + uint32_t GetFBlock(); + + uint32_t GetFunc(); + + uint32_t GetInst(); + + uint8_t GetOpType(); + + uint8_t *GetPayload(); + + uint32_t GetPayloadLen(); +}; + + +#endif //MOSTMSG_H diff --git a/Src/IP/MostMsgTx.cpp b/Src/IP/MostMsgTx.cpp new file mode 100644 index 0000000..d3c5864 --- /dev/null +++ b/Src/IP/MostMsgTx.cpp @@ -0,0 +1,86 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "MostMsgTx.h" + +CMostMsgTx::CMostMsgTx( CMsgAddr *Addr, uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, + uint32_t nPayloadLen, const uint8_t *Payload, uint32_t nTimeoutMs, OnMessageSent_t MessageSent, + void *UserContext ) : CMostMsg( nFBlock, nInst, nFunc, nOpType, nPayloadLen, Payload ) +{ + if( NULL != Addr ) + { + Addr->AddReference(); + m_Addr = Addr; + } + m_nTimeoutMs = nTimeoutMs; + m_MessageSent = MessageSent; + m_UserContext = UserContext; +} + +CMostMsgTx::~CMostMsgTx() +{ + if( NULL != m_Addr ) + { + m_Addr->RemoveReference(); + m_Addr = NULL; + } +} + + + +/*----------------------------------------------------------*/ +/*! \brief calls message handler with the send result +*/ + +/*----------------------------------------------------------*/ +void CMostMsgTx::MsgSent( bool bSuccess ) +{ + if( NULL != m_MessageSent ) + m_MessageSent( bSuccess, this ); +} + + + + +/*----------------------------------------------------------*/ +/*! \brief get target address +*/ + +/*----------------------------------------------------------*/ +CMsgAddr *CMostMsgTx::GetAddr() +{ + return m_Addr; +} + + + + +/*----------------------------------------------------------*/ +/*! \brief get user context +*/ + +/*----------------------------------------------------------*/ +void *CMostMsgTx::GetUserContext() +{ + return m_UserContext; +} diff --git a/Src/IP/MostMsgTx.h b/Src/IP/MostMsgTx.h new file mode 100644 index 0000000..00736ae --- /dev/null +++ b/Src/IP/MostMsgTx.h @@ -0,0 +1,96 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMostMsgTx class. + */ +/*----------------------------------------------------------*/ +#ifndef MOSTMSGTX_H +#define MOSTMSGTX_H + +#include +#include +#include "MostMsg.h" +#include "MsgAddr.h" + + + + +/*----------------------------------------------------------*/ +/*! +* \brief class holding a MOST message which needs to be +* sent +*/ + +/*----------------------------------------------------------*/ +class CMostMsgTx : public CMostMsg +{ +private: + typedef void ( *OnMessageSent_t )( bool bSucceeded, CMostMsgTx *Msg ); + CMsgAddr *m_Addr; + uint32_t m_nTimeoutMs; + OnMessageSent_t m_MessageSent; + void *m_UserContext; + +protected: + virtual ~CMostMsgTx(); + +public: + + + /*----------------------------------------------------------*/ + /*! \brief constructs a new message + * + * \param Addr - Target address + * \param nFBlock - Function Block ID + * \param nInst - Instance ID + * \param nFunc - Function ID + * \param nOpType - Operation + * \param nPayloadLen - length of payload in bytes + * \param Payload - Payload + * \param nTimeoutMs - Timeout for sending in Milliseconds + * \param MessageSentCB - Handler called, after message was sent + * \param UserContext - a class passed to the handler + */ + /*----------------------------------------------------------*/ + CMostMsgTx( CMsgAddr *Addr, uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, + uint32_t nPayloadLen, const uint8_t *Payload, uint32_t nTimeoutMs, OnMessageSent_t MessageSentCB, + void *UserContext ); + + + void MsgSent( bool bSuccess ); + + + + + CMsgAddr *GetAddr(); + + + + + void *GetUserContext(); +}; + + +#endif //MOSTMSGTX_H diff --git a/Src/IP/MsgAddr.cpp b/Src/IP/MsgAddr.cpp new file mode 100644 index 0000000..6c80222 --- /dev/null +++ b/Src/IP/MsgAddr.cpp @@ -0,0 +1,71 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include "MsgAddr.h" +#include "Console.h" + +CMsgAddr::CMsgAddr( const char *Addr, uint32_t nPort ) : m_nRefCount( 1 ), m_nPort( nPort ), + m_protocol( IpcUdp_V2_0 ) +{ + if( NULL != Addr ) + strncpy( m_IpAddr, Addr, sizeof( m_IpAddr ) ); +} + +CMsgAddr::CMsgAddr( const char *Addr, uint32_t nPort, IpcProtocol_t protocol ) : m_nRefCount( 1 ), m_nPort( nPort ), + m_protocol( protocol ) +{ + if( NULL != Addr ) + strncpy( m_IpAddr, Addr, sizeof( m_IpAddr ) ); +} + +CMsgAddr::~CMsgAddr() +{ +} + +void CMsgAddr::AddReference() +{ + ++m_nRefCount; +} + +void CMsgAddr::RemoveReference() +{ + if( 0 >= --m_nRefCount ) + delete this; +} + +char *CMsgAddr::GetInetAddress() +{ + return ( char * )m_IpAddr; +} + +uint32_t CMsgAddr::GetPort() +{ + return m_nPort; +} + +IpcProtocol_t CMsgAddr::GetProtocol() +{ + return m_protocol; +} diff --git a/Src/IP/MsgAddr.h b/Src/IP/MsgAddr.h new file mode 100644 index 0000000..9a3fa80 --- /dev/null +++ b/Src/IP/MsgAddr.h @@ -0,0 +1,73 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMsgAddr class. + */ +/*----------------------------------------------------------*/ +#ifndef MSGADDR_H +#define MSGADDR_H +#include + +typedef enum +{ + IpcUdp_V2_0 = 1, + IpcTcp_V2_0 = 2 +} IpcProtocol_t; + +/*----------------------------------------------------------*/ +/*! \brief Storage class, which holds IP address, IP port and MAC address. +*/ +/*----------------------------------------------------------*/ +class CMsgAddr +{ +private: + int32_t m_nRefCount; + char m_IpAddr[32]; + uint32_t m_nPort; + IpcProtocol_t m_protocol; + +public: + CMsgAddr( const char *Addr, uint32_t nPort ); + CMsgAddr( const char *Addr, uint32_t nPort, IpcProtocol_t protocol ); + ~CMsgAddr(); + + /*----------------------------------------------------------*/ + /*! \brief Increments the reference counter + */ + /*----------------------------------------------------------*/ + void AddReference(); + + /*----------------------------------------------------------*/ + /*! \brief Decrements the reference counter, if the value reaches 0, this object destroys it self. + */ + /*----------------------------------------------------------*/ + void RemoveReference(); + + char *GetInetAddress(); + uint32_t GetPort(); + IpcProtocol_t GetProtocol(); +}; + +#endif //MSGADDR_H diff --git a/Src/IP/MsgFilter.cpp b/Src/IP/MsgFilter.cpp new file mode 100644 index 0000000..f9684f4 --- /dev/null +++ b/Src/IP/MsgFilter.cpp @@ -0,0 +1,47 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "Console.h" +#include "MsgFilter.h" + +CMsgFilter::CMsgFilter( uint32_t nFBlock, uint32_t nFunc, uint8_t nOpType, OnMsg_t MsgHandler ) +{ + m_nFBlock = nFBlock; + m_nFunc = nFunc; + m_nOpType = nOpType; + m_MsgHandler = MsgHandler; +} + +void CMsgFilter::Filter( CMsgAddr *Addr, CMostMsg *Msg ) +{ + if( NULL == Addr || NULL == Msg ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMsgFilter::Filter was called with invalid parameters"RESETCOLOR"\n" ); + return; + } + if( NULL != m_MsgHandler ) + if( Msg->GetFBlock() == m_nFBlock && Msg->GetFunc() == m_nFunc + && Msg->GetOpType() == m_nOpType ) + m_MsgHandler( Addr, Msg ); +} diff --git a/Src/IP/MsgFilter.h b/Src/IP/MsgFilter.h new file mode 100644 index 0000000..72ef45f --- /dev/null +++ b/Src/IP/MsgFilter.h @@ -0,0 +1,72 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMsgFilter class. + */ +/*----------------------------------------------------------*/ +#ifndef MSGFILTER_H +#define MSGFILTER_H + +#include +#include "MsgAddr.h" +#include "MostMsg.h" + +typedef void ( *OnMsg_t )( CMsgAddr *Addr, CMostMsg *Msg ); + +/*----------------------------------------------------------*/ +/*! + * \brief filters messages and dispatches result to a + * handler + */ + +/*----------------------------------------------------------*/ +class CMsgFilter +{ + uint32_t m_nFBlock; + uint32_t m_nFunc; + uint8_t m_nOpType; + OnMsg_t m_MsgHandler; + +public: + + + /*----------------------------------------------------------*/ + /*! \brief construct a message filter + */ + /*----------------------------------------------------------*/ + CMsgFilter( uint32_t nFBlock, uint32_t nFunc, uint8_t nOpType, OnMsg_t MsgHandler ); + + + + + /*----------------------------------------------------------*/ + /*! \brief filter an incoming message + */ + /*----------------------------------------------------------*/ + void Filter( CMsgAddr *Addr, CMostMsg *Msg ); +}; + + +#endif //MSGFILTER_H diff --git a/Src/MacAddr.cpp b/Src/MacAddr.cpp new file mode 100644 index 0000000..8b1f699 --- /dev/null +++ b/Src/MacAddr.cpp @@ -0,0 +1,165 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include "MacAddr.h" + + + + +CMacAddr::CMacAddr() +{ + memset( m_Mac, 0, sizeof( m_Mac ) ); +} + + + +CMacAddr::CMacAddr( const uint8_t *pAddress ) +{ + if( NULL == pAddress ) + { + memset( m_Mac, 0, sizeof( m_Mac ) ); + return; + } + m_Mac[0] = pAddress[0]; + m_Mac[1] = pAddress[1]; + m_Mac[2] = pAddress[2]; + m_Mac[3] = pAddress[3]; + m_Mac[4] = pAddress[4]; + m_Mac[5] = pAddress[5]; +} + + + +CMacAddr::CMacAddr( uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5 ) +{ + m_Mac[0] = b0; + m_Mac[1] = b1; + m_Mac[2] = b2; + m_Mac[3] = b3; + m_Mac[4] = b4; + m_Mac[5] = b5; +} + + + +CMacAddr::CMacAddr( CMacAddr *copySouce ) +{ + if( NULL != copySouce ) + { + memcpy( m_Mac, copySouce->m_Mac, sizeof( m_Mac ) ); + } +} + + +const char *CMacAddr::ToString() +{ + snprintf( stringRep, sizeof( stringRep ), "%02X-%02X-%02X-%02X-%02X-%02X", m_Mac[0], m_Mac[1], m_Mac[2], m_Mac[3], + m_Mac[4], m_Mac[5] ); + return stringRep; +} + + +const uint8_t *CMacAddr::GetBytes() +{ + return m_Mac; +} + + + +void CMacAddr::CopyValuesFromByteArray( const uint8_t *pAddress ) +{ + if( NULL == pAddress ) + return; + m_Mac[0] = pAddress[0]; + m_Mac[1] = pAddress[1]; + m_Mac[2] = pAddress[2]; + m_Mac[3] = pAddress[3]; + m_Mac[4] = pAddress[4]; + m_Mac[5] = pAddress[5]; +} + + + +void CMacAddr::CopyValuesFromString( const char *pAddress ) +{ + if( NULL == pAddress ) + return; + uint8_t pos = 0; + char buf[64]; + char *tok; + char *tkPtr; + strncpy( buf, pAddress, sizeof( buf ) ); + tok = strtok_r( buf, "-: ,.", &tkPtr ); + while( NULL != tok && pos < sizeof( m_Mac ) ) + { + uint32_t val; + sscanf( tok, "%X", &val ); + m_Mac[pos++] = val; + tok = strtok_r( NULL, "-: ,.", &tkPtr ); + } +} + + + +CMacAddr::CMacAddr( uint8_t deviceInstance, uint16_t nodeAddress ) +{ + if( 0x01 == nodeAddress ) + nodeAddress = 0x100; + + m_Mac[0] = + 0x02; //Local defined MAC address flag. Has to be unique in the local network. + m_Mac[1] = 0; + m_Mac[2] = deviceInstance; + m_Mac[3] = ( uint8_t )( nodeAddress & 0xFF ); + m_Mac[4] = ( uint8_t )( ( nodeAddress >> 8 ) & 0xFF ); + m_Mac[5] = ( uint8_t )( nodeAddress & 0xFF ); +} + + + + +CMacAddr::~CMacAddr() +{ +} + + + + + + + +uint8_t CMacAddr::operator[]( int nByte ) +{ + return ( 5 < nByte ) ? 0 : m_Mac[nByte]; +} + + + + +bool CMacAddr::operator==( CMacAddr const &rhs ) +{ + return ( 0 == memcmp( m_Mac, rhs.m_Mac, sizeof( m_Mac ) ) ); +} diff --git a/Src/MacAddr.h b/Src/MacAddr.h new file mode 100644 index 0000000..0ff9858 --- /dev/null +++ b/Src/MacAddr.h @@ -0,0 +1,153 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMacAddr class. + */ +/*----------------------------------------------------------*/ +#ifndef _MACADDR_H_ +#define _MACADDR_H_ + +#include + +/*----------------------------------------------------------*/ +/*! \brief Class to deal with MAC addresses. + */ +/*----------------------------------------------------------*/ +class CMacAddr +{ +private: + uint8_t m_Mac[6]; + char stringRep[32]; + +public: + /*----------------------------------------------------------*/ + /*! \brief Default constructor of CMacAddr. + */ + /*----------------------------------------------------------*/ + CMacAddr(); + + + + /*----------------------------------------------------------*/ + /*! \brief Default destructor of CMacAddr. + */ + /*----------------------------------------------------------*/ + ~CMacAddr(); + + + /*----------------------------------------------------------*/ + /*! \brief Constructor of CMacAddr, which uses the given bytes to initialize this class. + * \param pAddress - 6 Byte array containing the MAC address. + */ + /*----------------------------------------------------------*/ + CMacAddr( const uint8_t *pAddress ); + + /*----------------------------------------------------------*/ + /*! \brief Constructor of CMacAddr, which uses the given bytes to initialize this class. + * \param b0 - The 1st byte of the MAC address. + * \param b1 - The 2nd byte of the MAC address. + * \param b2 - The 3rd byte of the MAC address. + * \param b3 - The 4th byte of the MAC address. + * \param b4 - The 5th byte of the MAC address. + * \param b5 - The 6th byte of the MAC address. + */ + /*----------------------------------------------------------*/ + CMacAddr( uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5 ); + + + + /*----------------------------------------------------------*/ + /*! \brief Constructor of CMacAddr, which uses the given parameters to create a system wide unique MAC address. + * \param deviceIndex - The index of the physical device instance of the server device. + * \param nodeAddress - The MOST node address (e.g. 0x101). + */ + /*----------------------------------------------------------*/ + CMacAddr( uint8_t deviceInstance, uint16_t nodeAddress ); + + + + /*----------------------------------------------------------*/ + /*! \brief Copy-Constructor of CMacAddr, which creates a new instance out of the given object (deep copy). + * \param copySouce - The object, to copy. + */ + /*----------------------------------------------------------*/ + CMacAddr( CMacAddr *copySouce ); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets a zero terminated string representation of this MAC address. The delimiter is '-'. + * \return A string representation of this MAC address. + */ + /*----------------------------------------------------------*/ + const char *ToString(); + + + /*----------------------------------------------------------*/ + /*! \brief Gets a Byte array with the length of 6 containing the MAC address + * \return MAC address byte array. + */ + /*----------------------------------------------------------*/ + const uint8_t *GetBytes(); + + + /*----------------------------------------------------------*/ + /*! \brief Copy the MAC address value from a given byte array. + * \param pAddress - 6 Byte array containing the MAC address. + */ + /*----------------------------------------------------------*/ + void CopyValuesFromByteArray( const uint8_t *pAddress ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Copy the MAC address value from a given string. + * \param pAddress - String containing the MAC address. + */ + /*----------------------------------------------------------*/ + void CopyValuesFromString( const char *pAddress ); + + + /*----------------------------------------------------------*/ + /*! \brief Access the MAC address byte wise. + * \param nByte - offset of the byte array of the MAC address. Must be between 0 - 5. + * \return The byte value of the MAC address on the given byte offset. + */ + /*----------------------------------------------------------*/ + uint8_t operator[]( int nByte ); + + + + /*----------------------------------------------------------*/ + /*! \brief Compares the given CMacAddr instance with this one. + * \param rhs - The source CMacAddr instance, which then will be compared. + * \return true, if the value of the MAC address is equal. false, the value of the MAC address is different. + */ + /*----------------------------------------------------------*/ + bool operator==( CMacAddr const &rhs ); +}; + +#endif diff --git a/Src/Main.cpp b/Src/Main.cpp new file mode 100644 index 0000000..b11fd40 --- /dev/null +++ b/Src/Main.cpp @@ -0,0 +1,1255 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#define NM_VERSION_MAJOR ((uint8_t)3) +#define NM_VERSION_MINOR ((uint8_t)0) +#define NM_VERSION_BUGFIX ((uint8_t)4) +#define NM_VERSION_BUILD ((uint8_t)0) + +#define IPC_PORT_NUMBER_NM (5533) +#define FBLOCK_NETWORK_MANAGER (10) +#define FUNC_NM_SERVERVERSION (1) +#define FUNC_NM_SUBSCRIBE_MCM (2) +#define FUNC_NM_UNSUBSCRIBE_MCM (3) +#define FUNC_NM_RECEIVED_MCM (4) +#define FUNC_NM_SEND_MCM_ADR (5) +#define FUNC_NM_SEND_MCM_DEV (6) +#define FUNC_NM_COUNTER_ROUTE (10) +#define FUNC_NM_EXECUTE_CONFIG (15) +#define FUNC_NM_EXECUTE_SCRIPT_FILE (20) +#define FUNC_NM_EXECUTE_SCRIPT_MEM (21) +#define FUNC_NM_NET_STATE (30) +#define FUNC_NM_CONNECTION_LIST (100) +#define FUNC_NM_RING_BREAK_DIAGNOSIS (200) + +#define IPC_TEMP_BUFFER_SIZE (200000) + +#define AMOUNT_OF_NETWORK_STATES 10 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Console.h" +#include "Network.h" +#include "Board.h" +#include "MacAddr.h" +#include "MostIpc.h" +#include "ConnectionInfo.h" + +using namespace std; + +class CNetworkManagerHandler; +static CNetworkManagerHandler *s_mainClass = NULL; + +/*----------------------------------------------------------*/ +/*! \brief Structure which holds information to address an MOST device via it's source and target address. + */ +/*----------------------------------------------------------*/ +typedef struct +{ + ///The MOST ring instance, starting with 0 for the first MOST ring. + uint32_t mostInst; + ///The MOST source address (may be not accurate). + uint32_t sourceAddr; + ///The MOST target address. + uint32_t targetAddr; + ///The MOST Function Block Identifier + uint32_t nFBlock; + ///The MOST Function Block Instance Identifier + uint32_t nInst; + ///The MOST Function Block Function Identifier + uint32_t nFunc; + ///The MOST Function Block Function Operation Type + uint32_t nOpType; + ///The amount of bytes stored in Payload + uint32_t nPayloadLen; +} MostMcmAdr_t; + +/*----------------------------------------------------------*/ +/*! \brief Structure which holds information to address an MOST device via it's device id and device instance (like described in the XML file). + */ +/*----------------------------------------------------------*/ +typedef struct +{ + ///The MOST ring instance, starting with 0 for the first MOST ring. + uint32_t mostInst; + ///The device identifier (group address) as specified in the XML file. + uint32_t deviceId; + ///The instance number of the device. Starting with 0 for the first device with deviceId. + uint32_t deviceInst; + ///The MOST Function Block Identifier + uint32_t nFBlock; + ///The MOST Function Block Instance Identifier + uint32_t nInst; + ///The MOST Function Block Function Identifier + uint32_t nFunc; + ///The MOST Function Block Function Operation Type + uint32_t nOpType; + ///The amount of bytes stored in Payload + uint32_t nPayloadLen; +} MostMcmDev_t; + +/*----------------------------------------------------------*/ +/*! \brief Structure which holds information to address an MOST device via it's MOST instance and the MOST target address. + */ +/*----------------------------------------------------------*/ +typedef struct +{ + ///The MOST ring instance, starting with 0 for the first MOST ring. + uint32_t mostInst; + ///The MOST target address. + uint32_t targetAddr; +} MostPacketAddr_t; + +/*----------------------------------------------------------*/ +/*! \brief Structure which holds information of all MOST specific states. + */ +/*----------------------------------------------------------*/ +typedef struct +{ + //If this value is false, all other values must be ignored. + bool isValid; + ///The MOST ring instance, starting with 0 for the first MOST ring. + uint8_t mostInstance; + ///If set to true, the MOST ring is fully functional. Otherwise, no data can be transported. + bool available; + ///Shows the amount of nodes for the specific MOST ring. + uint8_t maxPos; + ///Shows how many bytes in a MOST frame is reserved for Packet (IP) data. + uint16_t packetBW; +} NetworkInformation_t; + +static void PrintMenu(); + +/*! +* \brief Applications main entry point and event handler, as it implements callbacks from CNetworkListener. +* It controls the Network via the CNetwork class. +* Isochronous TX connections will be automatically connected to the CMultiplexer class. +*/ +class CNetworkManagerHandler : public CNetworkListner, public CThread +{ +private: + class MostListener + { + private: + CSafeVector allListeners; + + public: + MostListener() + { + } + + ~MostListener() + { + for (uint32_t i = 0; i < allListeners.Size(); i++) + allListeners[i]->RemoveReference(); + allListeners.RemoveAll(false); + } + + void RemoveListener( CMsgAddr *pAddr ) + { + if( NULL == pAddr ) + return; + allListeners.Remove(pAddr); + pAddr->RemoveReference(); + } + + void AddListener( CMsgAddr *pAddr ) + { + if( NULL == pAddr ) + return; + + for (uint32_t i = 0; i < allListeners.Size(); i++) + { + CMsgAddr *l = allListeners[i]; + if (NULL != l + && l->GetProtocol() == pAddr->GetProtocol() + && 0 == strcmp(l->GetInetAddress(), pAddr->GetInetAddress())) + { + if (l->GetPort() == pAddr->GetPort()) + { + //Already registered with the same port, do not continue + return; + } + else + { + //The client reconnected with different (random) port. + //Remove old connection + allListeners.Remove(l); + l->RemoveReference(); + break; + } + } + } + pAddr->AddReference(); + allListeners.PushBack( pAddr ); + } + + uint32_t GetCount() + { + return allListeners.Size(); + } + + CMsgAddr *GetListener( uint32_t index ) + { + return allListeners[index]; + } + }; + + NetworkInformation_t allStates[AMOUNT_OF_NETWORK_STATES]; + CNetwork *network; + bool allowThreadRun; + uint32_t updateCount; + static const uint32_t updateCountMaxVal = 3; + + CMostIpc *mostIpc; + CMsgFilter *serverVersion_Get; + CMsgFilter *subscribeMcm_Set; + CMsgFilter *unsubscribeMcm_Set; + CMsgFilter *sendMcmAdr_Set; + CMsgFilter *sendMcmDev_Set; + CMsgFilter *counterRoute_Get; + CMsgFilter *executeConfigFile_Set; + CMsgFilter *executeScriptFile_Set; + CMsgFilter *executeScriptBuffer_Set; + CMsgFilter *connectionList_Get; + CMsgFilter *ringBreakDiagnosis_Get; + CMsgFilter *netState_Get; + + MostListener netStateListener; + MostListener mcmListener; + MostListener connectionListener; + MostListener rbdListener; + +public: + CConnectionInfoContainer infoContainer; + + CNetworkManagerHandler() : CThread( "CNetworkManagerHandler", + false ), allowThreadRun( true ), updateCount( updateCountMaxVal ) + { + memset(allStates, 0, sizeof(allStates)); + + network = CNetwork::GetInstance(); + network->AddListener( this ); + + mostIpc = new CMostIpc( IPC_PORT_NUMBER_NM, true ); + + serverVersion_Get = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_SERVERVERSION, + CMostMsg::OP_GET, OnServerVersion_Get ); + mostIpc->RegisterMessageHandler( serverVersion_Get ); + + subscribeMcm_Set = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_SUBSCRIBE_MCM, + CMostMsg::OP_SET, OnSubscribeMcm_Set ); + mostIpc->RegisterMessageHandler( subscribeMcm_Set ); + + unsubscribeMcm_Set = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_UNSUBSCRIBE_MCM, + CMostMsg::OP_SET, OnUnsubscribeMcm_Set ); + mostIpc->RegisterMessageHandler( unsubscribeMcm_Set ); + + sendMcmAdr_Set = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_SEND_MCM_ADR, + CMostMsg::OP_SET, OnMcmSendAddress_Set ); + mostIpc->RegisterMessageHandler( sendMcmAdr_Set ); + + sendMcmDev_Set = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_SEND_MCM_DEV, + CMostMsg::OP_SET, OnMcmSendDevice_Set ); + mostIpc->RegisterMessageHandler( sendMcmDev_Set ); + + counterRoute_Get = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_COUNTER_ROUTE, + CMostMsg::OP_GET, OnCounterRoute_Get ); + mostIpc->RegisterMessageHandler( counterRoute_Get ); + + executeConfigFile_Set = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_EXECUTE_CONFIG, + CMostMsg::OP_SET, OnExecuteConfigFile_Set ); + mostIpc->RegisterMessageHandler( executeConfigFile_Set ); + + executeScriptFile_Set = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_EXECUTE_SCRIPT_FILE, + CMostMsg::OP_SET, OnExecuteScriptFile_Set ); + mostIpc->RegisterMessageHandler( executeScriptFile_Set ); + + executeScriptBuffer_Set = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_EXECUTE_SCRIPT_MEM, + CMostMsg::OP_SET, OnExecuteScriptBuffer_Set ); + mostIpc->RegisterMessageHandler( executeScriptBuffer_Set ); + + connectionList_Get = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_CONNECTION_LIST, + CMostMsg::OP_GET, OnConnectionList_Get ); + mostIpc->RegisterMessageHandler( connectionList_Get ); + + ringBreakDiagnosis_Get = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_RING_BREAK_DIAGNOSIS, + CMostMsg::OP_GET, OnRingBreakDiagnosis_Get ); + mostIpc->RegisterMessageHandler( ringBreakDiagnosis_Get ); + + netState_Get = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_NET_STATE, + CMostMsg::OP_GET, OnNetState_Get ); + mostIpc->RegisterMessageHandler( netState_Get ); + + Start(); + } + + virtual ~CNetworkManagerHandler() + { + allowThreadRun = false; + Stop(); + infoContainer.DestroyAllInfos(); + if( NULL != network ) + { + network->RemoveListener( this ); + delete network; + network = NULL; + } + if( NULL != mostIpc ) + { + delete mostIpc; + mostIpc = NULL; + } + if( NULL != subscribeMcm_Set ) + { + delete subscribeMcm_Set; + subscribeMcm_Set = NULL; + } + if( NULL != unsubscribeMcm_Set ) + { + delete unsubscribeMcm_Set; + unsubscribeMcm_Set = NULL; + } + if( NULL != sendMcmAdr_Set ) + { + delete sendMcmAdr_Set; + sendMcmAdr_Set = NULL; + } + if( NULL != sendMcmDev_Set ) + { + delete sendMcmDev_Set; + sendMcmDev_Set = NULL; + } + if( NULL != counterRoute_Get ) + { + delete counterRoute_Get; + counterRoute_Get = NULL; + } + if( NULL != executeConfigFile_Set ) + { + delete executeConfigFile_Set; + executeConfigFile_Set = NULL; + } + if( NULL != executeScriptFile_Set ) + { + delete executeScriptFile_Set; + executeScriptFile_Set = NULL; + } + if( NULL != executeScriptBuffer_Set ) + { + delete executeScriptBuffer_Set; + executeScriptBuffer_Set = NULL; + } + if( NULL != connectionList_Get ) + { + delete connectionList_Get; + connectionList_Get = NULL; + } + if( NULL != ringBreakDiagnosis_Get ) + { + delete ringBreakDiagnosis_Get; + ringBreakDiagnosis_Get = NULL; + } + if( NULL != netState_Get ) + { + delete netState_Get; + netState_Get = NULL; + } + } + + static void OnServerVersion_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnServerVersion_Get parameters are invalid."RESETCOLOR"\n" ); + return; + } + ConsolePrintf( PRIO_HIGH, + GREEN"MOST-IPC Client connected. ip=%s"RESETCOLOR"\n", pAddr->GetInetAddress() ); + + const uint8_t version[] = + { + NM_VERSION_MAJOR, NM_VERSION_MINOR, + NM_VERSION_BUGFIX, NM_VERSION_BUILD + }; + CMostMsgTx *mostMsg = new CMostMsgTx( pAddr,FBLOCK_NETWORK_MANAGER, + 0, FUNC_NM_SERVERVERSION, CMostMsg::OP_STATUS, sizeof ( version ), + version, 500, NULL, NULL ); + s_mainClass->mostIpc->SendMsg( mostMsg ); + mostMsg->RemoveReference(); + } + + static void OnSubscribeMcm_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnSubscribeMcm_Set parameters are invalid."RESETCOLOR"\n" ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + GREEN"MOST-IPC Client subscribed for MCM. ip=%s"RESETCOLOR"\n", pAddr->GetInetAddress() ); + s_mainClass->mcmListener.AddListener( pAddr ); + } + + static void OnUnsubscribeMcm_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnUnsubscribeMcm_Set parameters are invalid."RESETCOLOR"\n" ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + GREEN"MOST-IPC Client unsubscribed for MCM. ip=%s"RESETCOLOR"\n", pAddr->GetInetAddress() ); + s_mainClass->mcmListener.RemoveListener( pAddr ); + } + + static void OnMcmSendAddress_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnMcmSend_Set parameters are invalid."RESETCOLOR"\n" ); + return; + } + MostMcmAdr_t *mcm = ( MostMcmAdr_t * )pMsg->GetPayload(); + if( NULL == mcm || pMsg->GetPayloadLen() < sizeof( MostMcmAdr_t ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"OnMcmSend_Set payload length is too small to carry a MCM message."RESETCOLOR"\n" ); + return; + } + ConsolePrintfStart( PRIO_MEDIUM, + GREEN"MOST-IPC Client send MCM to MOST address. ip=%s, ring-inst:%d"\ + "target:0x%X, fblock:0x%X, inst:%d, func:0x%x, op:0x%X, p:0x[", pAddr->GetInetAddress(), mcm->mostInst, + mcm->targetAddr, mcm->nFBlock, mcm->nInst, mcm->nFunc, mcm->nOpType ); + uint8_t *payload = &( ( uint8_t * )mcm )[sizeof( MostMcmAdr_t )]; + for( uint32_t i = 0; i < mcm->nPayloadLen; i++ ) + { + ConsolePrintfContinue( " %02X", payload[i] ); + } + ConsolePrintfExit( "]"RESETCOLOR"\n" ); + + s_mainClass->SendMostControlMessage( mcm->mostInst, mcm->targetAddr, mcm->nFBlock, mcm->nInst, mcm->nFunc, + mcm->nOpType, mcm->nPayloadLen, payload ); + } + + static void OnMcmSendDevice_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnMcmSend_Set parameters are invalid."RESETCOLOR"\n" ); + return; + } + MostMcmDev_t *mcm = ( MostMcmDev_t * )pMsg->GetPayload(); + if( NULL == mcm || pMsg->GetPayloadLen() < sizeof( MostMcmDev_t ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"OnMcmSend_Set payload length is too small to carry a MCM message."RESETCOLOR"\n" ); + return; + } + ConsolePrintfStart( PRIO_MEDIUM, + GREEN"MOST-IPC Client send MCM to specific device. ip=%s, ring-inst:%d"\ + "device-id:0x%X, device-instance:%d, fblock:0x%X, inst:%d, func:0x%x, op:0x%X, p:0x[", + pAddr->GetInetAddress(), mcm->mostInst, mcm->deviceId, mcm->deviceInst, mcm->nFBlock, mcm->nInst, + mcm->nFunc, mcm->nOpType ); + uint8_t *payload = &( ( uint8_t * )mcm )[sizeof( MostMcmAdr_t )]; + for( uint32_t i = 0; i < mcm->nPayloadLen; i++ ) + { + ConsolePrintfContinue( " %02X", payload[i] ); + } + ConsolePrintfExit( "]"RESETCOLOR"\n" ); + + s_mainClass->SendMostControlMessage( mcm->mostInst, mcm->deviceId, mcm->deviceInst, mcm->nFBlock, mcm->nInst, + mcm->nFunc, mcm->nOpType, mcm->nPayloadLen, payload ); + } + + static void OnCounterRoute_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnCounterRoute_Get parameters are invalid."RESETCOLOR"\n" ); + return; + } + CNetwork::Route_t *inRoute = ( CNetwork::Route_t * )pMsg->GetPayload(); + if( NULL == inRoute || pMsg->GetPayloadLen() < sizeof( CNetwork::Route_t ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"OnCounterRoute_Get payload length is too small to carry a Route_t message."RESETCOLOR + "\n" ); + return; + } + CNetwork::Route_t *outRoutes = NULL; + uint32_t routeElements = s_mainClass->network->GetCounterPartOfRoute( inRoute, &outRoutes ); + ConsolePrintfStart( PRIO_MEDIUM, + GREEN"MOST-IPC Client asked for counter route for: (deviceType:0x%X, inst:%d, channelId:%d), Result:", + inRoute->deviceType, inRoute->instance, inRoute->channelId ); + for( uint32_t i = 0; NULL != outRoutes && i < routeElements; i++ ) + { + ConsolePrintfContinue( " (deviceType:0x%X, inst:%d, channelId:%d)", outRoutes[i].deviceType, + outRoutes[i].instance, outRoutes[i].channelId ); + } + ConsolePrintfExit( RESETCOLOR"\n" ); + uint32_t bufferLen = ( routeElements + 1 ) * sizeof( CNetwork::Route_t ); + uint8_t *buffer = ( uint8_t * )malloc( bufferLen ); + if( NULL == buffer ) + { + free( outRoutes ); + ConsolePrintf( PRIO_ERROR, + RED"OnCounterRoute_Get payload failed to allocate memory for response."RESETCOLOR + "\n" ); + return; + } + //Copy request to response element 0 + memcpy( buffer, inRoute, sizeof( CNetwork::Route_t ) ); + //Copy the results beginning at element 1 + if( 0 != routeElements ) + memcpy( &buffer[sizeof( CNetwork::Route_t )], outRoutes, routeElements * sizeof( CNetwork::Route_t ) ); + free( outRoutes ); + + CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr + FBLOCK_NETWORK_MANAGER, // nFBlock + 0, // nInst + FUNC_NM_COUNTER_ROUTE, // nFunc + CMostMsg::OP_STATUS, // nOpType + bufferLen, //nPayloadLen + buffer, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpc->SendMsg( mostMsg ); + mostMsg->RemoveReference(); + free( buffer ); + } + + static void OnExecuteConfigFile_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || 0 == pMsg->GetPayloadLen() ) + { + ConsolePrintf( PRIO_ERROR, RED"OnExecuteScriptFile_Set parameters are invalid."RESETCOLOR"\n" ); + return; + } + char *fileName = ( char * )pMsg->GetPayload(); + ConsolePrintf( PRIO_HIGH, "Executing new config file:%s\n", fileName); + s_mainClass->network->LoadConfig(fileName); + } + + static void OnExecuteScriptFile_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnExecuteScriptFile_Set parameters are invalid."RESETCOLOR"\n" ); + return; + } + MostPacketAddr_t *addr = ( MostPacketAddr_t * )pMsg->GetPayload(); + if( NULL == addr || pMsg->GetPayloadLen() <= sizeof( MostPacketAddr_t ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"OnExecuteScriptFile_Set payload length is too small to carry a MCM message."RESETCOLOR"\n" ); + return; + } + char *payload = ( char * )( pMsg->GetPayload() + sizeof( MostPacketAddr_t ) ); + s_mainClass->network->ExecuteXmlScriptFromFile( addr->mostInst, addr->targetAddr, payload ); + } + + static void OnExecuteScriptBuffer_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnExecuteScriptFile_Set parameters are invalid."RESETCOLOR"\n" ); + return; + } + MostPacketAddr_t *addr = ( MostPacketAddr_t * )pMsg->GetPayload(); + if( NULL == addr || pMsg->GetPayloadLen() <= sizeof( MostPacketAddr_t ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"OnExecuteScriptFile_Set payload length is too small to carry a MCM message."RESETCOLOR"\n" ); + return; + } + char *payload = ( char * )( pMsg->GetPayload() + sizeof( MostPacketAddr_t ) ); + uint32_t payloadLen = pMsg->GetPayloadLen() - sizeof( MostPacketAddr_t ); + s_mainClass->network->ExecuteXmlScriptFromMemory( addr->mostInst, addr->targetAddr, payload, payloadLen ); + } + + static void OnConnectionList_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnConnectionList_Get parameters are invalid."RESETCOLOR"\n" ); + return; + } + + s_mainClass->connectionListener.AddListener( pAddr ); + + char *pBuffer = ( char * )malloc( IPC_TEMP_BUFFER_SIZE ); + uint32_t used = s_mainClass->infoContainer.SerializeToString( pBuffer, IPC_TEMP_BUFFER_SIZE ); + CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr + FBLOCK_NETWORK_MANAGER, // nFBlock + 0, // nInst + FUNC_NM_CONNECTION_LIST, // nFunc + CMostMsg::OP_STATUS, // nOpType + used, //nPayloadLen + ( uint8_t * )pBuffer, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpc->SendMsg( mostMsg ); + mostMsg->RemoveReference(); + free( pBuffer ); + } + + static void OnRingBreakDiagnosis_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnRingBreakDiagnosis_Get parameters are invalid."RESETCOLOR"\n" ); + return; + } + s_mainClass->rbdListener.AddListener( pAddr ); + s_mainClass->network->GetRingBreakDiagnosisResult( 0, 1 ); + } + + static void OnNetState_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) + { + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnNetState_Get parameters are invalid."RESETCOLOR"\n" ); + return; + } + s_mainClass->netStateListener.AddListener( pAddr ); + + for (uint32_t i = 0; i < AMOUNT_OF_NETWORK_STATES; i++) + { + if (!s_mainClass->allStates[i].isValid) + continue; + s_mainClass->SendNetworkState(pAddr, s_mainClass->allStates[i].mostInstance, + s_mainClass->allStates[i].available, s_mainClass->allStates[i].maxPos, + s_mainClass->allStates[i].packetBW); + } + } + + void SendNetworkState( CMsgAddr *addr, uint8_t mostInstance, bool available, uint8_t maxPos, uint16_t packetBW ) + { + uint8_t p[5]; + p[0] = mostInstance; + p[1] = available; + p[2] = maxPos; + p[3] = packetBW /256; + p[4] = packetBW %256; + CMostMsgTx *mostMsg = new CMostMsgTx( addr, //Addr + FBLOCK_NETWORK_MANAGER, // nFBlock + 0, // nInst + FUNC_NM_NET_STATE, // nFunc + CMostMsg::OP_STATUS, // nOpType + sizeof(p), //nPayloadLen + p, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpc->SendMsg( mostMsg ); + mostMsg->RemoveReference(); + } + +public: + + static CNetworkManagerHandler *GetInstance() + { + if( NULL == s_mainClass ) + s_mainClass = new CNetworkManagerHandler(); + return s_mainClass; + } + + static void DestroyInstance() + { + if( NULL != s_mainClass ) + { + delete s_mainClass; + s_mainClass = NULL; + } + } + + void SetPromiscuousMode( bool promiscuousMode ) + { + network->SetPromiscuousMode( promiscuousMode ); + } + + virtual void Run() + { + if( allowThreadRun ) + { + if( !network->ActionsPending() + && ( updateCount > 0 ) + && ( 0 == --updateCount ) ) + { + if( 0 != network->GetAmountOfLocalNodes() ) + { + infoContainer.PrintTable( false, false ); + SendConnectionList(); + } + else + { + ConsolePrintf( PRIO_ERROR, RED \ + "No local attached INIC was found. Are the drivers loaded? Was the device found, try 'lsusb'?" \ + RESETCOLOR"\n" ); + } + } + } + usleep( 100000 ); + } + + void SendConnectionList() + { + char *pBuffer = ( char * )malloc( IPC_TEMP_BUFFER_SIZE ); + uint32_t used = s_mainClass->infoContainer.SerializeToString( pBuffer, IPC_TEMP_BUFFER_SIZE ); + for( uint32_t i = 0; i < connectionListener.GetCount(); i++ ) + { + CMostMsgTx *mostMsg = new CMostMsgTx( connectionListener.GetListener( i ), //Addr + FBLOCK_NETWORK_MANAGER, // nFBlock + 0, // nInst + FUNC_NM_CONNECTION_LIST, // nFunc + CMostMsg::OP_STATUS, // nOpType + used, //nPayloadLen + ( uint8_t * )pBuffer, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpc->SendMsg( mostMsg ); + mostMsg->RemoveReference(); + } + free( pBuffer ); + } + + /*----------------------------------------------------------*/ + /*! \brief Determines, if there are ongoing MOST transactions enqueued. + * \return true, if there are still transactions ongoing. false, the system is idle. + */ + /*----------------------------------------------------------*/ + bool ActionsPending() + { + return network->ActionsPending(); + } + + /*----------------------------------------------------------*/ + /*! \brief Loads the given configuration XML file. This will start up the MOST ring, and the + * connections will be created. + * \param szConfigXml - string holding only the filename to the XML file. + * \param szSearchPath - string holding the path to all the XML files. + * \return true, if the file could be parsed, and the initial MOST setup was successful. + */ + /*----------------------------------------------------------*/ + bool LoadConfig( const char *szConfigXml, const char *szSearchPath ) + { + return network->LoadConfig( szConfigXml, szSearchPath ); + } + + /*----------------------------------------------------------*/ + /*! \brief Connects the given source with the given sink. The index is relative to the list, + reported by the PrintTable method of the CConnectionInfo class. + * \return true, if the connection may be acceptable. The transactions were enqueued to be performed asynchronously. + * false, the connection can not be established. + */ + /*----------------------------------------------------------*/ + bool ConnectDevices( uint32_t sourceIndex, uint32_t sinkIndex ) + { + bool success = false; + if( sourceIndex < infoContainer.GetAllInfoAmount() + && sinkIndex < infoContainer.GetAllInfoAmount() ) + { + CConnectionInfo *source = infoContainer.GetInfo( sourceIndex ); + CConnectionInfo *sink = infoContainer.GetInfo( sinkIndex ); + success = network->ConnectSourceToSink( source->macAddr, source->channelId, sink->macAddr, + sink->channelId ); + if( success ) + { + sink->mostConnectionLabel = source->mostConnectionLabel; + } + } + return success; + } + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the corresponding sink index for the given MAC address + * \param pMacAddress - Pointer to the byte array with length 6, holding the MAC address + * \return The sink index if found, otherwise 0xFFFFFFFF + */ + /*----------------------------------------------------------*/ + uint32_t GetSinkIndex( uint8_t *pMacAddress ) + { + uint32_t returnVal = 0xFFFFFFFF; + if( NULL == pMacAddress ) + return returnVal; + CMacAddr *mac1 = new CMacAddr( pMacAddress[0], pMacAddress[1], + pMacAddress[2], pMacAddress[3], pMacAddress[4], pMacAddress[5] ); + for( uint32_t i = 0; i < infoContainer.GetAllInfoAmount(); i++ ) + { + CConnectionInfo *info = infoContainer.GetInfo( i ); + if( NULL == info ) + continue; + CMacAddr *mac2 = info->macAddr; + if( *mac1 == *mac2 ) + { + if( !info->isTX ) + { + returnVal = i; + break; + } + } + } + delete mac1; + return returnVal; + } + + /*----------------------------------------------------------*/ + /*! \brief Sends the given Control Message out to the given MOST ring. + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring. + * \param targetAddr - The MOST target address (0x100, 0x401, etc..) + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + bool SendMostControlMessage( TMostInstace mostInstance, uint32_t targetAddr, uint32_t nFBlock, uint32_t nInst, + uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) + { + return network->SendMostControlMessage( mostInstance, targetAddr, nFBlock, nInst, nFunc, nOpType, nPayloadLen, + Payload ); + } + + bool GetRingBreakDiagnosisResult() + { + return network->GetRingBreakDiagnosisResult( 0, 1 ); + } + + void DebugEnableMost(bool enabled) + { + network->EnableMost(enabled); + } + + /*----------------------------------------------------------*/ + /*! \brief Sends the given Control Message out to the given MOST ring. + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring. + * \param deviceId - The device identifier (group address) as specified in the XML file. + * \param devInst - The instance number of the device. Starting with 0 for the first device with deviceId. + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + bool SendMostControlMessage( TMostInstace mostInstance, TDeviceId deviceId, uint8_t devInst, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) + { + return network->SendMostControlMessage( mostInstance, deviceId, devInst, nFBlock, nInst, nFunc, nOpType, + nPayloadLen, Payload ); + } + + virtual void OnNetworkState( uint8_t mostInstance, bool available, uint8_t maxPos, uint16_t packetBW ) + { + ConsolePrintf( PRIO_LOW, ">>>>>>>>>>>>>> OnNetworkState available=%s, mpr=%d, sbc=%d\n", + available ? "yes" : "no", maxPos, packetBW ); + + if (mostInstance >= AMOUNT_OF_NETWORK_STATES) + { + ConsolePrintf(PRIO_ERROR, RED"OnNetworkState MOST instance is out "\ + "of range, please increase AMOUNT_OF_NETWORK_STATES define"RESETCOLOR"\n"); + } + else + { + allStates[mostInstance].isValid = true; + allStates[mostInstance].available = available; + allStates[mostInstance].maxPos = maxPos; + allStates[mostInstance].packetBW = packetBW; + } + + for( uint32_t i = 0; i < netStateListener.GetCount(); i++ ) + { + SendNetworkState( netStateListener.GetListener( i ),mostInstance, available, maxPos, packetBW ); + } + } + + virtual void OnChannelAvailable( CMacAddr *macAddr, TDeviceId deviceId, uint8_t devInst, TChannelId channelId, + TMostInstace mostInstance, EPDataType_t dataType, TBlockwidth reservedBandwidth, bool isSourceDevice, + bool inSocketCreated, bool outSocketCreated, bool socketsConnected, int32_t bufferSize, + uint32_t mostConnectionLabel, int16_t splittedOffset, int16_t splittedMaxBandwidth, uint16_t packetsPerXact, + const char *deviceName ) + { + if( NULL != macAddr ) + { + CConnectionInfo *info = infoContainer.GetInfo( macAddr, channelId, mostInstance, isSourceDevice ); + if( NULL == info ) + return; + updateCount = updateCountMaxVal; + info->deviceType = deviceId; + info->deviceInstance = devInst; + info->dataType = dataType; + info->reservedBandwidth = reservedBandwidth; + info->bufferSize = bufferSize; + info->inSocketCreated = inSocketCreated; + info->outSocketCreated = outSocketCreated; + info->socketsConnected = socketsConnected; + info->mostConnectionLabel = mostConnectionLabel; + info->splittedOffset = splittedOffset; + info->splittedMaxBandwidth = splittedMaxBandwidth; + info->packetsPerXact = packetsPerXact; + if( NULL != deviceName && strlen( deviceName ) != 0 ) + { + strncpy( info->deviceName, deviceName, sizeof( info->deviceName ) ); + } + } + else + { + ConsolePrintf( PRIO_LOW, ">>>>>>>>>>>>>> OnChannelAvailable, MAC:unknown, channel-Id:%d, " \ + "MOST-Inst:%d, TX:%d, in:%d, out:%d, con:%d, name:%s\n", channelId, mostInstance, isSourceDevice, + inSocketCreated, outSocketCreated, socketsConnected, deviceName ); + } + } + + virtual void OnChannelUnavailable( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ) + { + updateCount = updateCountMaxVal; + ConsolePrintf( PRIO_LOW, ">>>>>>>>>>>>>> OnChannelUnavailable, MAC:%s, channel-Id:%d" \ + ", MOST-Inst:%d\n", macAddr->ToString(), ( uint32_t )channelId, mostInstance ); + + infoContainer.DestroyInfo( macAddr, channelId, mostInstance ); + } + + virtual void OnMostControlMessage( uint8_t devInst, uint32_t sourceAddr, uint32_t targetAddr, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) + { + ConsolePrintfStart( PRIO_MEDIUM, ">>>>>>>>>>>>>> OnMostControlMessage, source:0x%X, " \ + "target:0x%X, FBlock:0x%X, Inst:%d, Func:0x%X, OP:0x%X, Len:%d, P:0x[", sourceAddr, targetAddr, nFBlock, + nInst, nFunc, nOpType, nPayloadLen ); + for( uint32_t i = 0; NULL != Payload && i < nPayloadLen; i++ ) + ConsolePrintfContinue( "%02X ", Payload[i] ); + ConsolePrintfExit( " ]\n" ); + + uint32_t bufLen = sizeof( MostMcmAdr_t ) + nPayloadLen; + MostMcmAdr_t *mcm = ( MostMcmAdr_t * )calloc( 1, bufLen ); + if( NULL == mcm ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to allocate memory: OnMostControlMessage"RESETCOLOR"\n" ); + return; + } + mcm->sourceAddr = sourceAddr; + mcm->targetAddr = targetAddr; + mcm->nFBlock = nFBlock; + mcm->nInst = nInst; + mcm->nFunc = nFunc; + mcm->nOpType = nOpType; + mcm->nPayloadLen = nPayloadLen; + uint8_t *pBuffer = ( uint8_t * )mcm; + memcpy( &pBuffer[sizeof( MostMcmAdr_t )], Payload, nPayloadLen ); + + for( uint32_t i = 0; i < mcmListener.GetCount(); i++ ) + { + CMsgAddr *c = mcmListener.GetListener( i ); + if( NULL == c ) + break; + CMostMsgTx *mostMsg = new CMostMsgTx( c, FBLOCK_NETWORK_MANAGER, + 0, FUNC_NM_RECEIVED_MCM, CMostMsg::OP_STATUS, bufLen, + pBuffer, 500, NULL, NULL ); + mostIpc->SendMsg( mostMsg ); + mostMsg->RemoveReference(); + } + } + + virtual void OnRingBreakDiagnosisResultV3( uint8_t devInst, uint16_t nodeAddress, uint8_t result, + uint8_t position, uint8_t status, uint16_t id ) + { + uint8_t pBuffer[8]; + pBuffer[0] = devInst; + pBuffer[1] = nodeAddress / 256; + pBuffer[2] = nodeAddress % 256; + pBuffer[3] = result; + pBuffer[4] = position; + pBuffer[5] = status; + pBuffer[6] = id / 256; + pBuffer[7] = id % 256; + + for( uint32_t i = 0; i < rbdListener.GetCount(); i++ ) + { + CMsgAddr *c = rbdListener.GetListener( i ); + if( NULL == c ) + break; + CMostMsgTx *mostMsg = new CMostMsgTx( c, FBLOCK_NETWORK_MANAGER, + 0, FUNC_NM_RING_BREAK_DIAGNOSIS, CMostMsg::OP_STATUS, sizeof( pBuffer ), + pBuffer, 500, NULL, NULL ); + mostIpc->SendMsg( mostMsg ); + mostMsg->RemoveReference(); + } + } +}; + +static void PrintMenu() +{ + ConsolePrintfStart( PRIO_HIGH, "Main Menu, press key and enter:\n" ); + ConsolePrintfContinue( "-------------------------------\n" ); + ConsolePrintfContinue( " m - Print this menu\n" ); + ConsolePrintfContinue( " p - Print Connection List\n" ); + ConsolePrintfContinue( " c - Connect Source and Sink\n" ); + ConsolePrintfContinue( " s - Send sample control message to MOST ring\n" ); + ConsolePrintfContinue( " r - Get Ring Break Diagnosis Result\n" ); + ConsolePrintfContinue( " x - Exit this application\n" ); + ConsolePrintfContinue( "==Debugging==\n" ); + ConsolePrintfContinue( " 0 - Turn off MOST\n" ); + ConsolePrintfContinue( " 1 - Turn on MOST\n" ); + ConsolePrintfExit( "-------------------------------\n" ); +} + +int main( int argc, char *argv[] ) +{ + int sourceIndex, sinkIndex; + + ConsoleInit( false ); + + char *configName = NULL; + char *searchPath = NULL; + bool disableMenu = true; + ConsolePrio_t prio = PRIO_HIGH; + bool promiscuousMode = false; + + if( 2 == argc && strstr( argv[1], ".xml" ) ) + { + configName = argv[1]; + } + else + { + for( int32_t i = 1; i < argc; i++ ) + { + if( strstr( argv[i], "-i" ) ) + { + if( argc <= ( i + 1 ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"-i parameter requires configuration filename as next parameter"RESETCOLOR"\n" ); + return -1; + } + configName = argv[i + 1]; + i++; + } + else if( strstr( argv[i], "-p" ) ) + { + if( argc <= ( i + 1 ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"-p parameter requires search path as next parameter"RESETCOLOR"\n" ); + return -1; + } + searchPath = argv[i + 1]; + i++; + } + else if( strstr( argv[i], "-vv" ) ) + { + prio = PRIO_LOW; + } + else if( strstr( argv[i], "-v" ) ) + { + prio = PRIO_MEDIUM; + } + else if( strstr( argv[i], "--help" ) ) + { + ConsolePrintfStart( PRIO_HIGH, "Usage: %s [OPTION] ... [FILE] ... \n", argv[0] ); + ConsolePrintfContinue( + "Example MOST4.0 NetworkManager, setup and configures all devices in an entire MOST network.\n" ); + + ConsolePrintfContinue( + "\t-i\t\t input configuration file, if there is no other options -i may be left away\n" ); + ConsolePrintfContinue( "\t-p\t\t path to search the configurations file, if not set,"\ + "the path will be extracted from the input configuration file\n" ); + ConsolePrintfContinue( "\t-m\t\t enable user menu\n" ); + ConsolePrintfContinue( "\t-s\t\t enable MEP promiscuous mode (Ethernet SPY)\n" ); + ConsolePrintfContinue( "\t-v\t\t verbose mode - print debug info\n" ); + ConsolePrintfContinue( "\t-vv\t\t super verbose mode - print even more debug info\n" ); + ConsolePrintfContinue( "Example: %s config.xml\n", argv[0] ); + ConsolePrintfContinue( "Example: %s -i /root/config.xml\n", argv[0] ); + ConsolePrintfExit( "Example: %s -i config.xml -p /root\n", argv[0] ); + return 0; + } + else if( strstr( argv[i], "-m" ) ) + { + disableMenu = false; + } + else if( strstr( argv[i], "-s" ) ) + { + ConsolePrintf( PRIO_HIGH, YELLOW"Warning promiscuous mode is activated (-s),"\ + " this may have negative impact on system load"RESETCOLOR"\n" ); + promiscuousMode = true; + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Ignoring command line parameter"\ + " at pos %d: %s, type '%s --help' to see the correct parameter syntax"\ + RESETCOLOR"\n", i, argv[i], argv[0] ); + } + } + } + if( NULL == searchPath && NULL != configName ) + { + uint32_t len = strlen( configName ); + if( 0 != len ) + { + uint32_t stLen = ( len + 1 ); + searchPath = ( char * )calloc( stLen, sizeof( char ) ); + strncpy( searchPath, configName, stLen ); + int32_t pos = len; + for(; pos >= 0; pos-- ) + { + if( '/' == searchPath[pos] ) + { + searchPath[pos] = '\0'; + configName = &searchPath[pos + 1]; + break; + } + } + if( pos <= 0 ) + strncpy( searchPath, ".", stLen ); + } + } + + ConsoleSetPrio( prio ); + ConsolePrintf( PRIO_HIGH, BLUE"========== Network Manager Start =========="RESETCOLOR"\n" ); + CNetworkManagerHandler *mainClass = CNetworkManagerHandler::GetInstance(); + mainClass->SetPromiscuousMode( promiscuousMode ); + + if( NULL != configName && NULL != searchPath ) + { + ConsolePrintf( PRIO_HIGH, "Starting with configuration:'%s'. Search path:'%s'\n", configName, searchPath ); + if( !s_mainClass->LoadConfig( configName, searchPath ) ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to load configuration"RESETCOLOR"\n" ); + } + } + else + { + ConsolePrintf( PRIO_HIGH, "Starting without configuration. It must then be loaded via IPC command\n" ); + } + + int32_t timeout = 5; + do + { + usleep( 1000000 ); + } + while( mainClass->ActionsPending() && --timeout > 0 ); + if( timeout <= 0 ) + ConsolePrintf( PRIO_ERROR, + RED"Warning: main routine waited unusually long for finishing network setup"RESETCOLOR"\n" ); + + if( disableMenu || !isatty( fileno( stdin ) ) ) + { + while( true ) + sleep( 1 ); + } + else + { + PrintMenu(); + + while( true ) + { + int c = getchar(); + fflush( stdin ); + switch( c ) + { + case 'M': + case 'm': + PrintMenu(); + break; + + case 'R': + case 'r': + mainClass->GetRingBreakDiagnosisResult(); + break; + + case 'P': + case 'p': + mainClass->infoContainer.PrintTable( false, false ); + break; + + case 'C': + case 'c': + mainClass->infoContainer.PrintTable( false, false ); + ConsolePrintf( PRIO_HIGH, "Enter Source and Target Index:" ); + if( 0 != scanf( "%d %d", &sourceIndex, &sinkIndex ) ) + { + ConsolePrintf( PRIO_HIGH, "Connecting Source Index %d with Sink Index %d\n", sourceIndex, + sinkIndex ); + if( mainClass->ConnectDevices( sourceIndex, sinkIndex ) ) + { + do + { + usleep( 100000 ); + ConsolePrintf( PRIO_HIGH, "Wait for connection to be finished.\n" ); + } + while( mainClass->ActionsPending() ); + mainClass->infoContainer.PrintTable( false, false ); + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Failed to connect devices"RESETCOLOR"\n" ); + } + } + break; + + case 'S': + case 's': + { + char test[] = "Hello World!\0"; + mainClass->SendMostControlMessage( 0, 0x3C8, 0xF0, 0, 0x100, 1, + strlen( test ), ( uint8_t * )test ); + break; + } + + case 'X': + case 'x': + CNetworkManagerHandler::DestroyInstance(); + usleep( 1000000 ); + ConsolePrintf( PRIO_HIGH, BLUE"========== Network Manager End =========="RESETCOLOR"\n" ); + ConsoleDeinit(); + return 0; + case '0': + mainClass->DebugEnableMost(false); + break; + case '1': + mainClass->DebugEnableMost(true); + break; + default: + break; + } + usleep( 10000 ); //Avoid high CPU load, if terminal is disconnected (running as daemon) + } + } +} diff --git a/Src/Network/IndustrialStack.cpp b/Src/Network/IndustrialStack.cpp new file mode 100644 index 0000000..e84ab22 --- /dev/null +++ b/Src/Network/IndustrialStack.cpp @@ -0,0 +1,275 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class. + */ +/*----------------------------------------------------------*/ + +//#define CON_TRACE + +#define DISABLE_PARALLEL_MODE + +#include +#include "IndustrialStack.h" + +CIndustrialStack::CIndustrialStack(uint8_t deviceApi, int controlRxHandle, int controlTxHandle) +{ + msnw = new CISNetServiceWrapper(deviceApi, controlRxHandle, controlTxHandle); + msnw->AddListener(this); + GetQueue(0x3C8); //Make sure that Broadcast queue is the first one +} + +CIndustrialStack::~CIndustrialStack() +{ + delete msnw; + deviceQueues.RemoveAll(true); + intEventListeners.RemoveAll(false); +} + +void CIndustrialStack::EnqueueElement(uint16_t nodeAddress, IISElement *element) +{ + CISDeviceQueue *dev = GetQueue(nodeAddress); + assert(NULL != dev); + element->AddReference(); + dev->elements.PushBack(element); +} + +void CIndustrialStack::ServiceStack(uint32_t time) +{ + for (uint32_t i = 0; i < deviceQueues.Size(); i++) + { + assert(NULL != deviceQueues[i]); + if (deviceQueues[i]->elements.Size() == 0) + continue; + IISElement *element = deviceQueues[i]->elements[0]; + assert(NULL != element); + ISReturn_t state = element->Service(this, time); + if (state == ISReturn_NoChange) + continue; + deviceQueues[i]->elements.PopFront(); + HandleStateChange(state, element); + element->RemoveReference(); + msnw->ServiceMns(); + } + msnw->ServiceMns(); +} + +bool CIndustrialStack::ElementsPending() +{ + for (uint32_t i = 0; i < deviceQueues.Size(); i++) + { + assert(NULL != deviceQueues[i]); + if (deviceQueues[i]->elements.Size() != 0) + return true; + } + return false; +} + +bool CIndustrialStack::ElementsPending(uint16_t ingoreNodeAddress) +{ + for (uint32_t i = 0; i < deviceQueues.Size(); i++) + { + CISDeviceQueue *q = deviceQueues[i]; + assert(NULL != q); + if (q->GetNodeAddress() == ingoreNodeAddress) + continue; + if (q->elements.Size() != 0) + return true; + } + return false; +} + +void CIndustrialStack::ClearAllElements() +{ + for (uint32_t i = 0; i < deviceQueues.Size(); i++) + { + assert(NULL != deviceQueues[i]); + deviceQueues[i]->elements.RemoveAll(true); + } +} + +bool CIndustrialStack::SendMostMessage(CISMostMsg *message) +{ + assert(NULL != msnw); + assert(NULL != message); + if (NULL == message) + return false; +#ifdef CON_TRACE + ConsolePrintfStart(PRIO_HIGH, "IS TX, node:0x%X," \ + " fblock:0x%X, func:0x%X, op:0x%X pl:0x[", message->TargetAddress, + message->FBlock, message->Func, message->OpType); + for (uint32_t i = 0; i < message->PayloadLen; i++) + ConsolePrintfContinue(" %02X", message->Payload[i]); + ConsolePrintfExit(" ]\n"); +#endif + bool success = msnw->SendMostMessage(message); + for (uint32_t i = 0; success && i < intEventListeners.Size(); i++) + { + intEventListeners[i]->OnMostMessage(this, message); + } + return success; +} + +void CIndustrialStack::Unsynchronize() +{ + assert(NULL != msnw); + return msnw->Unsynchronize(); +} + +void CIndustrialStack::AddInternalEventListener(CSInternalEvent* callback) +{ + intEventListeners.PushBack(callback); +} + +/*-------------------------- + * Callback from MSN Wrapper + *-------------------------- + */ + +void CIndustrialStack::OnReceivedMostMessage(CISMostMsg *rcvMessage) +{ + assert(NULL != rcvMessage); + assert(rcvMessage->IsValid); +#ifdef CON_TRACE + ConsolePrintfStart(PRIO_HIGH, "IS RX, node:0x%X," \ + " fblock:0x%X, func:0x%X, op:0x%X pl:0x[", rcvMessage->SourceAddress, + rcvMessage->FBlock, rcvMessage->Func, rcvMessage->OpType); + for (uint32_t i = 0; i < rcvMessage->PayloadLen; i++) + ConsolePrintfContinue(" %02X", rcvMessage->Payload[i]); + ConsolePrintfExit(" ]\n"); +#endif + if (CISOpType_ERROR == rcvMessage->OpType + || CISOpType_ERRORACK == rcvMessage->OpType) + { + //Ignore Destroy Resource Errors + if (rcvMessage->Func != 0x800) + { + ConsolePrintfStart(PRIO_ERROR, RED"Received error message, node:0x%X," \ + " fblock:0x%X, func:0x%X, op:0x%X pl:0x[", rcvMessage->SourceAddress, + rcvMessage->FBlock, rcvMessage->Func, rcvMessage->OpType); + for (uint32_t i = 0; i < rcvMessage->PayloadLen; i++) + ConsolePrintfContinue(" %02X", rcvMessage->Payload[i]); + ConsolePrintfExit(" ]"RESETCOLOR"\n"); + } + } + for (uint32_t i = 0; i < deviceQueues.Size(); i++) + { + assert(NULL != deviceQueues[i]); + IISElement *element = deviceQueues[i]->elements[0]; + if (NULL == element) + continue; + ISReturn_t state = element->OnMostMessage(this, rcvMessage); + if (state == ISReturn_NoChange) + continue; + deviceQueues[i]->elements.PopFront(); + HandleStateChange(state, element); + element->RemoveReference(); + } + for (uint32_t i = 0; i < intEventListeners.Size(); i++) + { + intEventListeners[i]->OnMostMessage(this, rcvMessage); + } +} + +void CIndustrialStack::OnSyncStateChanged(bool isSynced) +{ + for (uint32_t i = 0; i < intEventListeners.Size(); i++) + { + assert(NULL != intEventListeners[i]); + intEventListeners[i]->OnSyncStateChanged(this, isSynced); + } +} + +void CIndustrialStack::OnControlReadEnd() +{ + for (uint32_t i = 0; i < intEventListeners.Size(); i++) + { + assert(NULL != intEventListeners[i]); + intEventListeners[i]->OnControlReadEnd(this); + } +} + +/*-------------------------- + * Private helper functions: + *-------------------------- + */ + +#ifdef DISABLE_PARALLEL_MODE +CISDeviceQueue *CIndustrialStack::GetQueue(uint16_t nodeAddress) +{ + CISDeviceQueue *dev = deviceQueues[0]; + if (NULL == dev) + { + ConsolePrintf(PRIO_HIGH, GREEN"Creating new Device Queue"RESETCOLOR"\n"); + dev = new CISDeviceQueue(0); + deviceQueues.PushBack(dev); + } + return dev; +} +#else +CISDeviceQueue *CIndustrialStack::GetQueue(uint16_t nodeAddress) +{ + CISDeviceQueue *dev = NULL; + if (nodeAddress >= 0x400 && nodeAddress <= 0x4FF) + nodeAddress -= 0x300; + for (uint32_t i = 0; i < deviceQueues.Size(); i++) + { + if (deviceQueues[i]->GetNodeAddress() == nodeAddress) + { + dev = deviceQueues[i]; + break; + } + } + if (NULL == dev) + { + ConsolePrintf(PRIO_HIGH, GREEN"Creating new Device Queue for"\ + " node address:0x%02X"RESETCOLOR"\n", nodeAddress); + dev = new CISDeviceQueue(nodeAddress); + deviceQueues.PushBack(dev); + } + return dev; +} +#endif + +void CIndustrialStack::HandleStateChange(ISReturn_t state, IISElement * element) +{ + assert(NULL != element); + switch (state) + { + case ISReturn_Failure: + case ISReturn_Timeout: + ConsolePrintf(PRIO_ERROR, RED"Job '%s' %s"RESETCOLOR"\n", + element->ElementName, state == ISReturn_Failure ? "failed" : "timed out"); + case ISReturn_Success: + if (NULL != element->Callback) + element->Callback->ElementProcessed(this, state, element); + break; + case ISReturn_NoChange: + //Nothing to do + break; + default: + assert(false); + } +} diff --git a/Src/Network/IndustrialStack.h b/Src/Network/IndustrialStack.h new file mode 100644 index 0000000..fc42f66 --- /dev/null +++ b/Src/Network/IndustrialStack.h @@ -0,0 +1,238 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class (common part). + */ +/*----------------------------------------------------------*/ + +#ifndef INDUSTRIAL_STACK_H +#define INDUSTRIAL_STACK_H + +#include +#include +#include +#include +#include "SafeVector.h" +#include "IndustrialStack_Types.h" +#include "IndustrialStack_MNS.h" +#include "Console.h" + +class CIndustrialStack : IISNetServiceWrapperCB +{ +private: + CISNetServiceWrapper *msnw; + CSafeVector deviceQueues; + CSafeVector intEventListeners; + CISDeviceQueue *GetQueue(uint16_t deviceType); + void HandleStateChange(ISReturn_t state, IISElement * element); +public: + CIndustrialStack(uint8_t deviceApi, int controlRxHandle, int controlTxHandle); + virtual ~CIndustrialStack(); + void ServiceStack(uint32_t time); + void EnqueueElement(uint16_t nodeAddress, IISElement *element); + bool ElementsPending(); + bool ElementsPending(uint16_t ingoreNodeAddress); + void ClearAllElements(); + bool SendMostMessage(CISMostMsg *message); + void Unsynchronize(); + void AddInternalEventListener(CSInternalEvent* callback); + + /* Callback from IISNetServiceWrapperCB interface */ + virtual void OnReceivedMostMessage(CISMostMsg *rcvMessage); + virtual void OnSyncStateChanged(bool isSynced); + virtual void OnControlReadEnd(); +}; + +class CISWaitElement : public IISElement +{ +protected: + uint32_t startTime; + uint32_t timeout; +public: + CISWaitElement() : IISElement(), startTime(0), timeout(2000) + { + ElementName = "Wait Element"; + } + + void SetTimeout(uint32_t timeInMillis) + { + timeout = timeInMillis; + } + + virtual ISReturn_t Service(CIndustrialStack *iStack, uint32_t time) + { + assert(NULL != iStack); + if (0 == startTime) + startTime = time; + else if ( ( time - startTime >= timeout ) ) + return ISReturn_Success; + return ISReturn_NoChange; + } + + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *rcvMessage) + { + assert(NULL != iStack); + return ISReturn_NoChange; + } +}; + +class CISWaitForPendingElements : public CISWaitElement +{ +private: + uint16_t ignoreNode; +public: + CISWaitForPendingElements(uint16_t ingoreNodeAddress) + : CISWaitElement(), ignoreNode(ingoreNodeAddress) + { + } + + virtual ISReturn_t Service(CIndustrialStack *iStack, uint32_t time) + { + assert(NULL != iStack); + if (!iStack->ElementsPending(ignoreNode)) + return ISReturn_Success; + return (CISWaitElement::Service(iStack, time) == ISReturn_NoChange) + ? ISReturn_NoChange : ISReturn_Timeout; + } +}; + +class CISSendMostMsgElement : public CISWaitElement +{ +private: + uint8_t retryCount; +public: + CISMostMsg Request; + CISMostMsg Response; + bool WaitForResponse; + CISOpType_t WaitForResponseOpType; + + CISSendMostMsgElement() : CISWaitElement(), retryCount(4), + WaitForResponse(false), + WaitForResponseOpType(CISOpType_INVALID) + { + timeout = 500; + } + + virtual ISReturn_t Service(CIndustrialStack *iStack, uint32_t time) + { + assert(NULL != iStack); + if (0 == startTime) + { + CISWaitElement::Service(iStack, time); + if (Request.IsValid == false || Request.FBlock == 0xFFFFFFFF + || Request.Func == 0xFFFFFFFF || Request.Inst == 0xFFFFFFFF + || Request.OpType == CISOpType_INVALID) + { + ConsolePrintf(PRIO_ERROR, + RED"CISSendMostMsgElement %s invalid MOST message to be"\ + " sent"RESETCOLOR"\n", ElementName); + return ISReturn_Failure; + } + bool success = iStack->SendMostMessage(&Request); + if (success && !WaitForResponse) + return ISReturn_Success; + return success ? ISReturn_NoChange : ISReturn_Failure; + } + ISReturn_t waitState = CISWaitElement::Service(iStack, time); + if (ISReturn_Success == waitState) + { + if (0 == --retryCount) + { + ConsolePrintf(PRIO_ERROR, RED"All High level retries failed for Request '%s',"\ + " node 0x%X!"RESETCOLOR"\n", + ElementName, Request.TargetAddress); + return ISReturn_Timeout; + } + //Retry sending + ConsolePrintf(PRIO_HIGH, YELLOW"High level retry after timeout for Request '%s',"\ + " node 0x%X"RESETCOLOR"\n", + ElementName, Request.TargetAddress); + startTime = time; + return (iStack->SendMostMessage(&Request)) ? ISReturn_NoChange : ISReturn_Failure; + } + return ISReturn_NoChange; + } + + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *rcvMessage) + { + if (!WaitForResponse) + return ISReturn_NoChange; + assert(NULL != iStack); + assert(NULL != rcvMessage); + assert(Request.IsValid); + assert(rcvMessage->IsValid); + + uint32_t requestAddr = Request.TargetAddress; + uint32_t responseAddr = rcvMessage->SourceAddress; + if (requestAddr >= 0x400 && requestAddr <= 0x4FF ) + requestAddr -= 0x300; + if (responseAddr >= 0x400 && responseAddr <= 0x4FF ) + responseAddr -= 0x300; + + if ( ( requestAddr < 0x300 || requestAddr > 0x3FF ) + && ( responseAddr != requestAddr)) + return ISReturn_NoChange; + + if ( rcvMessage->FBlock == Request.FBlock + && rcvMessage->Func == Request.Func) + { + if (rcvMessage->OpType == WaitForResponseOpType) + { + Response.DeepCopy(rcvMessage); + return ISReturn_Success; + } + else if(0 == --retryCount) + { + Response.DeepCopy(rcvMessage); + return ISReturn_Failure; + } + //Retry sending + ConsolePrintf(PRIO_HIGH, YELLOW"High level retry after wrong result for Request '%s',"\ + " node 0x%X"RESETCOLOR"\n", + ElementName, Request.TargetAddress); + return (iStack->SendMostMessage(&Request)) ? ISReturn_NoChange : ISReturn_Failure; + } + return ISReturn_NoChange; + } +}; + +class CSInternalEvent : public IISElement +{ +public: + virtual ISReturn_t Service(CIndustrialStack *iStack, uint32_t time) + { + //Must not be called + return ISReturn_Failure; + } + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r) + { + //Must not be called + return ISReturn_Failure; + } + virtual void OnSyncStateChanged(CIndustrialStack *iStack, bool isSynced) {} + virtual void OnControlReadEnd(CIndustrialStack *iStack) {} +}; + +#endif //INDUSTRIAL_STACK_H diff --git a/Src/Network/IndustrialStack_ApiGeneric.h b/Src/Network/IndustrialStack_ApiGeneric.h new file mode 100644 index 0000000..730f157 --- /dev/null +++ b/Src/Network/IndustrialStack_ApiGeneric.h @@ -0,0 +1,125 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class (Generic commands). + */ +/*----------------------------------------------------------*/ + +#ifndef INDUSTRIAL_STACK_API_GENERIC_H +#define INDUSTRIAL_STACK_API_GENERIC_H + +#include "IndustrialStack.h" +#include "Network.h" +#include "Types.h" + +class CGeneric_SendConfigOk : public CISSendMostMsgElement +{ +public: + CGeneric_SendConfigOk(bool isOkay) + { + ElementName = "CGeneric_SendConfigOk"; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = 0x3C8; + Request.FBlock = 0x2; + Request.Func = 0xA00; + Request.Inst = 0x00; + Request.OpType = CISOpType_STATUS; + Request.PayloadLen = 1; + Request.Payload[0] = isOkay; + } +}; + +class CGeneric_GetMacAddress : public CISSendMostMsgElement +{ +public: + CGeneric_GetMacAddress(uint16_t nodeAddress) + { + ElementName = "CGeneric_GetMacAddress"; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x1; + Request.Func = 0x013; + Request.Inst = 0x00; + Request.OpType = CISOpType_GET; + Request.PayloadLen = 0; + } +}; + +class CGeneric_GetGroupAddress : public CISSendMostMsgElement +{ +public: + CGeneric_GetGroupAddress(uint16_t nodeAddress) + { + ElementName = "CGeneric_GetGroupAddress"; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x1; + Request.Func = 0x004; + Request.Inst = 0x00; + Request.OpType = CISOpType_GET; + Request.PayloadLen = 0; + } +}; + +class CGeneric_SendMostMessage : public CISSendMostMsgElement +{ +public: + CGeneric_SendMostMessage(const uint8_t *pPayload, uint32_t payloadLength, + uint16_t nodeAddress, uint8_t fblockId, uint8_t instanceId, + uint16_t functionId, uint8_t opTypeRequest, uint8_t opTypeResponse) + { + if (payloadLength > sizeof(Request.Payload)) + { + ConsolePrintf(PRIO_ERROR, RED"CGeneric_SendMostMessage was called"\ + " with to big payload size"RESETCOLOR"\n"); + payloadLength = sizeof(Request.Payload); + } + ElementName = "CGeneric_SendMostMessage"; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = fblockId; + Request.Func = functionId; + Request.Inst = instanceId; + Request.OpType = (CISOpType_t)opTypeRequest; + Request.PayloadLen = payloadLength; + if (0 != payloadLength) + memcpy(Request.Payload, pPayload, payloadLength); + if( 0xFF != opTypeResponse ) + { + WaitForResponse = true; + WaitForResponseOpType = (CISOpType_t)opTypeResponse; + } + } +}; + +#endif //INDUSTRIAL_STACK_API_GENERIC_H diff --git a/Src/Network/IndustrialStack_ApiV1.h b/Src/Network/IndustrialStack_ApiV1.h new file mode 100644 index 0000000..1c7f45a --- /dev/null +++ b/Src/Network/IndustrialStack_ApiV1.h @@ -0,0 +1,712 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class (API V1 commands). + */ +/*----------------------------------------------------------*/ + +#ifndef INDUSTRIAL_STACK_API_V1_H +#define INDUSTRIAL_STACK_API_V1_H + +#define ISO_PACKET_SIZE 188 + +#include "IndustrialStack.h" +#include "Network.h" +#include "Types.h" + +class CV1_OnMostRx : public CSInternalEvent +{ +private: + CNetworkDevice *device; +public: + CV1_OnMostRx(CNetworkDevice *d) : device(d) + { + } + + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r) + { + assert(NULL != device); + assert(r->IsValid); + if (0x0 == r->FBlock) //INIC Block + { + switch (r->Func) + { + case 0x30E: //Change EUI48 + if (CISOpType_STATUS == r->OpType && r->PayloadLen >= 6) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, true, + r->SourceAddress, r->Payload[0], r->Payload[1], + r->Payload[2], r->Payload[3], r->Payload[4], r->Payload[5]); + } + else if (CISOpType_ERROR == r->OpType) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, false, + r->SourceAddress, 0,0,0,0,0,0); + } + break; + } + } + return ISReturn_NoChange; + } + + virtual void OnControlReadEnd(CIndustrialStack *iStack) + { + assert(NULL != device); + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnControlChannelReadEnd(device); + } +}; + +class CV1_ChangeEUI48 : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; +public: + CV1_ChangeEUI48(CNetworkDevice *d, uint16_t nodeAddress, + uint8_t macAddress1, uint8_t macAddress2, uint8_t macAddress3, + uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6, + bool persistent) : device(d) + { + ElementName = "CV1_ChangeEUI48"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x30E; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = macAddress1; + Request.Payload[1] = macAddress2; + Request.Payload[2] = macAddress3; + Request.Payload[3] = macAddress4; + Request.Payload[4] = macAddress5; + Request.Payload[5] = macAddress6; + Request.Payload[6] = persistent; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + assert(this == element); + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 6; + if (success) + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, true, + Request.TargetAddress, Response.Payload[0],Response.Payload[1], + Response.Payload[2], Response.Payload[3], Response.Payload[4], + Response.Payload[5]); + else + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, false, + Request.TargetAddress, 0, 0, 0, 0, 0, 0); + } +}; + +class CV1_NetworkStartup : public CISWaitElement +{ +private: + CNetworkDevice *device; +public: + CV1_NetworkStartup(CNetworkDevice *d, bool isTimingMaster, uint16_t packetBW) : device(d) + { + ElementName = "CV1_NetworkStartup is not implemented!"; + timeout = 1; + } +}; + +class CV1_NetworkShutdown : public CISWaitElement +{ +private: + CNetworkDevice *device; +public: + CV1_NetworkShutdown(CNetworkDevice *d) : device(d) + { + ElementName = "CV1_NetworkShutdown is not implemented!"; + timeout = 1; + } +}; + +class CV1_TsiPortCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; +public: + CV1_TsiPortCreate(CNetworkDevice *d, uint16_t nodeAddress, V1TsiPortInstance_t tsiPort, V1TsiPortMode tsiMode) : device(d) + { + ElementName = "CV2_TsiPortCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x400; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 2; + Request.Payload[0] = tsiPort; + Request.Payload[1] = tsiMode; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnOpenTsiV1(device, (ISReturn_Success == result), Request.TargetAddress); + } +}; + +class CV1_TsiSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + V1TsiPortInstance_t tsiPort; + EPDataType_t epType; + EPDirection_t epDir; + uint16_t blockWidthTsi; + uint32_t tag; +public: + CV1_TsiSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, V1TsiPortInstance_t tp, + EPDataType_t ept, EPDirection_t epd, uint16_t bw, uint32_t t) : + device(d), tsiPort(tp), epType(ept), epDir(epd), blockWidthTsi(bw), tag(t) + { + if ( EP_Isochron != epType ) + { + ConsolePrintf( PRIO_ERROR, RED"CV1_TsiSocketCreate: Unsupported Data type"RESETCOLOR"\n" ); + return; + } + ElementName = "CV1_TsiSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x403; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = tsiPort; + Request.Payload[1] = epDir; + Request.Payload[2] = epType; + Request.Payload[3] = blockWidthTsi / 256; + Request.Payload[4] = blockWidthTsi % 256; + Request.Payload[5] = ISO_PACKET_SIZE / 256; + Request.Payload[6] = ISO_PACKET_SIZE % 256; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint8_t tsiPortHandle = 0xFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 1; + if (success) + tsiPortHandle = Response.Payload[0]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateTsiSocketV1(device, success, Request.TargetAddress, tsiPort, + epType, epDir, blockWidthTsi, tsiPortHandle, tag); + } +}; + +class CV1_MlbPortCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; +public: + CV1_MlbPortCreate(CNetworkDevice *d, uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed) : device(d) + { + ElementName = "CV2_MlbPortCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x400; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 4; + Request.Payload[0] = 0x00; //SCM_PORT_ID_MEDIALB; + Request.Payload[1] = 0xFF; + Request.Payload[2] = 0x00; //SCM_PORT_CFG_MLB_MODE_CTL; + Request.Payload[3] = mlbSpeed; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnOpenMlbV1(device, (ISReturn_Success == result), Request.TargetAddress); + } +}; + +class CV1_MlbSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + EPDataType_t epType; + EPDirection_t epDir; + uint16_t mlbChannelAddress; + uint16_t blockWidthMlb; + uint32_t tag; +public: + CV1_MlbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ept, EPDirection_t epd, + uint16_t mlb, uint16_t bw, uint32_t t) : device(d), epType(ept), epDir(epd), + mlbChannelAddress(mlb), blockWidthMlb(bw), tag(t) + { + ElementName = "CV1_MlbSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x403; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = 0x00; //SCM_PORT_ID_MEDIALB + Request.Payload[1] = epDir; + Request.Payload[2] = epType; + Request.Payload[3] = blockWidthMlb / 256; + Request.Payload[4] = blockWidthMlb % 256; + Request.Payload[5] = mlbChannelAddress / 256; + Request.Payload[6] = mlbChannelAddress % 256; + if ( EP_Isochron == epType ) + { + Request.PayloadLen = 10; + Request.Payload[7] = ISO_PACKET_SIZE / 256; + Request.Payload[8] = ISO_PACKET_SIZE % 256; + Request.Payload[9] = 0x1; //SCM_FLOW_CONTROL_ON + } + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint8_t mlbPortHandle = 0xFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 1; + if (success) + mlbPortHandle = Response.Payload[0]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateMlbSocketV1(device, success, Request.TargetAddress, epType, + epDir, blockWidthMlb, mlbChannelAddress, mlbPortHandle, tag); + } +}; + +class CV1_SplittedMlbSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + EPDataType_t epType; + EPDirection_t epDir; + uint16_t mlbChannelAddress; + uint16_t blockWidthMlb; + uint16_t splittedOffset; + uint16_t blockWidthCombined; + uint32_t tag; +public: + CV1_SplittedMlbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, + EPDataType_t ept, EPDirection_t epd, uint16_t mlb, uint16_t bw, + uint16_t so, uint16_t bwc, uint32_t t) + : device(d), epType(ept), epDir(epd), mlbChannelAddress(mlb), + blockWidthMlb(bw), splittedOffset(so), blockWidthCombined(bwc), tag(t) + { + ElementName = "CV1_SplittedMlbSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x403; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 9; + Request.Payload[0] = 0x00; //SCM_PORT_ID_MEDIALB + Request.Payload[1] = epDir; + Request.Payload[2] = epType; + Request.Payload[3] = blockWidthMlb / 256; + Request.Payload[4] = blockWidthMlb % 256; + Request.Payload[5] = mlbChannelAddress / 256; + Request.Payload[6] = mlbChannelAddress % 256; + Request.Payload[7] = ( uint8_t )( splittedOffset & 0xFF ); + Request.Payload[8] = ( uint8_t )( blockWidthCombined & 0xFF ); + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint8_t mlbPortHandle = 0xFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 1; + if (success) + mlbPortHandle = Response.Payload[0]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateMlbSocketV1(device, success, Request.TargetAddress, epType, + epDir, blockWidthMlb, mlbChannelAddress, mlbPortHandle, tag); + } +}; + +class CV1_MostSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + EPDataType_t epType; + EPDirection_t epDir; + uint16_t connectionLabel; + uint16_t blockWidthMost; + uint32_t tag; +public: + CV1_MostSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ept, EPDirection_t epd, + uint16_t cl, uint16_t bw, uint32_t t) : device(d), epType(ept), epDir(epd), + connectionLabel(cl), blockWidthMost(bw), tag(t) + { + ElementName = "CV1_MostSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x403; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.Payload[0] = 0x02; //SCM_PORT_ID_MOST + Request.Payload[1] = epDir; + Request.Payload[2] = epType; + Request.Payload[3] = blockWidthMost / 256; + Request.Payload[4] = blockWidthMost % 256; + switch( epType ) + { + case EP_Isochron: + Request.Payload[5] = ISO_PACKET_SIZE / 256; + Request.Payload[6] = ISO_PACKET_SIZE % 256; + if( SCM_IN == epDir ) + { + Request.PayloadLen = 10; + Request.Payload[7] = 0x1; //SCM_PA_CL + Request.Payload[8] = connectionLabel / 256; + Request.Payload[9] = connectionLabel % 256; + } + else + { + Request.PayloadLen = 8; + Request.Payload[7] = 0x0; //SCM_NOT_PA + } + break; + case EP_Synchron: + if( SCM_IN == epDir ) + { + Request.PayloadLen = 8; + Request.Payload[5] = 0x1; //SCM_PA_CL + Request.Payload[6] = connectionLabel / 256; + Request.Payload[7] = connectionLabel % 256; + } + else + { + Request.PayloadLen = 6; + Request.Payload[5] = 0x0; //SCM_NOT_PA + } + break; + default: + timeout = 1; + ConsolePrintf( PRIO_ERROR, RED"CreateMostSocket: Unsupported Data type"RESETCOLOR"\n" ); + return; + } + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint8_t usbSocketHandle = 0xFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 1; + if (success) + usbSocketHandle = Response.Payload[0]; + if( success && SCM_OUT == epDir && Response.PayloadLen >= 4) + connectionLabel = ( Response.Payload[2] << 8) | Response.Payload[3]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateMostSocketV1(device, success, Request.TargetAddress, epType, + epDir, blockWidthMost, connectionLabel, usbSocketHandle, tag); + } +}; + +class CV1_ConnectSockets : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint16_t nodeAddress; + EPDataType_t epType; + uint16_t inHandle; + uint16_t outHandle; + uint32_t tag; +public: + CV1_ConnectSockets(CNetworkDevice *d, uint16_t nodeAddress, uint16_t iH, + uint16_t oH, uint32_t t) : device(d), inHandle(iH), outHandle(oH), tag(t) + { + ElementName = "CV1_ConnectSockets"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x405; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 2; + Request.Payload[0] = inHandle; + Request.Payload[1] = outHandle; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint8_t conHandle = 0xFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 1; + if (success) + conHandle = Response.Payload[0]; + + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnConnectSocketsV1(device, success, + Request.TargetAddress, inHandle, outHandle, conHandle, tag); + } +}; + +class CV1_DestroySocket : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint8_t handle; + uint32_t tag; +public: + CV1_DestroySocket(CNetworkDevice *d, int16_t nodeAddress, uint8_t h, + uint32_t t) : device(d), handle(h), tag(t) + { + ElementName = "CV1_DestroySocket"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x404; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 1; + Request.Payload[0] = handle; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnDestroySocketV1(device, (ISReturn_Success == result), + Request.TargetAddress, handle, tag); + } +}; + +class CV1_DisconnectSockets : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint8_t handle; + uint32_t tag; +public: + CV1_DisconnectSockets(CNetworkDevice *d, int16_t nodeAddress, uint8_t h, + uint32_t t) : device(d), handle(h), tag(t) + { + ElementName = "CV1_DisconnectSockets"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x406; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 1; + Request.Payload[0] = handle; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnDisconnectSocketsV1(device, (ISReturn_Success == result), + Request.TargetAddress, handle, tag); + } +}; + +class CV1_StreamPortOpen : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + V1I2SPortClkDriveMode_t portMode; + V1I2SStreamingPortMode_t streamPortMode; + V1I2SStreamingDataFormat_t format; + uint32_t tag; +public: + CV1_StreamPortOpen(CNetworkDevice *d, int16_t nodeAddress, + V1I2SPortClkDriveMode_t pm, V1I2SStreamingPortMode_t spm, + V1I2SStreamingDataFormat_t f, uint32_t t) : device(d), portMode(pm), + streamPortMode(spm), format(f), tag(t) + { + ElementName = "CV1_StreamPortOpen"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x400; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 4; + Request.Payload[0] = 0x03; //Streaming Port + Request.Payload[1] = portMode; + Request.Payload[2] = streamPortMode; + Request.Payload[3] = format; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnOpenI2SPortV1(device, (ISReturn_Success == result), + Request.TargetAddress, portMode, format, tag); + } +}; + +class CV1_StreamSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + EPDirection_t epDir; + uint16_t blockWidthI2S; + V1I2SPin_t pin; + uint32_t tag; +public: + CV1_StreamSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDirection_t epd, + uint16_t bw, V1I2SPin_t p, uint32_t t) : device(d), epDir(epd), + blockWidthI2S(bw), pin(p), tag(t) + { + ElementName = "CV1_StreamSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x403; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 6; + Request.Payload[0] = 0x03; //Streaming Port + Request.Payload[1] = epDir; + Request.Payload[2] = 0x0; //SyncData + Request.Payload[3] = blockWidthI2S / 256; + Request.Payload[4] = blockWidthI2S % 256; + Request.Payload[5] = (uint8_t)pin; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint8_t i2sSocketHandle = 0xFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 1; + if (success) + i2sSocketHandle = Response.Payload[0]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateI2SSocketV1(device, success, + Request.TargetAddress, epDir, blockWidthI2S, pin, i2sSocketHandle, tag); + } +}; + +class CV1_SplittedStreamSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + EPDirection_t epDir; + uint16_t blockWidthI2S; + V1I2SPin_t pin; + uint32_t tag; +public: + CV1_SplittedStreamSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDirection_t epd, + uint16_t bw, uint16_t splittedOffset, uint16_t blockWidthCombined, V1I2SPin_t p, + uint32_t t) : device(d), epDir(epd), blockWidthI2S(bw), pin(p), tag(t) + { + ElementName = "CV1_SplittedStreamSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x403; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = 0x03; //Streaming Port + Request.Payload[1] = epDir; + Request.Payload[2] = 0x0; //SyncData + Request.Payload[3] = blockWidthI2S / 256; + Request.Payload[4] = blockWidthI2S % 256; + Request.Payload[5] = (uint8_t)pin; + Request.Payload[6] = ( uint8_t )( splittedOffset & 0xFF ); + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint8_t i2sSocketHandle = 0xFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 1; + if (success) + i2sSocketHandle = Response.Payload[0]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateI2SSocketV1(device, success, + Request.TargetAddress, epDir, blockWidthI2S, pin, i2sSocketHandle, tag); + } +}; + +#endif //INDUSTRIAL_STACK_API_V1_H \ No newline at end of file diff --git a/Src/Network/IndustrialStack_ApiV3.h b/Src/Network/IndustrialStack_ApiV3.h new file mode 100644 index 0000000..7e37789 --- /dev/null +++ b/Src/Network/IndustrialStack_ApiV3.h @@ -0,0 +1,1123 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class (API V3 commands). + */ +/*----------------------------------------------------------*/ + +#ifndef INDUSTRIAL_STACK_API_V3_H +#define INDUSTRIAL_STACK_API_V3_H + +#include "IndustrialStack.h" +#include "Network.h" +#include "Types.h" + +class CV3_OnMostRx : public CSInternalEvent +{ +private: + CNetworkDevice *device; +public: + CV3_OnMostRx(CNetworkDevice *d) : device(d) + { + } + + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r) + { + assert(NULL != device); + assert(r->IsValid); + if (0x0 == r->FBlock) //INIC Block + { + switch (r->Func) + { + case 0x520: //MOST Network Status + if (CISOpType_STATUS == r->OpType && r->PayloadLen == 11) + { + bool mpValChanged = ( 0 != ( r->Payload[1] & 0x1 ) ); //This is a 16 Bit value!! + bool systemNotOk = false; //Not transmitted any longer + bool mostAvailable = ( 0 != ( r->Payload[2] & 0x1 ) ); + uint8_t availableSubState = r->Payload[3]; + uint8_t availableTransition = r->Payload[4]; + uint16_t nodeAddress = r->Payload[5] << 8 + | r->Payload[6]; + uint8_t nodePos = ( r->Payload[7] & 0x3F ); + uint8_t maxPos = ( r->Payload[8] ); + uint16_t packetBW = r->Payload[9] << 8 + | r->Payload[10]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnNetworkState(device, + mpValChanged, systemNotOk, mostAvailable, availableSubState, + availableTransition, nodeAddress, nodePos, maxPos, packetBW); + } + break; + case 0x524: //MOST Network Startup + if (CISOpType_STATUS == r->OpType) + { + if (r->Payload[0] == 0x20 && r->Payload[1] == 0x04 && r->Payload[2] == 0x40) + return ISReturn_NoChange; //Startup was called, but we already have locked network, so ignore this error + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnNetworkStartupV3(device, (CISOpType_STATUS == r->OpType)); + } + break; + case 0x527: //RBD Result + if (CISOpType_STATUS == r->OpType && r->PayloadLen == 5) + { + uint8_t result = r->Payload[0]; + uint8_t position = r->Payload[1]; + uint8_t status = r->Payload[2]; + uint16_t id = r->Payload[3] << 8 + | r->Payload[4]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnRbdResultV3(device, r->SourceAddress, + result, position, status, id); + } + break; + } + } + else if (0x1 == r->FBlock) //NetBlock FBlock + { + switch (r->Func) + { + case 0x004: //GroupAddress Changed + if (CISOpType_STATUS == r->OpType && r->PayloadLen >= 2) + { + uint16_t groupAddress = r->Payload[0] << 8 + | r->Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostDeviceType(device, true, r->SourceAddress, groupAddress ); + } + else if (CISOpType_ERROR == r->OpType) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostDeviceType(device, false, r->SourceAddress, 0xFFFF ); + } + break; + case 0x013: //NetBlock EUI48 + if (CISOpType_STATUS == r->OpType && r->PayloadLen >= 6) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, true, + r->SourceAddress, r->Payload[0], r->Payload[1], + r->Payload[2], r->Payload[3], r->Payload[4], r->Payload[5]); + } + else if (CISOpType_ERROR == r->OpType) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, false, + r->SourceAddress, 0,0,0,0,0,0); + } + break; + } + } + return ISReturn_NoChange; + } + + virtual void OnSyncStateChanged(CIndustrialStack *iStack, bool isSynced) + { + assert(NULL != device); + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnSync(device, isSynced ); + } +}; + +class CV3_NetworkStartup : public CISSendMostMsgElement +{ +private: + CNetworkDevice *device; +public: + CV3_NetworkStartup(CNetworkDevice *d, uint16_t autoForcedNotAvailable, uint16_t packetBW) : device(d) + { + ElementName = "CV3_NetworkStartup"; + WaitForResponseOpType = CISOpType_RESULT; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = 0x1; + Request.FBlock = 0x0; + Request.Func = 0x524; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 4; + Request.Payload[0] = autoForcedNotAvailable / 256; + Request.Payload[1] = autoForcedNotAvailable % 256; + Request.Payload[2] = packetBW / 256; + Request.Payload[3] = packetBW % 256; + } +}; + +class CV3_MostNetworkConfiguration : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; +public: + CV3_MostNetworkConfiguration(CNetworkDevice *d, uint16_t nodeAddress, + uint8_t macAddress1, uint8_t macAddress2, uint8_t macAddress3, + uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6, + bool promiscuous) : device(d) + { + ElementName = "CV3_MostNetworkConfiguration"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_STATUS; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x521; + Request.Inst = 0x00; + Request.OpType = CISOpType_SETGET; + Request.PayloadLen = 24; + Request.Payload[0] = 0; // HB: MASK + Request.Payload[1] = + ( 1 << 3 ) // LB: MASK: Packet Filter Mode is considered + | ( 1 << 5 ); // LB: MASK: PacketEUI is considered + Request.Payload[2] = 0x00; // HB: Node Address (ignored) + Request.Payload[3] = 0x08; // LB: Node Address (ignored) + Request.Payload[4] = 0x00; // HB: Group Address (ignored) + Request.Payload[5] = 0x00; // LB: Group Address (ignored) + Request.Payload[6] = 0x00; // Control LLR Block Count (ignored) + Request.Payload[7] = 0x00; // HB: FilterMode (reserved) + Request.Payload[8] = promiscuous ? 0x0A : 0x08; // LB: FilterMode (Allow all Multicast) + Request.Payload[9] = 0x00; // HB: PacketHash_63to48 (ignored) + Request.Payload[10] = 0x00; // LB: PacketHash_63to48 (ignored) + Request.Payload[11] = 0x00; // HB: PacketHash_47to32 (ignored) + Request.Payload[12] = 0x00; // LB: PacketHash_47to32 (ignored) + Request.Payload[13] = 0x00; // HB: PacketHash_31to16 (ignored) + Request.Payload[14] = 0x00; // LB: PacketHash_31to16 (ignored) + Request.Payload[15] = 0x00; // HB: PacketHash_15to0 (ignored) + Request.Payload[16] = 0x00; // LB: PacketHash_15to0 (ignored) + Request.Payload[17] = macAddress1; // HB: PacketEUI48_47to32 + Request.Payload[18] = macAddress2; // LB: PacketEUI48_47to32 + Request.Payload[19] = macAddress3; // HB: PacketEUI48_31to16 + Request.Payload[20] = macAddress4; // LB: PacketEUI48_31to16 + Request.Payload[21] = macAddress5; // HB: PacketEUI48_15to0 + Request.Payload[22] = macAddress6; // LB: PacketEUI48_15to0 + Request.Payload[23] = 0x00; // PacketLLRTime (ignored) + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + assert(this == element); + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 18; + if (success) + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, true, + Request.TargetAddress, Response.Payload[15],Response.Payload[16], + Response.Payload[17], Response.Payload[18], Response.Payload[19], + Response.Payload[20]); + else + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnMostMacAddress(device, false, + Request.TargetAddress, 0, 0, 0, 0, 0, 0); + } +}; + +class CV3_DeviceAttach : public CISSendMostMsgElement +{ +public: + CV3_DeviceAttach() + { + ElementName = "CV3_DeviceAttach"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = 0x1; + Request.FBlock = 0x0; + Request.Func = 0x223; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + } +}; + +class CV3_Unsynchronize : public IISElement +{ +public: + CV3_Unsynchronize() + { + ElementName = "CV3_Unsynchronize"; + } + + virtual ISReturn_t Service(CIndustrialStack *iStack, uint32_t time) + { + assert(NULL != iStack); + iStack->Unsynchronize(); + return ISReturn_Success; + } + + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r) + { + return ISReturn_NoChange; + } +}; + +class CV3_DeviceSync : public CISSendMostMsgElement +{ +public: + CV3_DeviceSync(uint16_t nodeAddress, bool sync) + { + if (0x1 == nodeAddress) + { + ConsolePrintf(PRIO_ERROR, RED"CV3_DeviceSync was called with local" \ + " node address, this will fail!"RESETCOLOR"\n"); + } + ElementName = "CV3_DeviceSync"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x224; + Request.Inst = 0x01; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 1; + Request.Payload[0] = sync; + } +}; + +class CV3_NetworkShutdown : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; +public: + CV3_NetworkShutdown(CNetworkDevice *d) : device(d) + { + ElementName = "CV3_NetworkShutdown"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = 0x1; + Request.FBlock = 0x0; + Request.Func = 0x525; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 0; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnNetworkShutdownV3(device, (ISReturn_Success == result)); + } +}; + +class CV3_MlbPortCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; +public: + CV3_MlbPortCreate(CNetworkDevice *d, uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed) : device(d) + { + ElementName = "CV3_MlbPortCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x621; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 2; + Request.Payload[0] = 0x00; //Index + Request.Payload[1] = mlbSpeed; //ClockConfig + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint16_t mlbPortHandle = 0xFFFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (success) + mlbPortHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnOpenMlbV3(device, success, Request.TargetAddress, mlbPortHandle); + } +}; + +class CV3_UsbSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + EPDataType_t epType; + EPDirection_t epDir; + uint8_t endPointAddress; + uint16_t packetsPerFrame; + uint32_t tag; +public: + CV3_UsbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ept, + EPDirection_t epd, uint8_t epa, uint16_t p, uint32_t t) : device(d), epType(ept), + epDir(epd), endPointAddress(epa), packetsPerFrame(p), tag(t) + { + ElementName = "CV3_UsbSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x671; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = 0x12; // HB: UsbPortHandle + Request.Payload[1] = 0x00; // LB: UsbPortHandle + Request.Payload[2] = epDir; // Direction + Request.Payload[3] = epType; // DataType + Request.Payload[4] = endPointAddress; // EndpointAddress + Request.Payload[5] = packetsPerFrame / 256; // HB: Packets per USB frame + Request.Payload[6] = packetsPerFrame % 256; // LB: Packets per USB frame + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint16_t usbHandle = 0xFFFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (success) + usbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateUsbSocketV3(device, success, + Request.TargetAddress, epType, epDir, endPointAddress, usbHandle, tag); + } +}; + +class CV3_SplittedUsbSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + EPDataType_t epType; + EPDirection_t epDir; + uint8_t endPointAddress; + uint16_t packetsPerUsbFrame; + uint16_t bytesPerPacket; + uint32_t tag; + uint16_t usbHandle; +public: + CV3_SplittedUsbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ept, + EPDirection_t epd, uint8_t epa, uint16_t p, uint16_t bp, uint32_t t) : device(d), epType(ept), + epDir(epd), endPointAddress(epa), packetsPerUsbFrame(p), bytesPerPacket(bp), tag(t), + usbHandle(0xFFFF) + { + ElementName = "CV3_SplittedUsbSocketCreate-USB"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x671; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = 0x12; // HB: UsbPortHandle + Request.Payload[1] = 0x00; // LB: UsbPortHandle + Request.Payload[2] = epDir; // Direction + Request.Payload[3] = epType; // DataType + Request.Payload[4] = endPointAddress; // EndpointAddress + Request.Payload[5] = packetsPerUsbFrame / 256; // HB: Packets per USB frame + Request.Payload[6] = packetsPerUsbFrame % 256; // LB: Packets per USB frame + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (!success) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateSplittedUsbSocketV3(device, false, + Request.TargetAddress, epType, epDir, endPointAddress, 0xFFFF, 0xFFFF, tag); + return; + } + if (0x671 == Response.Func) + { + usbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + CreateSplitter(iStack); + } + else + { + uint16_t splitterHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateSplittedUsbSocketV3(device, success, + Request.TargetAddress, epType, epDir, endPointAddress, usbHandle, splitterHandle, tag); + } + } +private: + void CreateSplitter(CIndustrialStack *iStack) + { + assert(usbHandle != 0xFFFF); + ElementName = "CV3_SplittedUsbSocketCreate-Splitter"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + startTime = 0; + Request.FBlock = 0x0; + if( EPDIR_IN == epDir ) + Request.Func = 0x911; //SplitterCreate + else + Request.Func = 0x901; //CombinerCreate + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 6; + Request.Payload[0] = usbHandle / 256; // HB: SocketHandle IN/OUT + Request.Payload[1] = usbHandle % 256; // LB: SocketHandle IN/OUT + Request.Payload[2] = 0x0D; // HB: MOSTPortHandle + Request.Payload[3] = 0x00; // LB: MOSTPortHandle + Request.Payload[4] = bytesPerPacket / 256; // HB: BytesPerFrame + Request.Payload[5] = bytesPerPacket % 256; // LB: BytesPerFrame + iStack->EnqueueElement(Request.TargetAddress, this); + } +}; + +class CV3_MlbSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint16_t mlbPortHandle; + EPDataType_t epType; + EPDirection_t epDir; + uint8_t mlbChannelAddress; + uint16_t blockWidthMlb; + uint32_t tag; +public: + CV3_MlbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint16_t mlb, EPDataType_t ept, + EPDirection_t epd, uint8_t eca, uint16_t bw, uint32_t t) : device(d), mlbPortHandle(mlb), + epType(ept), epDir(epd), mlbChannelAddress(eca), blockWidthMlb(bw), tag(t) + { + ElementName = "CV3_MlbSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x631; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 8; + Request.Payload[0] = mlbPortHandle / 256; + Request.Payload[1] = mlbPortHandle % 256; + Request.Payload[2] = epDir; + Request.Payload[3] = epType; + Request.Payload[4] = blockWidthMlb / 256; + Request.Payload[5] = blockWidthMlb % 256; + Request.Payload[6] = mlbChannelAddress / 256; + Request.Payload[7] = mlbChannelAddress % 256; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint16_t mlbHandle = 0xFFFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (success) + mlbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateMlbSocketV3(device, success, + Request.TargetAddress, epType, epDir, blockWidthMlb, mlbChannelAddress, mlbHandle, tag); + } +}; + +class CV3_SplittedMlbSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint16_t mlbPortHandle; + EPDataType_t epType; + EPDirection_t epDir; + uint8_t mlbChannelAddress; + uint16_t blockWidthMlb; + uint16_t bytesPerPacket; + uint16_t mlbHandle; + uint32_t tag; +public: + CV3_SplittedMlbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint16_t mlb, EPDataType_t ept, + EPDirection_t epd, uint8_t eca, uint16_t bw, uint16_t bp, uint32_t t) : device(d), mlbPortHandle(mlb), + epType(ept), epDir(epd), mlbChannelAddress(eca), blockWidthMlb(bw), bytesPerPacket(bp), tag(t) + { + ElementName = "CV3_SplittedMlbSocketCreate-MLB"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x631; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 8; + Request.Payload[0] = mlbPortHandle / 256; + Request.Payload[1] = mlbPortHandle % 256; + Request.Payload[2] = epDir; + Request.Payload[3] = epType; + Request.Payload[4] = blockWidthMlb / 256; + Request.Payload[5] = blockWidthMlb % 256; + Request.Payload[6] = mlbChannelAddress / 256; + Request.Payload[7] = mlbChannelAddress % 256; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (!success) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateSplittedMlbSocketV3(device, false, + Request.TargetAddress, epType, epDir, blockWidthMlb, mlbChannelAddress, + 0xFFFF, 0xFFFF, tag); + return; + } + if (0x631 == Response.Func) + { + mlbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + CreateSplitter(iStack); + } + else + { + uint16_t splitterHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateSplittedMlbSocketV3(device, success, + Request.TargetAddress, epType, epDir, blockWidthMlb, mlbChannelAddress, + mlbHandle, splitterHandle, tag); + } + } +private: + void CreateSplitter(CIndustrialStack *iStack) + { + assert(mlbHandle != 0xFFFF); + ElementName = "CV3_SplittedMlbSocketCreate-Splitter"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + startTime = 0; + Request.FBlock = 0x0; + if( EPDIR_IN == epDir ) + Request.Func = 0x911; //SplitterCreate + else + Request.Func = 0x901; //CombinerCreate + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 6; + Request.Payload[0] = mlbHandle / 256; // HB: SocketHandle IN/OUT + Request.Payload[1] = mlbHandle % 256; // LB: SocketHandle IN/OUT + Request.Payload[2] = 0x0D; // HB: MOSTPortHandle + Request.Payload[3] = 0x00; // LB: MOSTPortHandle + Request.Payload[4] = bytesPerPacket / 256; // HB: BytesPerFrame + Request.Payload[5] = bytesPerPacket % 256; // LB: BytesPerFrame + iStack->EnqueueElement(Request.TargetAddress, this); + } +}; + +class CV3_MostSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint16_t nodeAddress; + EPDataType_t epType; + EPDirection_t epDir; + uint16_t connectionLabel; + uint16_t blockwidthMost; + uint32_t tag; +public: + CV3_MostSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ep, EPDirection_t epd, + uint16_t cl, uint16_t bw, uint32_t t) : device(d), epType(ep), + epDir(epd), connectionLabel(cl), blockwidthMost(bw), tag(t) + { + ElementName = "CV3_MostSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x611; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 8; + Request.Payload[0] = 0x0D; // HB: MostPortHandle + Request.Payload[1] = 0x00; // LB: MostPortHandle + Request.Payload[2] = epDir; // Dir + Request.Payload[3] = epType; // DataType + Request.Payload[4] = blockwidthMost / 256; // HB: bandwidth + Request.Payload[5] = blockwidthMost % 256; // LB: bandwidth + Request.Payload[6] = connectionLabel / 256; // HB: ConnectionLabel + Request.Payload[7] = connectionLabel % 256; // LB: ConnectionLabel + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint16_t conHandle = 0xFFFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 4; + if (success) + conHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + + if (success && SCM_OUT == epDir && Response.PayloadLen >= 4) + connectionLabel = ( Response.Payload[2] << 8 ) | Response.Payload[3]; + + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateMostSocketV3(device, success, + Request.TargetAddress, epType, epDir, blockwidthMost, connectionLabel, conHandle, tag); + } +}; + +class CV3_ConnectSockets : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint16_t nodeAddress; + EPDataType_t epType; + uint16_t inHandle; + uint16_t outHandle; + uint16_t offset; + uint32_t tag; +public: + CV3_ConnectSockets(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ep, uint16_t iH, + uint16_t oH, uint16_t o, uint32_t t) : device(d), epType(ep), inHandle(iH), + outHandle(oH), offset(o), tag(t) + { + ElementName = "CV3_ConnectSockets"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + + switch( epType ) + { + case EP_Isochron: + Request.Func = 0x861; //AVPCreate + Request.PayloadLen = 6; + Request.Payload[0] = inHandle / 256; // HB: in handle (will be filled by decomposerHandleIn) + Request.Payload[1] = inHandle % 256; // LB: in handle (will be filled by decomposerHandleIn) + Request.Payload[2] = outHandle / 256; // HB: out handle (will be filled by decomposerHandleOut) + Request.Payload[3] = outHandle % 256; // LB: out handle (will be filled by decomposerHandleOut) + Request.Payload[4] = 0x00; // HB: IsoPacketSize + Request.Payload[5] = 188; // LB: IsoPacketSize + break; + case EP_Synchron: + Request.Func = 0x871; // SyncCreate + Request.PayloadLen = 8; + Request.Payload[0] = inHandle / 256; // HB: in handle (will be filled by decomposerHandleIn) + Request.Payload[1] = inHandle % 256; // LB: in handle (will be filled by decomposerHandleIn) + Request.Payload[2] = outHandle / 256; // HB: out handle (will be filled by decomposerHandleOut) + Request.Payload[3] = outHandle % 256; // LB: out handle (will be filled by decomposerHandleOut) + Request.Payload[4] = 0x00; // 0: not muted by default; 1: muted by default + Request.Payload[5] = 0x00; // 0: NoMuting, 1:MuteSignal, 2:AutoMute + Request.Payload[6] = offset / 256; // HB: Offset + Request.Payload[7] = offset % 256; // LB: Offset + break; + default: + WaitForResponse = false; + ConsolePrintf( PRIO_ERROR, RED"CV3_ConnectSockets: Unsupported Data type"RESETCOLOR"\n" ); + return; + } + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + assert(this == element); + uint16_t conHandle = 0xFFFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (success) + conHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnConnectSocketsV3(device, success, + Request.TargetAddress, epType, inHandle, outHandle, conHandle, tag); + } +}; + +class CV3_ResourceDestroy : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint16_t nodeAddress; + uint8_t amountOfHandles; + uint32_t tag; +public: + CV3_ResourceDestroy(CNetworkDevice *d, int16_t nodeAddress, + uint8_t amount, const uint16_t *pHandle, uint32_t t) : device(d), + amountOfHandles(amount), tag(t) + { + ElementName = "CV3_ResourceDestroy"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x800; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + + uint8_t index = 0; + Request.Payload[index++] = 0x00; // HB: SenderHandle + Request.Payload[index++] = 0x00; // LB: SenderHandle + for( uint8_t i = 0; i < amountOfHandles; i++ ) + { + Request.Payload[index++] = pHandle[i] / 256; // HB: handle + Request.Payload[index++] = pHandle[i] % 256; // LB: handle + } + Request.PayloadLen = index; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnResourceDestroyV3(device, true, + Request.TargetAddress, amountOfHandles, ( uint16_t * )&Request.Payload[2], tag); + } +}; + +class CV3_StreamPortConfig : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint8_t portInstance; + V3I2SPortOption_t option; + V3I2SClockMode_t mode; + V3I2SDelayMode_t delay; + uint32_t tag; +public: + CV3_StreamPortConfig(CNetworkDevice *d, uint16_t nodeAddress, uint8_t port, + V3I2SPortOption_t opt, V3I2SClockMode_t md, V3I2SDelayMode_t del, uint32_t t) + : device(d), portInstance(port), option(opt), mode(md), delay(del), tag(t) + { + ElementName = "CV3_StreamPortConfig"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x680; + Request.Inst = 0x00; + Request.OpType = CISOpType_SETGET; + Request.PayloadLen = 5; + Request.Payload[0] = portInstance; + Request.Payload[1] = 0x00; //Fixed operation mode 0 = Generic + Request.Payload[2] = option; + Request.Payload[3] = mode; + Request.Payload[4] = delay; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnConfigureI2SPortV3(device, (ISReturn_Success == result), + Request.TargetAddress, portInstance, option, mode, delay, tag); + } +}; + +class CV3_StreamPortCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint8_t portInstance; + V3I2SPortSpeed_t clock; + V3I2SAlignment_t align; + uint32_t tag; +public: + CV3_StreamPortCreate(CNetworkDevice *d, uint16_t nodeAddress, uint8_t port, + V3I2SPortSpeed_t cl, V3I2SAlignment_t al, uint32_t t) + : device(d), portInstance(port), clock(cl), align(al), tag(t) + { + ElementName = "CV3_StreamPortCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x681; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 3; + Request.Payload[0] = portInstance; + Request.Payload[1] = clock; + Request.Payload[2] = align; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateI2SPortV3(device, (ISReturn_Success == result), + Request.TargetAddress, portInstance, clock, align, tag); + } +}; + +class CV3_StreamSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint8_t portInstance; + EPDirection_t epDir; + uint16_t blockWidthI2S; + V3I2SPin_t pin; + uint32_t tag; +public: + CV3_StreamSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint8_t pi, EPDirection_t epd, + uint16_t bw, V3I2SPin_t p, uint32_t t) : device(d), portInstance(pi), + epDir(epd), blockWidthI2S(bw), pin(p), tag(t) + { + ElementName = "CV3_StreamSocketCreate"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x691; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = 0x16; //Fixed port handle for I2S + Request.Payload[1] = portInstance; //dynamic port handle for I2S (Port A or B) + Request.Payload[2] = epDir; + Request.Payload[3] = 0x00; //Data type (fixed 0 for sync) + Request.Payload[4] = blockWidthI2S / 256; + Request.Payload[5] = blockWidthI2S % 256; + Request.Payload[6] = pin; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + uint16_t i2sHandle = 0xFFFF; + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (success) + i2sHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateI2SSocketV3(device, success, + Request.TargetAddress, portInstance, epDir, blockWidthI2S, pin, i2sHandle, tag); + } +}; + +class CV3_SplittedStreamSocketCreate : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint8_t portInstance; + EPDirection_t epDir; + uint16_t blockWidthI2S; + V3I2SPin_t pin; + uint16_t bytesPerPacket; + uint32_t tag; + uint16_t i2sHandle; +public: + CV3_SplittedStreamSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint8_t pi, + EPDirection_t epd, uint16_t bw, V3I2SPin_t p, uint16_t bp, uint32_t t) : device(d), + portInstance(pi), epDir(epd), blockWidthI2S(bw), pin(p), bytesPerPacket(bp), tag(t), + i2sHandle(0xFFFF) + { + ElementName = "CV3_SplittedStreamSocketCreate-Stream"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x691; + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 7; + Request.Payload[0] = 0x16; //Fixed port handle for I2S + Request.Payload[1] = portInstance; //dynamic port handle for I2S (Port A or B) + Request.Payload[2] = epDir; + Request.Payload[3] = 0x00; //Data type (fixed 0 for sync) + Request.Payload[4] = blockWidthI2S / 256; + Request.Payload[5] = blockWidthI2S % 256; + Request.Payload[6] = pin; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2; + if (!success) + { + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateSplittedI2SSocketV3(device, false, + Request.TargetAddress, portInstance, epDir, blockWidthI2S, pin, + 0xFFFF, 0xFFFF, tag); + return; + } + if (0x691 == Response.Func) + { + i2sHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + CreateSplitter(iStack); + } + else + { + uint16_t splitterHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1]; + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnCreateSplittedI2SSocketV3(device, success, + Request.TargetAddress, portInstance, epDir, blockWidthI2S, pin, + i2sHandle, splitterHandle, tag); + } + } +private: + void CreateSplitter(CIndustrialStack *iStack) + { + assert(i2sHandle != 0xFFFF); + ElementName = "CV3_SplittedStreamSocketCreate-Splitter"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_RESULT; + Callback = this; + startTime = 0; + Request.FBlock = 0x0; + if( EPDIR_IN == epDir ) + Request.Func = 0x911; //SplitterCreate + else + Request.Func = 0x901; //CombinerCreate + Request.Inst = 0x00; + Request.OpType = CISOpType_STARTRESULT; + Request.PayloadLen = 6; + Request.Payload[0] = i2sHandle / 256; // HB: SocketHandle IN/OUT + Request.Payload[1] = i2sHandle % 256; // LB: SocketHandle IN/OUT + Request.Payload[2] = 0x0D; // HB: MOSTPortHandle + Request.Payload[3] = 0x00; // LB: MOSTPortHandle + Request.Payload[4] = bytesPerPacket / 256; // HB: BytesPerFrame + Request.Payload[5] = bytesPerPacket % 256; // LB: BytesPerFrame + iStack->EnqueueElement(Request.TargetAddress, this); + } +}; + +class CV3_GetRbdResult : public CISSendMostMsgElement +{ +public: + CV3_GetRbdResult(uint16_t nodeAddress) + { + ElementName = "CV3_GetRbdResult"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_STATUS; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Func = 0x527; + Request.Inst = 0x00; + Request.OpType = CISOpType_GET; + Request.PayloadLen = 0; + } +}; + +class CV3_DeviceVersion : public CISSendMostMsgElement, public IISElementCallback +{ +private: + CNetworkDevice *device; + uint16_t nodeAddress; + EPDataType_t epType; + uint16_t inHandle; + uint16_t outHandle; + uint16_t offset; + uint32_t tag; +public: + CV3_DeviceVersion(CNetworkDevice *d, uint16_t nodeAddress, uint32_t t) : device(d), tag(t) + { + ElementName = "CV3_DeviceVersion"; + WaitForResponse = true; + WaitForResponseOpType = CISOpType_STATUS; + Callback = this; + + Request.IsValid = true; + Request.SourceAddress = 0x2; + Request.TargetAddress = nodeAddress; + Request.FBlock = 0x0; + Request.Inst = 0x00; + Request.Func = 0x221; + Request.OpType = CISOpType_GET; + Request.PayloadLen = 0; + } + + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) + { + assert(this == element); + bool success = (ISReturn_Success == result) && Response.PayloadLen >= 13; + + uint32_t productId = 0xFFFFFFFF; + uint32_t fwVersion = 0xFFFFFFFF; + uint32_t buildVersion = 0xFFFFFFFF; + uint8_t hwVersion = 0xFF; + uint16_t diagnosisId = 0xFFFF; + + if (success) + { + productId = ( Response.Payload[0] << 24 ) + | ( Response.Payload[1] << 16 ) + | ( Response.Payload[2] << 8 ) + | Response.Payload[3]; + + fwVersion = ( Response.Payload[4] << 24 ) + | ( Response.Payload[5] << 16 ) + | ( Response.Payload[6] << 8 ); + + buildVersion = ( Response.Payload[7] << 24 ) + | ( Response.Payload[8] << 16 ) + | ( Response.Payload[9] << 8 ) + | Response.Payload[10]; + + hwVersion = Response.Payload[10]; + + diagnosisId = ( Response.Payload[11] << 8 ) + | Response.Payload[12]; + } + + for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ ) + device->GetListener( i )->OnDeviceVersion(device, success, Response.SourceAddress, + productId, fwVersion, buildVersion, hwVersion, diagnosisId, tag); + } +}; + +#endif //INDUSTRIAL_STACK_API_V3_H \ No newline at end of file diff --git a/Src/Network/IndustrialStack_LLD.cpp b/Src/Network/IndustrialStack_LLD.cpp new file mode 100644 index 0000000..f025dec --- /dev/null +++ b/Src/Network/IndustrialStack_LLD.cpp @@ -0,0 +1,278 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +//#define LLD_TRACE + +/*----------------------------------------------------------*/ +/*! \file */ +/*! \brief Base Board initialisation */ +/*----------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "Console.h" +#include "Board.h" +#include "DriverConfiguration.h" +#include "IndustrialStack_LLD.h" + +/*----------------------------------------------------------*/ +/* Static C helper functions */ +/*----------------------------------------------------------*/ + +static void Queue_Init( Queue_t *queue ) +{ + assert(NULL != queue); + queue->testPattern = QUEUE_TESTPATTERN; + queue->rxPos = 0; + queue->txPos = 0; + queue->pRx = queue->dataQueue; + queue->pTx = queue->dataQueue; +} + +static QueueEntry_t *Queue_GetRxPtr( Queue_t *queue ) +{ + assert(NULL != queue && QUEUE_TESTPATTERN == queue->testPattern); + QueueEntry_t *pEntry = NULL; + if( queue->txPos - queue->rxPos > 0 ) + { + pEntry = queue->pRx; + } + return pEntry; +} + +static void Queue_PopRx( Queue_t *queue ) +{ + assert(NULL != queue && QUEUE_TESTPATTERN == queue->testPattern); + if( ++queue->pRx >= queue->dataQueue + BOARD_PMS_RX_QUEUE ) + queue->pRx = queue->dataQueue; + ++queue->rxPos; +} + +static QueueEntry_t *Queue_GetTxPtr( Queue_t *queue ) +{ + assert(NULL != queue && QUEUE_TESTPATTERN == queue->testPattern); + QueueEntry_t *pEntry = NULL; + if( ( uint32_t )BOARD_PMS_RX_QUEUE + queue->rxPos - queue->txPos > 0 ) + { + pEntry = queue->pTx; + } + return pEntry; +} + +static void Queue_PopTx( Queue_t *queue ) +{ + assert(NULL != queue && QUEUE_TESTPATTERN == queue->testPattern); + if( ++queue->pTx >= queue->dataQueue + BOARD_PMS_RX_QUEUE ) + queue->pTx = queue->dataQueue; + ++queue->txPos; +} + +static void *ReceiveThread( void *tag ) +{ + assert(NULL != tag); + CIndustrialStackLld *var = ( CIndustrialStackLld * )tag; + while( var->allowThreadRun ) + { + if( -1 == var->hControlRx ) + { + ConsolePrintf( PRIO_ERROR, RED"File handle for Control-RX is invalid, stopping reader thread."RESETCOLOR"\n" ); + if (NULL != var->listener) + var->listener->OnReadThreadEnd(var); + pthread_exit( NULL ); + } + QueueEntry_t *pEntry = Queue_GetTxPtr(&var->rxQueue); + if( NULL != pEntry ) + { + int16_t payloadLen = 0; + payloadLen = read( var->hControlRx, pEntry->buffer, sizeof( pEntry->buffer ) ); + if( payloadLen < 0 ) + { + if( var->allowThreadRun ) + { + ConsolePrintf( PRIO_ERROR, RED"Stopping reader thread, because of error: '%s'"RESETCOLOR"\n", + GetErrnoString() ); + if (NULL != var->listener) + var->listener->OnReadThreadEnd(var); + } + pthread_exit( NULL ); + } + else if( payloadLen != 0 ) + { + pEntry->payloadLen = payloadLen; + Queue_PopTx(&var->rxQueue); +#ifdef LLD_TRACE + { + ConsolePrintfStart( PRIO_HIGH, BLUE"%04d: MSG_RX: ", GetTickCountWord()); + for ( int16_t i = 0; i < pEntry->payloadLen; i++ ) + { + ConsolePrintfContinue( "%02X ", pEntry->buffer[i] ); + } + ConsolePrintfExit(RESETCOLOR"\n"); + } +#endif + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"WARNING, RX QUEUE FULL !!\nPlease increase BOARD_PMS_RX_QUEUE value, or increase polling speed\n"RESETCOLOR"\n" ); + usleep( 10000 ); + } + } + ConsolePrintf( PRIO_LOW, "Control Receive Thread ends\n" ); + if( var->allowThreadRun && NULL != var->listener) + var->listener->OnReadThreadEnd(var); + pthread_exit( NULL ); +} + +static void *SendThread( void *tag ) +{ + assert(NULL != tag); + CIndustrialStackLld *var = ( CIndustrialStackLld * )tag; + while( var->allowThreadRun ) + { + sem_wait( &var->txSem ); + if (!var->allowThreadRun) + pthread_exit( NULL ); + QueueEntry_t *pEntry = Queue_GetRxPtr( &var->txQueue ); + assert(NULL != pEntry); + if( -1 == var->hControlTx ) + { + ConsolePrintf( PRIO_ERROR, RED"File handle for Control-TX is invalid, stopping send thread."RESETCOLOR"\n" ); + pthread_exit( NULL ); + } +#ifdef LLD_TRACE + { + uint32_t i; + ConsolePrintfStart( PRIO_MEDIUM, YELLOW"%04d: MSG_TX: ", GetTickCountWord()); + for ( i = 0; i < pEntry->payloadLen; i++ ) + { + ConsolePrintfContinue( "%02X ", pEntry->buffer[i] ); + } + ConsolePrintfExit(RESETCOLOR"\n"); + } +#endif + if( write( var->hControlTx, pEntry->buffer, pEntry->payloadLen ) != pEntry->payloadLen ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to send %d bytes to the MOST control channel"RESETCOLOR"\n", pEntry->payloadLen ); + usleep(1000); + } + else + { + Queue_PopRx(&var->txQueue); + } + + } + pthread_exit( NULL ); +} + +/*----------------------------------------------------------*/ +/* Public method implementations */ +/*----------------------------------------------------------*/ + +CIndustrialStackLld::CIndustrialStackLld( int controlRxHandle, int controlTxHandle ) + : hControlRx(controlRxHandle), hControlTx(controlTxHandle), allowThreadRun(true), listener(NULL) +{ + Queue_Init(&rxQueue); + Queue_Init(&txQueue); + + if (sem_init(&txSem, 0, 0) == -1) + { + ConsolePrintf(PRIO_ERROR, RED"CIndustrialStackLld constructor: sem_init failed"RESETCOLOR"\n"); + return; + } + + pthread_create( &rxThread, NULL, ReceiveThread, this ); + pthread_create( &txThread, NULL, SendThread, this ); +} + +CIndustrialStackLld::~CIndustrialStackLld() +{ + void *dummy; + allowThreadRun = false; + sem_post( &txSem ); + + pthread_join( txThread, &dummy ); + pthread_join( rxThread, &dummy ); + + sem_destroy( &txSem ); +} + +void CIndustrialStackLld::ClearQueues() +{ + ConsolePrintf( PRIO_LOW, "Clearing INIC queues\n" ); + QueueEntry_t *pEntry; + while( NULL != ( pEntry = Queue_GetRxPtr(&rxQueue) ) ) + Queue_PopRx(&rxQueue); + while( NULL != ( pEntry = Queue_GetRxPtr(&txQueue) ) ) + Queue_PopRx(&txQueue); +} + +uint16_t CIndustrialStackLld::DataAvailable() +{ + uint16_t readLen = 0; + QueueEntry_t *pEntry = Queue_GetRxPtr(&rxQueue); + if( NULL != pEntry ) + readLen = pEntry->payloadLen; + return readLen; +} + +uint16_t CIndustrialStackLld::Read( uint8_t *pData, uint32_t bufferLen ) +{ + uint16_t readLen = 0; + QueueEntry_t *pEntry = Queue_GetRxPtr(&rxQueue); + if( NULL != pData && NULL != pEntry ) + { + readLen = pEntry->payloadLen; + if (readLen > bufferLen) + { + ConsolePrintf(PRIO_ERROR, RED"CIndustrialStackLld::Read buffer"\ + " length is too small"RESETCOLOR"\n"); + readLen = bufferLen; + } + memcpy( pData, pEntry->buffer, readLen ); + Queue_PopRx(&rxQueue); + } + return readLen; +} + +bool CIndustrialStackLld::Write( uint16_t wLen, uint8_t *pData ) +{ + if( -1 == hControlTx ) + return false; + QueueEntry_t *pEntry = Queue_GetTxPtr( &txQueue ); + if( NULL != pData && NULL != pEntry ) + { + memcpy( pEntry->buffer, pData, wLen ); + pEntry->payloadLen = wLen; + Queue_PopTx( &txQueue ); + sem_post( &txSem ); + return true; + } + return false; +} diff --git a/Src/Network/IndustrialStack_LLD.h b/Src/Network/IndustrialStack_LLD.h new file mode 100644 index 0000000..8ae9738 --- /dev/null +++ b/Src/Network/IndustrialStack_LLD.h @@ -0,0 +1,148 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This component implements the MOST low level driver. + */ +/*----------------------------------------------------------*/ +#ifndef INDUSTRIAL_STACK_LLD_H +#define INDUSTRIAL_STACK_LLD_H + +#include +#include +#include +#include "IndustrialStack_Types.h" + +#define BOARD_PMS_RX_QUEUE 256 +#define BOARD_PMS_RX_SIZE 64 +#define BOARD_PMS_TX_SIZE 64 + +typedef struct +{ + int16_t payloadLen; + uint8_t buffer[BOARD_PMS_RX_SIZE]; +} QueueEntry_t; + +#define QUEUE_TESTPATTERN (0x34547382) +typedef struct +{ + uint32_t testPattern; + QueueEntry_t dataQueue[BOARD_PMS_RX_QUEUE]; + QueueEntry_t *pRx; + QueueEntry_t *pTx; + volatile uint32_t rxPos; + volatile uint32_t txPos; +} Queue_t; + +class CIndustrialStackLld +{ +private: + pthread_t txThread; + pthread_t rxThread; +public: + ///Do not use directly! (Called internally from thread context) + Queue_t txQueue; + ///Do not use directly! (Called internally from thread context) + Queue_t rxQueue; + ///Do not use directly! (Called internally from thread context) + int hControlRx; + ///Do not use directly! (Called internally from thread context) + int hControlTx; + ///Do not use directly! (Called internally from thread context) + bool allowThreadRun; + ///Do not use directly! (Called internally from thread context) + sem_t txSem; + + CIndustrialStackLldCB *listener; + + /*----------------------------------------------------------*/ + /*! \brief initializes LLD + * + * \param controlRxHandle File handle for the RX character device + * \param controlRxHandle File handle for the TX character device + * \return nothing. + */ + /*----------------------------------------------------------*/ + CIndustrialStackLld( int controlRxHandle, int controlTxHandle ); + + + + /*----------------------------------------------------------*/ + /*! \brief deinitializes LLD + */ + /*----------------------------------------------------------*/ + ~CIndustrialStackLld(); + + + /*----------------------------------------------------------*/ + /*! \brief determins if INIC has something to read + * + * \return The amount of data, which will be delivered by the next call of Read + */ + /*----------------------------------------------------------*/ + uint16_t DataAvailable(); + + + /*----------------------------------------------------------*/ + /*! \brief receive a control message via USB. + * + * \param pData - message data + * + * \return The amount of bytes read. + */ + /*----------------------------------------------------------*/ + uint16_t Read( uint8_t *pData, uint32_t bufferLen ); + + + + /*----------------------------------------------------------*/ + /*! \brief Clearing RX and TX queues. + * + * \return nothing + */ + /*----------------------------------------------------------*/ + void ClearQueues(); + + + + /*----------------------------------------------------------*/ + /*! \brief send a control message via USB. + * + * \param wLen - length of message in bytes + * \param pData - message data + * + * \return True if no error. + */ + /*----------------------------------------------------------*/ + bool Write( uint16_t wLen, uint8_t *pData ); + +}; + +class CIndustrialStackLldCB +{ +public: + virtual void OnReadThreadEnd(CIndustrialStackLld *lld) = 0; +}; + +#endif // INDUSTRIAL_STACK_LLD_H diff --git a/Src/Network/IndustrialStack_MNS.cpp b/Src/Network/IndustrialStack_MNS.cpp new file mode 100644 index 0000000..257db40 --- /dev/null +++ b/Src/Network/IndustrialStack_MNS.cpp @@ -0,0 +1,414 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class (NetService part). + */ +/*----------------------------------------------------------*/ + +#define ENABLE_INIC_WATCHDOG true + +#include +#include +#include +#include + +#include "IndustrialStack_MNS.h" +#include "IndustrialStack_Types.h" +#include "Board.h" +#include "Console.h" + +static bool CheckMnswPointer(void *inst_ptr, const char *functionName); +static void Lld_CtrlStartC( Mns_Lld_Api_t *callbacks_ptr, void *ns_ptr, void *inst_ptr ); +static void Lld_CtrlStopC( void *inst_ptr ); +static void Lld_CtrlTxTransmitC( Mns_Lld_TxMsg_t *msg_ptr, void *inst_ptr ); +static void Lld_CtrlRxMsgAvailableC( void *inst_ptr ); +static uint16_t GetTickCountWordC(); +static void OnMnslEventC( Mnsl_Event_t event_code, void *inst_ptr ); +static void OnMnslServiceC( void *inst_ptr ); +static void *OnMnslAmsAllocMemC( void *inst_ptr, uint16_t mem_size, Mns_Ams_MemUsage_t type, void** custom_info_pptr ); +static void OnMnslAmsFreeMemC( void *inst_ptr, void *mem_ptr, Mns_Ams_MemUsage_t type, void* custom_info_ptr ); +static void OnIcmRx( void *inst_ptr, Msg_MostTel_t *pRx ); +static void OnRcmRx( void *inst_ptr, Msg_MostTel_t *pRx ); +static void OnMcmRx( void *inst_ptr, Msg_MostTel_t *pRx ); +static void OnTransmissionStatus(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status); + +CISNetServiceWrapper::CISNetServiceWrapper(uint8_t deviceApi, int controlRxHandle, int controlTxHandle) + : testPattern(MNSW_TESTPATTERN), inicWriteError( false ), pMnsInterface(NULL), pMnsInst(NULL) + , isSynced(false), wrapperCB(NULL) +{ + if( 2 != deviceApi && 3 != deviceApi ) + { + ConsolePrintf( PRIO_ERROR, RED"CISNetServiceWrapper was called with deviceApi set"\ + " to %d. This is not supported by this branch of NetworkManager"\ + RESETCOLOR"\n", deviceApi ); + } + + lld = new CIndustrialStackLld(controlRxHandle, controlTxHandle); + lld->listener = this; + + memset(&pLldCb, 0, sizeof(pLldCb)); + pLldCb.start_fptr = &Lld_CtrlStartC; + pLldCb.stop_fptr = &Lld_CtrlStopC; + pLldCb.rx_available_fptr = &Lld_CtrlRxMsgAvailableC; + pLldCb.tx_transmit_fptr = &Lld_CtrlTxTransmitC; + + Mnsl_InitData_t initData; + Mnsl_SetDefaultConfig(&initData, ENABLE_INIC_WATCHDOG); + initData.lld.start_fptr = &Lld_CtrlStartC; + initData.lld.stop_fptr = &Lld_CtrlStopC; + initData.lld.rx_available_fptr = &Lld_CtrlRxMsgAvailableC; + initData.lld.tx_transmit_fptr = &Lld_CtrlTxTransmitC; + + initData.general.get_tickcount_fptr = &GetTickCountWordC; + initData.general.event_fptr = &OnMnslEventC; + initData.general.request_service_fptr = &OnMnslServiceC; + + initData.pms.active_fifos = MNSL_FIFOS_MCM_ICM_RCM; + initData.pms.compressed = (2 == deviceApi); + + initData.ams.rx_alloc_mem_fptr = &OnMnslAmsAllocMemC; + initData.ams.rx_free_mem_fptr = &OnMnslAmsFreeMemC; + + Mnsl_Init( &mnsl, &initData, this ); + + icm_inst_ptr = Mnsl_GetIcmTransceiver( &mnsl ); // Retrieve ICM instance + rcm_inst_ptr = Mnsl_GetRcmTransceiver( &mnsl ); // Retrieve RCM instance + mcm_inst_ptr = Mnsl_GetMcmTransceiver( &mnsl ); // Retrieve MCM instance + + Trcv_RxAssignReceiver( icm_inst_ptr, &OnIcmRx, this ); // Assign ICM Receiver Callback + Trcv_RxAssignReceiver( rcm_inst_ptr, &OnRcmRx, this ); // Assign ICM Receiver Callback + Trcv_RxAssignReceiver( mcm_inst_ptr, &OnMcmRx, this ); // Assign ICM Receiver Callback + + Mnsl_Synchronize( &mnsl ); +} + +CISNetServiceWrapper::~CISNetServiceWrapper() +{ + if (NULL != lld) + { + delete lld; + lld = NULL; + } +} + +void CISNetServiceWrapper::ServiceMns() +{ + assert(NULL != lld); + uint16_t wLen; + while( 0 != ( wLen = lld->DataAvailable() ) ) + { + if (NULL != pMnsInterface) + { + Mns_Lld_RxMsg_t *pRxMsg = pMnsInterface->rx_allocate_fptr( pMnsInst, wLen ); + if( pRxMsg ) + { + if (wLen != lld->Read( pRxMsg->data_ptr, wLen )) + ConsolePrintf( PRIO_ERROR, RED"! LLD read error"RESETCOLOR"\n" ); //Must not happen + pRxMsg->data_size = wLen; + pMnsInterface->rx_receive_fptr( pMnsInst, pRxMsg ); + Mnsl_Service( &mnsl ); + } + else + ConsolePrintf( PRIO_ERROR, RED"! out of message memory"RESETCOLOR"\n" ); + } + } + Mnsl_Service( &mnsl ); +} + +void CISNetServiceWrapper::AddListener(IISNetServiceWrapperCB *rcvListener) +{ + wrapperCB = rcvListener; +} + +bool CISNetServiceWrapper::SendMostMessage(CISMostMsg *msg) +{ + assert(NULL != msg); + assert(msg->IsValid); + + CTransceiver *tr = NULL; + + if (0x1 == msg->TargetAddress || 0x100 == msg->TargetAddress) + tr = icm_inst_ptr; + else if (0x0 == msg->FBlock || 0x1 == msg->FBlock) + tr = rcm_inst_ptr; + else + tr = mcm_inst_ptr; + + Msg_MostTel_t *tel = Trcv_TxAllocateMsg( tr, msg->PayloadLen ); + if( NULL == tel ) + { + ConsolePrintf( PRIO_ERROR, RED"Trcv_TxAllocateMsg failed. Len:"\ + " %u, Message-Type=%s"RESETCOLOR"\n", msg->PayloadLen, + (tr == icm_inst_ptr ? "ICM" : tr == rcm_inst_ptr ? "RCM" : "MCM")); + return false; + } + tel->source_addr = msg->SourceAddress; + tel->destination_addr = msg->TargetAddress; + tel->id.fblock_id = msg->FBlock; + tel->id.function_id = msg->Func; + tel->id.instance_id = msg->Inst; + tel->id.op_type = (Mns_OpType_t)msg->OpType; + tel->tel.tel_len = msg->PayloadLen; + tel->tel.tel_id = 0; + if (0 != msg->PayloadLen) + { + if (NULL != tel->tel.tel_data_ptr) + { + memcpy(tel->tel.tel_data_ptr, msg->Payload, msg->PayloadLen); + } else { + ConsolePrintf(PRIO_ERROR, RED"CISNetServiceWrapper::SendMostMessage,"\ + " packet has NULL pointer payload"RESETCOLOR"\n"); + Trcv_TxReleaseMsg(tel); + return false; + } + } + Trcv_TxSendMsgExt( tr, tel, OnTransmissionStatus, NULL ); + return true; +} + +void CISNetServiceWrapper::Unsynchronize() +{ + Mnsl_Unsynchronize( &mnsl, true ); +} + +void CISNetServiceWrapper::OnReadThreadEnd(CIndustrialStackLld *lld) +{ + if (NULL != wrapperCB) + wrapperCB->OnControlReadEnd(); +} + +void CISNetServiceWrapper::OnCtrlTxTransmit( Mns_Lld_TxMsg_t *msg_ptr) +{ + assert(NULL != msg_ptr); + assert(NULL != pMnsInterface); + if( msg_ptr && pMnsInterface ) + { + assert(NULL != lld); + Mns_Mem_Buffer_t *pMemBuf; +#define MAX_DATA_LEN 72 + uint8_t data[MAX_DATA_LEN]; + uint8_t *pW = data; + for( pMemBuf = msg_ptr->memory_ptr; pMemBuf != NULL; + pMemBuf = pMemBuf->next_buffer_ptr ) + { + if( pW + pMemBuf->data_size >= data + MAX_DATA_LEN ) + { + ConsolePrintf( PRIO_ERROR, RED"invalid size"RESETCOLOR"\n" ); + return; + } + memcpy( pW, pMemBuf->data_ptr, pMemBuf->data_size ); + pW += pMemBuf->data_size; + } + + if( !lld->Write( pW - data, data ) ) + { + if (!inicWriteError) + { + inicWriteError = true; + ConsolePrintf( PRIO_ERROR, RED"! Unable to write to INIC!"RESETCOLOR"\n" ); + } + } + else inicWriteError = false; + + pMnsInterface->tx_release_fptr( pMnsInst, msg_ptr ); + } +} + +void CISNetServiceWrapper::OnMnslEvent( Mnsl_Event_t event_code ) +{ + bool oldSyncState = isSynced; + switch( event_code ) + { + case MNSL_EVENT_SYNC_COMPLETE: + isSynced = true; + ConsolePrintf( PRIO_MEDIUM, "MNSL Event Callback notifies: MNSL_EVENT_SYNC_COMPLETE\n" ); + break; + case MNSL_EVENT_SYNC_FAILED: + isSynced = false; + ConsolePrintf( PRIO_ERROR, YELLOW"MNSL Event Callback notifies: MNSL_EVENT_SYNC_FAILED" \ + ", retrying..\nMake sure that local INIC runs Firmware V2.3.0 or later!"RESETCOLOR"\n" ); + Mnsl_Synchronize( &mnsl ); + return; /* Do not report the event */ + case MNSL_EVENT_SYNC_LOST: + isSynced = false; + ConsolePrintf( PRIO_ERROR, "MNSL Event Callback notifies: MNSL_EVENT_SYNC_LOST\n" ); + break; + case MNSL_EVENT_UNSYNC_COMPLETE: + isSynced = false; + ConsolePrintf( PRIO_MEDIUM, YELLOW"MNSL Event Callback notifies: MNSL_EVENT_UNSYNC_COMPLETE, syncing again.."RESETCOLOR"\n" ); + Mnsl_Synchronize( &mnsl ); + return; /* Do not report the event */ + case MNSL_EVENT_UNSYNC_FAILED: + isSynced = false; + ConsolePrintf( PRIO_ERROR, RED"MNSL Event Callback notifies: MNSL_EVENT_UNSYNC_FAILED"RESETCOLOR"\n" ); + Mnsl_Synchronize( &mnsl ); + break; + default: + ConsolePrintf( PRIO_ERROR, "MNSL Event Callback notifies: UNKNOWN CODE\n" ); + break; + } + if (NULL != wrapperCB && oldSyncState != isSynced) + { + wrapperCB->OnSyncStateChanged(isSynced); + } +} + +void CISNetServiceWrapper::OnMessage( Msg_MostTel_t *pRx, CTransceiver *pTr ) +{ + assert(NULL != pRx); + assert(pRx->tel.tel_len <= MAX_PAYLOAD_SIZE); + if (NULL == wrapperCB) + return; + CISMostMsg msg; + msg.IsValid = true; + msg.SourceAddress = pRx->source_addr; + msg.TargetAddress = pRx->destination_addr; + msg.FBlock = pRx->id.fblock_id; + msg.Func = pRx->id.function_id; + msg.Inst = pRx->id.instance_id; + msg.OpType = (CISOpType_t) pRx->id.op_type; + msg.PayloadLen = pRx->tel.tel_len; + memcpy(msg.Payload, pRx->tel.tel_data_ptr, msg.PayloadLen); + Trcv_RxReleaseMsg(pTr, pRx); + wrapperCB->OnReceivedMostMessage(&msg); +} + +/*---------------------------------------- + * Private helper functions and C wrapper: + *---------------------------------------- + */ +static bool CheckMnswPointer(void *inst_ptr, const char *functionName) +{ + if( NULL == inst_ptr || MNSW_TESTPATTERN != ( ( CISNetServiceWrapper * )inst_ptr )->testPattern ) + { + ConsolePrintf( PRIO_ERROR, RED"Parameter bug in %s"RESETCOLOR"\n", functionName ); + assert(false); + return true; + } + return false; +} +static void Lld_CtrlStartC( Mns_Lld_Api_t *callbacks_ptr, void *ns_ptr, void *inst_ptr ) +{ + if (CheckMnswPointer(inst_ptr, "Lld_CtrlStart")) return; + CISNetServiceWrapper *mnsw = ( CISNetServiceWrapper * )inst_ptr; + mnsw->pMnsInterface = callbacks_ptr; + mnsw->pMnsInst = ns_ptr; +} +static void Lld_CtrlStopC( void *inst_ptr ) +{ + if (CheckMnswPointer(inst_ptr, "Lld_CtrlStop")) return; + CISNetServiceWrapper *mnsw = ( CISNetServiceWrapper * )inst_ptr; + mnsw->pMnsInterface = 0; + mnsw->pMnsInst = 0; +} +static void Lld_CtrlTxTransmitC( Mns_Lld_TxMsg_t *msg_ptr, void *inst_ptr ) +{ + if (CheckMnswPointer(inst_ptr, "Lld_CtrlTxTransmit")) return; + CISNetServiceWrapper *mnsw = ( CISNetServiceWrapper * )inst_ptr; + mnsw->OnCtrlTxTransmit(msg_ptr); +} +static void Lld_CtrlRxMsgAvailableC( void *inst_ptr ) +{ + //Unused +} +static uint16_t GetTickCountWordC() +{ + return GetTickCountWord(); +} +static void OnMnslEventC( Mnsl_Event_t event_code, void *inst_ptr ) +{ + if (CheckMnswPointer(inst_ptr, "OnMnslEvent")) return; + CISNetServiceWrapper *mnsw = ( CISNetServiceWrapper * )inst_ptr; + mnsw->OnMnslEvent(event_code); +} +static void OnMnslServiceC( void *inst_ptr ) +{ + //Unused +} +static void *OnMnslAmsAllocMemC( void *inst_ptr, uint16_t mem_size, Mns_Ams_MemUsage_t type, void** custom_info_pptr ) +{ + if( 0 == mem_size ) + return NULL; + return calloc( mem_size, 1 ); +} +static void OnMnslAmsFreeMemC( void *inst_ptr, void *mem_ptr, Mns_Ams_MemUsage_t type, void* custom_info_ptr ) +{ + if( NULL != mem_ptr ) + free( mem_ptr ); +} +static void OnIcmRx( void *inst_ptr, Msg_MostTel_t *pRx ) +{ + if (CheckMnswPointer(inst_ptr, "OnIcmRx")) return; + CISNetServiceWrapper *mnsw = ( CISNetServiceWrapper * )inst_ptr; + mnsw->OnMessage(pRx, mnsw->icm_inst_ptr); +} +static void OnRcmRx( void *inst_ptr, Msg_MostTel_t *pRx ) +{ + if (CheckMnswPointer(inst_ptr, "OnRcmRx")) return; + CISNetServiceWrapper *mnsw = ( CISNetServiceWrapper * )inst_ptr; + mnsw->OnMessage(pRx, mnsw->rcm_inst_ptr); +} +static void OnMcmRx( void *inst_ptr, Msg_MostTel_t *pRx ) +{ + if (CheckMnswPointer(inst_ptr, "OnMcmRx")) return; + CISNetServiceWrapper *mnsw = ( CISNetServiceWrapper * )inst_ptr; + mnsw->OnMessage(pRx, mnsw->mcm_inst_ptr); +} + +static void OnTransmissionStatus(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status) +{ + if (MNS_MSG_STAT_OK != status) + { + ConsolePrintf(PRIO_ERROR, RED"Transmission failed for addr=0x%X FBlock=0x%X " \ + "Function=0x%X, error-code=0x%X"RESETCOLOR"\n", tel_ptr->destination_addr, + tel_ptr->id.fblock_id, tel_ptr->id.function_id, status); + } + Trcv_TxReleaseMsg(tel_ptr); +} + +#define TRACE_BUFFER_SZ 200 +#include +#include +void My_TraceInfo(uint8_t mns_inst_id, const char module_str[], const char entry_str[], uint16_t vargs_cnt, ...) +{ + char_t outbuf[TRACE_BUFFER_SZ]; + va_list argptr; + uint16_t timestamp = GetTickCountWord(); + va_start(argptr, vargs_cnt); + vsprintf(outbuf, entry_str, argptr); + va_end(argptr); + ConsolePrintf(PRIO_HIGH, YELLOW"[%u] | %u | Info | %s | %s"RESETCOLOR"\n", mns_inst_id, timestamp, module_str, outbuf); +} + +void My_TraceError(uint8_t mns_inst_id, const char module_str[], const char entry_str[], uint16_t vargs_cnt, ...) +{ + char_t outbuf[TRACE_BUFFER_SZ]; + va_list argptr; + uint16_t timestamp = GetTickCountWord(); + va_start(argptr, vargs_cnt); + vsprintf(outbuf, entry_str, argptr); + va_end(argptr); + ConsolePrintf(PRIO_ERROR, RED"[%u] | %u | Error | %s | %s"RESETCOLOR"\n", mns_inst_id, timestamp, module_str, outbuf); +} diff --git a/Src/Network/IndustrialStack_MNS.h b/Src/Network/IndustrialStack_MNS.h new file mode 100644 index 0000000..7cb28ee --- /dev/null +++ b/Src/Network/IndustrialStack_MNS.h @@ -0,0 +1,84 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class (NetService part). + */ +/*----------------------------------------------------------*/ + +#ifndef INDUSTRIAL_STACK_MNS_H +#define INDUSTRIAL_STACK_MNS_H + +#include +#include "IndustrialStack_Types.h" +#include "IndustrialStack_LLD.h" +#include "mns_types_cfg.h" +#include "mnsl.h" + +#define MNSW_TESTPATTERN ((uint32_t)0xA1B2C3D4) + +class IISNetServiceWrapperCB +{ +public: + virtual void OnReceivedMostMessage(CISMostMsg *rcvMessage) = 0; + virtual void OnSyncStateChanged(bool isSynced) = 0; + virtual void OnControlReadEnd() = 0; +}; + +class CISNetServiceWrapper : public CIndustrialStackLldCB +{ +public: + const uint32_t testPattern; + bool inicWriteError; + Mns_Lld_Api_t *pMnsInterface; + void *pMnsInst; + CTransceiver *icm_inst_ptr; + CTransceiver *rcm_inst_ptr; + CTransceiver *mcm_inst_ptr; +private: + CMnsl mnsl; + Mns_Lld_Callbacks_t pLldCb; + bool isSynced; + IISNetServiceWrapperCB *wrapperCB; + CIndustrialStackLld *lld; + +public: + CISNetServiceWrapper(uint8_t deviceApi, int controlRxHandle, int controlTxHandle); + virtual ~CISNetServiceWrapper(); + void ServiceMns(); + void AddListener(IISNetServiceWrapperCB *rcvListener); + + bool SendMostMessage(CISMostMsg *sndMessage); + void Unsynchronize(); + +public: + //Will be called from LLD, do not call directly + virtual void OnReadThreadEnd(CIndustrialStackLld *lld); + //All this functions will be called by the C wrappers, do not call them directly + void OnCtrlTxTransmit( Mns_Lld_TxMsg_t *msg_ptr); + void OnMnslEvent( Mnsl_Event_t event_code ); + void OnMessage( Msg_MostTel_t *pRx, CTransceiver *pTr ); +}; + +#endif //INDUSTRIAL_STACK_MNS_H diff --git a/Src/Network/IndustrialStack_Types.h b/Src/Network/IndustrialStack_Types.h new file mode 100644 index 0000000..21bba51 --- /dev/null +++ b/Src/Network/IndustrialStack_Types.h @@ -0,0 +1,159 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CIndustrialStack class (type defnitions and common classes). + */ +/*----------------------------------------------------------*/ + +#ifndef INDUSTRIAL_STACK_TYPES_H +#define INDUSTRIAL_STACK_TYPES_H + +#include +#include +#include +#include "SafeVector.h" +typedef enum +{ + ISReturn_NoChange, + ISReturn_Success, + ISReturn_Failure, + ISReturn_Timeout +} ISReturn_t; + +typedef enum +{ + CISOpType_INVALID = 0xFF, + CISOpType_SET = 0x0, + CISOpType_GET = 0x1, + CISOpType_SETGET = 0x2, + CISOpType_INC = 0x3, + CISOpType_DEC = 0x4, + CISOpType_GETINTERFACE = 0x5, + CISOpType_STATUS = 0xC, + CISOpType_INTERFACE = 0xE, + CISOpType_ERROR = 0xF, + CISOpType_START = 0x0, + CISOpType_ABORT = 0x1, + CISOpType_STARTRESULT = 0x2, + CISOpType_STARTRESULTACK = 0x6, + CISOpType_ABORTACK = 0x7, + CISOpType_STARTACK = 0x8, + CISOpType_ERRORACK = 0x9, + CISOpType_PROCESSINGACK = 0xA, + CISOpType_PROCESSING = 0xB, + CISOpType_RESULT = 0xC, + CISOpType_RESULTACK = 0xD, + CISOpType_REPORTS = 0x9 +} CISOpType_t; + +class CIndustrialStack; +class IISElement; +class IISElementCallback; +class CISWaitElement; +class CISMostMsg; +class CSInternalEvent; +class CISSendMostMsgElement; +class CISDeviceQueue; +class CIndustrialStackLldCB; +class CV1_OnMostRx; +class CV2_OnMostRx; +class CV3_OnMostRx; + +class IISElement +{ +private: + int32_t refCount; +public: + const char *ElementName; + IISElementCallback *Callback; + IISElement() : refCount(1), ElementName("Not set"), Callback(NULL) { } + virtual ~IISElement() {} + + virtual ISReturn_t Service(CIndustrialStack *iStack, uint32_t time) = 0; + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *rcvMessage) = 0; + + void AddReference() + { + ++refCount; + } + + void RemoveReference() + { + if( 0 == --refCount ) + delete this; + } +}; + +class CISMostMsg +{ +#define MAX_PAYLOAD_SIZE 45 +public: + bool IsValid; + uint32_t SourceAddress; + uint32_t TargetAddress; + uint32_t FBlock; + uint32_t Func; + uint32_t Inst; + CISOpType_t OpType; + uint32_t PayloadLen; + uint8_t Payload[MAX_PAYLOAD_SIZE]; + + CISMostMsg() : IsValid(false), SourceAddress(0xFFFFFFFF), TargetAddress(0xFFFFFFFF), + FBlock(0xFFFFFFFF), Func(0xFFFFFFFF), Inst(0xFFFFFFFF), + OpType(CISOpType_INVALID), PayloadLen(0) + { } + + void DeepCopy(CISMostMsg *msg) + { + IsValid = msg->IsValid; + SourceAddress = msg->SourceAddress; + TargetAddress = msg->TargetAddress; + FBlock = msg->FBlock; + Func = msg->Func; + Inst = msg->Inst; + OpType = msg->OpType; + PayloadLen = msg->PayloadLen; + memcpy(Payload, msg->Payload, PayloadLen); + } +}; + +class IISElementCallback +{ +public: + virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element) = 0; +}; + +class CISDeviceQueue +{ +private: + uint16_t nodeAddress; +public: + CSafeVector elements; + + CISDeviceQueue(uint16_t address) : nodeAddress(address) { } + uint16_t GetNodeAddress() { return nodeAddress; } +}; + +#endif //INDUSTRIAL_STACK_TYPES_H \ No newline at end of file diff --git a/Src/Network/Network.cpp b/Src/Network/Network.cpp new file mode 100644 index 0000000..0c4b10d --- /dev/null +++ b/Src/Network/Network.cpp @@ -0,0 +1,433 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include "SafeVector.h" +#include "Network.h" +#include "NetworkDevice.h" +#include "DriverConfiguration.h" +#include "MacAddr.h" +#include "Board.h" +#include "Console.h" +#include "NodeDatabase.h" + +using namespace std; + +static CNetwork *staticInstance = NULL; +CSafeVector CNetwork::allNetworkDevices; + +CNetwork::CNetwork() : CThread( "CNetwork", false ), allowNetworkRun( false ), + vodXml( NULL ), connectionBitMask( 0 ), promiscuous( false ), retryCounter( 0 ), + retryExecTime( 0 ) +{ + searchPath[0] = 0; + sem_init( &vodXmlMutex, 0, 1 ); //Mutex, initialized to 1 => sem_wait will block at 2nd call + Start(); +} + +CNetwork::~CNetwork() +{ + ConsolePrintf( PRIO_LOW, "Destructor of network called\r\n" ); + Stop(); + allowNetworkRun = false; + CloseAllNetworkDevices(); + sem_wait( &vodXmlMutex ); + if( NULL != vodXml ) + { + delete vodXml; + vodXml = NULL; + } + sem_post( &vodXmlMutex ); + staticInstance = NULL; + ConsolePrintf( PRIO_LOW, "end network\r\n" ); +} + +CNetwork *CNetwork::GetInstance( void ) +{ + if( NULL == staticInstance ) + { + staticInstance = new CNetwork(); + } + return staticInstance; +} + +void CNetwork::AddListener( CNetworkListner *listener ) +{ + allListeners.PushBack( listener ); +} + +void CNetwork::RemoveListener( CNetworkListner *listener ) +{ + allListeners.Remove(listener); +} + +void CNetwork::SetPromiscuousMode( bool enabled ) +{ + promiscuous = enabled; +} + +bool CNetwork::LoadConfig( char *szConfigXml ) +{ + uint32_t len = strlen(szConfigXml); + if( NULL == szConfigXml || 0 == len) + { + ConsolePrintf( PRIO_ERROR, RED"Path to config file was NULL"RESETCOLOR"\n" ); + return false; + } + if ('/' == szConfigXml[0]) + { + char path[300]; + uint32_t i; + uint32_t lastSlash = 0; + for (i = len - 1; i > 0; i--) + { + if ('/' == szConfigXml[i]) + { + lastSlash = i; + break; + } + } + memcpy(path, szConfigXml, lastSlash); + path[lastSlash] = '\0'; + memcpy(szConfigXml, &szConfigXml[lastSlash + 1], (len - lastSlash - 1) ); + szConfigXml[len - lastSlash - 1] = '\0'; + return LoadConfig(szConfigXml, path); + } + else + { + return LoadConfig(szConfigXml, NULL); + } +} + +bool CNetwork::LoadConfig( const char *szConfigXml, const char *szSearchPath ) +{ + char configPath[300]; + if( NULL == szConfigXml || 0 == strlen(szConfigXml)) + { + ConsolePrintf( PRIO_ERROR, RED"Path to config file was NULL"RESETCOLOR"\n" ); + return false; + } + if (NULL != szSearchPath) + strncpy( searchPath, szSearchPath, sizeof( searchPath ) ); + if( NULL != searchPath && '/' != szConfigXml[0]) + snprintf( configPath, sizeof( configPath ), "%s/%s", searchPath, szConfigXml ); + else + strncpy( configPath, szConfigXml, sizeof( configPath ) ); + + allowNetworkRun = true; + + sem_wait( &vodXmlMutex ); + bool success; + if( NULL != vodXml ) + { + delete vodXml; + } + vodXml = new CVodXml( configPath ); + success = ( NULL != vodXml ); + sem_post( &vodXmlMutex ); + + if ( allNetworkDevices.Size() != 0 ) + { + bool tm; + uint32_t bw; + if (vodXml->GetMostParameters( &tm, &bw ) ) + { + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + allNetworkDevices[i]->SetAsyncBandwidth(bw); + allNetworkDevices[i]->SetTimingMaster(tm); + ShutdownMost(allNetworkDevices[i]); + } + } + } + + if( success ) + FindNewNetworkDevices(); + + retryCounter = 0; + return success; +} + +bool CNetwork::ActionsPending() +{ + bool isPending = false; + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + if( allNetworkDevices[i]->ActionsPending() ) + { + isPending = true; + break; + } + } + return isPending; +} + +bool CNetwork::ConnectSourceToSink( CMacAddr *SourceMacAddr, TChannelId SourceChannelId, CMacAddr *SinkMacAddr, + TChannelId SinkChannelId ) +{ + CNodeEntry *inNode = CNodeEntry::GetNodeEntry( SourceMacAddr ); + CNodeEntry *outNode = CNodeEntry::GetNodeEntry( SinkMacAddr ); + return ConnectSourceToSink( inNode, outNode, SourceChannelId, SinkChannelId ); +} + +uint8_t CNetwork::GetAmountOfLocalNodes() +{ + return allNetworkDevices.Size(); +} + +bool CNetwork::SendMostControlMessage( TMostInstace mostInstance, uint32_t targetAddr, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) +{ + bool success = false; + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + if( allNetworkDevices[i]->GetDeviceIndex() == mostInstance ) + { + if (targetAddr < 0x300 || targetAddr > 0x3FF || targetAddr == 0x3C8) + { + success = allNetworkDevices[i]->SendMostControlMessage( mostInstance, targetAddr, nFBlock, nInst, nFunc, + nOpType, nPayloadLen, Payload ); + } + else + { + success = true; + for (uint16_t j = 0; j < CNodeEntry::GetAmountOfNodeEntries(); j++) + { + CNodeEntry *node = CNodeEntry::GetNodeEntry(j); + if (NULL != node && node->deviceInstance == mostInstance && + node->deviceType == targetAddr) + { + success &= allNetworkDevices[i]->SendMostControlMessage( mostInstance, node->nodeAddress, nFBlock, nInst, nFunc, + nOpType, nPayloadLen, Payload ); + } + } + } + break; + } + } + return success; +} + +bool CNetwork::SendMostControlMessage( TMostInstace mostInstance, TDeviceId deviceId, uint8_t devInst, + uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) +{ + bool success = false; + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( mostInstance, deviceId, devInst ); + if( NULL == entry ) + return success; + + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + if( allNetworkDevices[i]->GetDeviceIndex() == mostInstance ) + { + success = allNetworkDevices[i]->SendMostControlMessage( mostInstance, entry->nodeAddress, nFBlock, nInst, + nFunc, nOpType, nPayloadLen, Payload ); + break; + } + } + return success; +} + +bool CNetwork::ExecuteXmlScriptFromFile( TMostInstace mostInstance, uint32_t targetAddr, const char *szFile ) +{ + if (NULL == szFile) + return false; + bool success = false; + char scriptPath[300]; + const char *pPath = szFile; + if( NULL != searchPath && '/' != szFile[0]) + { + snprintf( scriptPath, sizeof( scriptPath ), "%s/%s", searchPath, szFile ); + pPath = scriptPath; + } + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + if( allNetworkDevices[i]->GetDeviceIndex() == mostInstance ) + { + allNetworkDevices[i]->ExecuteMcmScript( targetAddr, pPath ); + success = true; + break; + } + } + return success; +} + +bool CNetwork::ExecuteXmlScriptFromFile( TMostInstace mostInstance, TDeviceId deviceId, uint8_t devInst, + const char *szFile ) +{ + CNodeEntry *entry = CNodeEntry::GetNodeEntry( mostInstance, deviceId, devInst ); + if( NULL == entry ) + return false; + + return ExecuteXmlScriptFromFile( mostInstance, entry->nodeAddress, szFile ); +} + +bool CNetwork::ExecuteXmlScriptFromMemory( TMostInstace mostInstance, uint32_t targetAddr, const char *szBuffer, + uint32_t nBufferLen ) +{ + bool success = false; + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + if( allNetworkDevices[i]->GetDeviceIndex() == mostInstance ) + { + allNetworkDevices[i]->ExecuteMcmScript( targetAddr, szBuffer, nBufferLen ); + success = true; + break; + } + } + return success; +} + +bool CNetwork::ExecuteXmlScriptFromMemory( TMostInstace mostInstance, TDeviceId deviceId, uint8_t devInst, + const char *szBuffer, uint32_t nBufferLen ) +{ + CNodeEntry *entry = CNodeEntry::GetNodeEntry( mostInstance, deviceId, devInst ); + if( NULL == entry ) + return false; + + return ExecuteXmlScriptFromMemory( mostInstance, entry->nodeAddress, szBuffer, nBufferLen ); +} + +uint32_t CNetwork::GetCounterPartOfRoute( const Route_t *pInRoute, Route_t **pOutRouteArray ) +{ + if( ( NULL == pInRoute ) || ( NULL == pOutRouteArray ) ) + { + ConsolePrintf( PRIO_ERROR, RED"GetCounterPartOfRoute was called with wrong parameters"RESETCOLOR"\n" ); + return 0; + } + *pOutRouteArray = NULL; + CSafeVector connectedTerminals; + CRouteTerminal terminal; + terminal.deviceType = pInRoute->deviceType; + terminal.instance = pInRoute->instance; + terminal.channelId = pInRoute->channelId; + + sem_wait( &vodXmlMutex ); + bool found = vodXml->GetRouteTerminals( &terminal, connectedTerminals ); + sem_post( &vodXmlMutex ); + if( !found ) + return 0; + uint32_t arrayLen = connectedTerminals.Size(); + *pOutRouteArray = ( Route_t * )calloc( arrayLen, sizeof( Route_t ) ); + if( NULL == *pOutRouteArray ) + { + ConsolePrintf( PRIO_ERROR, RED"GetCounterPartOfRoute failed to allocate memory"RESETCOLOR"\n" ); + return 0; + } + for( uint32_t i = 0; i < arrayLen; i++ ) + { + ( *pOutRouteArray )[i].deviceType = connectedTerminals[i]->deviceType; + ( *pOutRouteArray )[i].instance = connectedTerminals[i]->instance; + ( *pOutRouteArray )[i].channelId = connectedTerminals[i]->channelId; + } + return arrayLen; +} + +bool CNetwork::GetRingBreakDiagnosisResult( TMostInstace mostInstance, uint32_t targetAddr ) +{ + bool success = false; + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + if( allNetworkDevices[i]->GetDeviceIndex() == mostInstance ) + { + allNetworkDevices[i]->GetRingBreakResultV3( 1, 0 ); + success = true; + break; + } + } + return success; +} + +void CNetwork::EnableMost(bool enabled) +{ + allowNetworkRun = enabled; + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + CNetworkDevice *device = allNetworkDevices[i]; + if (enabled) + { + if (NetworkState_Available == device->GetNetState()) + { + ConsolePrintf(PRIO_ERROR, RED"MOST is already in NET ON state"RESETCOLOR"\n"); + return; + } + device->SetNetstate( NetworkState_Unknown ); + bool isTimingMaster = device->IsTimingMaster(); + uint32_t asyncBandwidth = device->GetAsyncBandwidth(); + if( isTimingMaster ) + device->MostNetworkStartupV3( 0xFFFF, asyncBandwidth ); + } + else + { + device->SetNetstate( NetworkState_ShutdownInProgress ); + device->ClearAllPendingActions(); + device->MostNetworkShutdownV3(); + } + } +} + +void CNetwork::Run() +{ + sleep( 5 ); + if (retryCounter > 0 && (GetTickCount() - retryExecTime >= 60000)) + retryCounter = 0; + if( allowNetworkRun ) + FindNewNetworkDevices(); +} + +/*---------------------------------------------*/ +/* Empty implementation of CNetworkListner */ +/*---------------------------------------------*/ + +void CNetworkListner::OnNetworkState( uint8_t mostInstance, bool available, uint8_t maxPos, uint16_t packetBW ) +{ + ConsolePrintf( PRIO_LOW, "CNetwork::OnNetworkState\n" ); +} + +void CNetworkListner::OnChannelAvailable( CMacAddr *macAddr, TDeviceId deviceId, uint8_t devInst, TChannelId channelId, + TMostInstace mostInstance, EPDataType_t dataType, TBlockwidth reservedBandwidth, bool isSourceDevice, + bool inSocketCreated, bool outSocketCreated, bool socketsConnected, int32_t bufferSize, + uint32_t mostConnectionLabel, int16_t splittedOffset, int16_t splittedMaxBandwidth, uint16_t packetsPerXact, + const char *deviceName ) +{ + ConsolePrintf( PRIO_LOW, "CNetwork::OnCharacterDeviceAvailable(%s)\n", deviceName ); +} + +void CNetworkListner::OnChannelUnavailable( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ) +{ + ConsolePrintf( PRIO_LOW, "CNetwork::OnChannelUnavailable\n" ); +} + +void CNetworkListner::OnMostControlMessage( uint8_t devInst, uint32_t sourceAddr, uint32_t targetAddr, + uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) +{ + ConsolePrintf( PRIO_LOW, "CNetwork::OnMostControlMessage\n" ); +} + +void CNetworkListner::OnRingBreakDiagnosisResultV3( uint8_t devInst, uint16_t nodeAddress, uint8_t result, + uint8_t position, uint8_t status, uint16_t id ) +{ + ConsolePrintf( PRIO_LOW, "CNetwork::OnRingBreakDiagnosisResultV2\n" ); +} diff --git a/Src/Network/Network.h b/Src/Network/Network.h new file mode 100644 index 0000000..749e903 --- /dev/null +++ b/Src/Network/Network.h @@ -0,0 +1,498 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CNetworkListner class. + */ +/*----------------------------------------------------------*/ +#ifndef _NETWORK_H_ +#define _NETWORK_H_ + +#include "SafeVector.h" +#include "Types.h" +#include "Thread.h" +#include "MacAddr.h" +#include "NetworkDevice.h" +#include "NetworkDeviceListener.h" +#include "VodXml.h" + +/*----------------------------------------------------------*/ +/*! \brief CNetworkListner class shall be derived from classes, which want them self to be registered as callback to the CNetwork class. + */ +/*----------------------------------------------------------*/ +class CNetworkListner +{ +public: + /*----------------------------------------------------------*/ + /*! \brief This callback method is called whenever there is a new channel information available. + * \note In order to be informed about this event, derive this class, implement this method and register the class with AddListener method of CNetwork class. + * \param mostInstance - The instance number of the MOST bus. If the server device has more then one physical MOST devices, each ring + * is identified by a unique id starting with 0 for the first MOST instance. + * \param available - true, if the network is fully usable. false, otherwise. + * \param maxPos - The amount of devices found in the network (inclusive master) + * \param packetBW - Amount of bytes reserved for Ethernet data (multiple with 48000 to get Byte/s) + */ + /*----------------------------------------------------------*/ + virtual void OnNetworkState( uint8_t mostInstance, bool available, uint8_t maxPos, uint16_t packetBW ); + + /*----------------------------------------------------------*/ + /*! \brief This callback method is called whenever there is a new channel information available. + * \note In order to be informed about this event, derive this class, implement this method and register the class with AddListener method of CNetwork class. + * \param macAddr - The MAC address identifying the device unique. + * \param deviceId - The device identifier (group address) as specified in the XML file. + * \param devInst - The instance number of the device. Starting with 0 for the first device with deviceId. + * \param channelId - The channel identifier as specified in the XML file, given by "LoadConfig"-method of CNetwork class. + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring. + * \param dataType - The MOST data used by this channel. + * \param reservedBandwidth - The reserved MOST bandwidth in bytes for this channel. + * is identified by a unique id starting with 0 for the first MOST instance. + * \param isSourceDevice - true, if this device acts as source. false, if this device acts as sink. + * \param inSocketCreated - true, if the in-socket of the connection was created. false, there is no in-socket available at the moment. + * \param outSocketCreated - true, if the out-socket of the connection was created. false, there is no out-socket available at the moment. + * \param socketsConnected - true, if the in- and the out-socket are connected. + * \param splittedOffset - If this socket is a splitted / combined socket, this value holds the offset in byte. -1 if this is a normal socket. + * \param splittedMaxBandwidth - If this socket is a splitted / combined socket, this value holds the maximum bandwidth of all splitted sockets in byte. -1 if this is a normal socket. + * \param bufferSize - The amount of bytes reserved for this channel in Driver. If no buffers were allocated, this value is -1. + * \param packetsPerXactconst, This is USB specific. It describes how many sub-frames are concatenated into a single USB microframe. + * \param deviceName - The name of the Linux character device. May be NULL if the device is a remote device. + */ + /*----------------------------------------------------------*/ + virtual void OnChannelAvailable( CMacAddr *macAddr, TDeviceId deviceId, uint8_t devInst, TChannelId channelId, + TMostInstace mostInstance, EPDataType_t dataType, TBlockwidth reservedBandwidth, bool isSourceDevice, + bool inSocketCreated, bool outSocketCreated, bool socketsConnected, int32_t bufferSize, + uint32_t mostConnectionLabel, int16_t splittedOffset, int16_t splittedMaxBandwidth, + uint16_t packetsPerXact, const char *deviceName ); + + + /*----------------------------------------------------------*/ + /*! \brief This callback method is called whenever a channel has become unavailable. + * \note In order to be informed about this event, derive this class, implement this method and register the class with AddListener method of CNetwork class. + * \param macAddr - The MAC address identifying the device unique. + * \param channelId - The channel identifier as specified in the XML file, given by "LoadConfig"-method of CNetwork class. + * \param mostInstance - The instance number of the MOST bus. If the server device has more then one physical MOST devices, each ring + * is identified by a unique id starting with 0 for the first MOST instance. + */ + /*----------------------------------------------------------*/ + virtual void OnChannelUnavailable( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ); + + + /*----------------------------------------------------------*/ + /*! \brief This callback method is called whenever a MOST control message was sent or received. + * \note In order to be informed about this event, derive this class, implement this method and register the class with AddListener method of CNetwork class. + * \note The user may interpret the data and sent a corresponding MOST control message via CNetwork::SendMostControlMessage. + * \param devInst - The MOST ring instance, starting with 0 for the first MOST ring. + * \param sourceAddr - The MOST source address (may be not accurate) + * \param targetAddr - The MOST target address + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + virtual void OnMostControlMessage( uint8_t devInst, uint32_t sourceAddr, uint32_t targetAddr, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ); + + /*----------------------------------------------------------*/ + /*! \brief Callback, when a Ring Break Diagnosis Result was received. + * \param devInst - The MOST ring instance, starting with 0 for the first MOST ring. + * \param nodeAddr - The device with this MOST node address raised this event. + * \praram result - The ring break diagnosis result + * \param position - Relative position to the ring break. + * \param status - Gives information about the activity state. + * \param id - The RBD identifier as configured in config string. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnRingBreakDiagnosisResultV3( uint8_t devInst, uint16_t nodeAddress, uint8_t result, + uint8_t position, uint8_t status, uint16_t id ); +}; + +/*----------------------------------------------------------*/ +/*! \brief CNetwork represents the main controlling class, which opens and setup MOST connections. +*/ +/*----------------------------------------------------------*/ +class CNetwork : public CNetworkDeviceListener, public CThread +{ + +private: + bool allowNetworkRun; + char searchPath[256]; + CSafeVector allListeners; + static CSafeVector allNetworkDevices; + CVodXml *vodXml; + sem_t vodXmlMutex; + uint32_t connectionBitMask; + bool promiscuous; + uint32_t retryCounter; + uint32_t retryExecTime; + + CNetwork(); + + void DestroyAllResources( CNetworkDevice *device, uint16_t nodeAddress ); + + void CloseAllNetworkDevices(); + + void DestroyAllResources(); + + void FindNewNetworkDevices(); + + void TryToCreateRoute( uint32_t devInstance, bool mostIsTx, void *entry, void *terminal ); + + void TryToConnectSockets( uint32_t devInstance, uint16_t nodeAddr, uint32_t channelId ); + + void TryToCloseExistingMostConnection( void *inNode, uint32_t channelId, void *outNode, uint32_t outChannelId ); + + void RaiseAvailable( void *connection ); + + void RaiseUnavailable( void *connection ); + + CNetworkDevice *GetNetworkDevice( uint32_t devInstance ); + + CMacAddr *SetMacAddress( CNetworkDevice *device, uint32_t devInstance, uint16_t nodeAddress, + uint8_t inicApiVersion ); + + bool ConnectSourceToSink( void *mostTxNode, void *mostRxNode, uint32_t sourceChannelId, uint32_t sinkChannelId ); + + void RaiseUknownConnection( uint32_t devInstance, uint16_t nodeAddress, EPDataType_t dType ); + + void ShutdownMost( CNetworkDevice *device ); + + void ShutdownMostBecauseOfErrors( CNetworkDevice *device ); + + virtual void OnSync( void *source, bool isSynced ); + + virtual void OnNetworkState( void *source, bool mpValChanged, bool systemNotOk, + bool mostAvailable, uint8_t availableSubState, uint8_t availableTransition, uint16_t nodeAddress, + uint8_t nodePos, uint8_t maxPos, uint16_t packetBW ); + + virtual void OnNetworkStartupV3( void *source, bool success ); + + virtual void OnNetworkShutdownV3( void *source, bool success ); + + virtual void OnMostDeviceType( void *source, bool success, uint16_t nodeAddress, uint16_t deviceType ); + + virtual void OnCreateTsiSocketV1( void *source, bool success, uint16_t nodeAddr, V1TsiPortInstance_t tsiPortInst, + EPDataType_t epType, EPDirection_t epDir, uint16_t blockWidthTsi, uint16_t socketHandle, uint32_t tag ); + + virtual void OnCreateMlbSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ); + + virtual void OnCreateMostSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ); + + virtual void OnConnectSocketsV1( void *source, bool success, uint16_t nodeAddr, uint16_t inSocketHandle, + uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ); + + virtual void OnDestroySocketV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, uint32_t tag ); + + virtual void OnDisconnectSocketsV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, uint32_t tag ); + + virtual void OnCreateI2SSocketV1( void *source, bool success, uint16_t nodeAddr, EPDirection_t epDir, + uint16_t blockWidthI2S, V1I2SPin_t pin, uint16_t socketHandle, uint32_t tag ); + + virtual void OnCreateUsbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint8_t endPointAddress, uint16_t socketHandle, uint32_t tag ); + + virtual void OnCreateSplittedUsbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint8_t endPointAddress, uint16_t usbHandle, uint16_t splitterHandle, uint32_t tag ); + + virtual void OnCreateMlbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ); + + virtual void OnCreateSplittedMlbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t mlbSocketHandle, + uint16_t splitterHandle, uint32_t tag ); + + virtual void OnCreateI2SSocketV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t socketHandle, uint32_t tag ); + + virtual void OnCreateSplittedI2SSocketV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t i2sSocketHandle, uint16_t splitterHandle, + uint32_t tag ); + + virtual void OnCreateMostSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ); + + virtual void OnConnectSocketsV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + uint16_t inSocketHandle, uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ); + + virtual void OnControlChannelReadEnd( void *source ); + + virtual void OnMostControlMessage( void *source, uint32_t sourceAddr, uint32_t targetAddr, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ); + + virtual void OnRbdResultV3( void *source, uint16_t nodeAddress, uint8_t result, uint8_t position, + uint8_t status, uint16_t id ); + + virtual void OnMostMacAddress( void *source, bool success, uint16_t nodeAddress, uint8_t macAddress1, + uint8_t macAddress2, uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6 ); + + virtual void OnConfigureI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortOption_t option, V3I2SClockMode_t mode, V3I2SDelayMode_t delay, uint32_t tag ); + + virtual void OnCreateI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortSpeed_t clock, V3I2SAlignment_t align, uint32_t tag ); + + virtual void OnDeviceVersion( void *source, bool success, uint32_t sourceAddr, + uint32_t productId, uint32_t fwVersion, uint32_t buildVersion, + uint8_t hwVersion, uint16_t diagnosisId, uint32_t tag ); +public: + /*----------------------------------------------------------*/ + /*! \brief Destructor of CNetwork. + */ + /*----------------------------------------------------------*/ + virtual ~CNetwork(); + + + + //Singleton pattern, there can only be one manager + /*----------------------------------------------------------*/ + /*! \brief Gets the singleton instance of CNetwork (Only one object). + * \return Pointer to the singleton object. + */ + /*----------------------------------------------------------*/ + static CNetwork *GetInstance( void ); + + + + /*----------------------------------------------------------*/ + /*! \brief Registers the given CNetworkListener to this component. + * If there are events, all registered listeners will be called back. + * \note Multiple listerners are supported. + * \param listener- Class derivating CNetworkListener base class. This class will be called back on events. + */ + /*----------------------------------------------------------*/ + void AddListener( CNetworkListner *listener ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Deregisters the given CNetworkListener from this component. + * The given object will no longer be called back. + * \param listener- Class derivating CNetworkListener base class. This class will not be called after this method call. + */ + /*----------------------------------------------------------*/ + void RemoveListener( CNetworkListner *listener ); + + + /*----------------------------------------------------------*/ + /*! \brief Enables or Disables the promiscuous mode on MEP channel. If enabled, tools like Wireshark will capture every packet. + * \param enabled - true, promiscuous mode. false, perfect match filter with multicast and broadcast enabled. + */ + /*----------------------------------------------------------*/ + void SetPromiscuousMode( bool enabled ); + + + /*----------------------------------------------------------*/ + /*! \brief Loads a XML file holding information about connections and devices. + * \param szConfigXml - string holding only the filename to the XML file. + * \note This method tries to extract the search path from the given string. Therefor it must start with '/'. + * \note The given string will be modified by this method. Don't use it anymore after wise! + * \return true, if successful, false otherwise + */ + /*----------------------------------------------------------*/ + bool LoadConfig( char *szConfigXml ); + + + + /*----------------------------------------------------------*/ + /*! \brief Loads a XML file holding information about connections and devices. + * \param szConfigXml - string holding only the filename to the XML file. + * \param szSearchPath - string holding the path to all the XML files. + * \return true, if successful, false otherwise + */ + /*----------------------------------------------------------*/ + bool LoadConfig( const char *szConfigXml, const char *szSearchPath ); + + + + /*----------------------------------------------------------*/ + /*! \brief Checks if there are pending actions enqueued. + * \note This method blocks, until the result (true / false is reported by the subprocess). + * \return true, when there are pending actions in the queue. false, otherwise + */ + /*----------------------------------------------------------*/ + bool ActionsPending(); + + + + + /*----------------------------------------------------------*/ + /*! \brief Connects a source to sink according to the configuration of the XML file, provided by LoadConfig method. + * \param SourceMacAddr - The MAC address of the source device, reported by the OnChannelAvailable callback method. + * \param SourceChannelId - The channel identifier of the source as specified in the XML file and reported by + * the OnChannelAvailable callback method. + * \param SinkMacAddr - The MAC address of the sink device, reported by the OnChannelAvailable callback method. + * \param SourceChannelId - The channel identifier of the sink as specified in the XML file and reported by + * the OnChannelAvailable callback method. + * \return true, if succesful. false, otherwise + */ + /*----------------------------------------------------------*/ + bool ConnectSourceToSink( CMacAddr *SourceMacAddr, TChannelId SourceChannelId, CMacAddr *SinkMacAddr, + TChannelId SinkChannelId ); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the amount of connected INIC nodes. + * \return The amount of local connected INIC nodes. + */ + /*----------------------------------------------------------*/ + uint8_t GetAmountOfLocalNodes(); + + + /*----------------------------------------------------------*/ + /*! \brief Sends the given Control Message out to the given MOST ring + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring + * \param targetAddr - The MOST target address (0x100, 0x401, etc..) + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + bool SendMostControlMessage( TMostInstace mostInstance, uint32_t targetAddr, uint32_t nFBlock, uint32_t nInst, + uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ); + + + /*----------------------------------------------------------*/ + /*! \brief Sends the given Control Message out to the given MOST ring + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring + * \param deviceId - The device identifier (group address) as specified in the XML file + * \param devInst - The instance number of the device. Starting with 0 for the first device with deviceId + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + bool SendMostControlMessage( TMostInstace mostInstance, TDeviceId deviceId, uint8_t devInst, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ); + + + /*----------------------------------------------------------*/ + /*! \brief Executes a given XML script by it's filename and MOST target address + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring + * \param targetAddr - The MOST target address (0x100, 0x401, etc..) + * \param szFile - Path to the XML file (zero terminated string) + */ + /*----------------------------------------------------------*/ + bool ExecuteXmlScriptFromFile( TMostInstace mostInstance, uint32_t targetAddr, const char *szFile ); + + + /*----------------------------------------------------------*/ + /*! \brief Executes a given XML script by it's filename, device identifier and device instance + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring + * \param deviceId - The device identifier (group address) as specified in the XML file + * \param devInst - The instance number of the device. Starting with 0 for the first device with deviceId + * \param szFile - Path to the XML file (zero terminated string) + */ + /*----------------------------------------------------------*/ + bool ExecuteXmlScriptFromFile( TMostInstace mostInstance, TDeviceId deviceId, uint8_t devInst, + const char *szFile ); + + + /*----------------------------------------------------------*/ + /*! \brief Executes a given XML script directly by it's content stored in the memory and MOST target address + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring + * \param targetAddr - The MOST target address (0x100, 0x401, etc..) + * \param szBuffer - The Buffer containing the XML script + * \param nBufferLen - The length of the XML script stored in szBuffer + */ + /*----------------------------------------------------------*/ + bool ExecuteXmlScriptFromMemory( TMostInstace mostInstance, uint32_t targetAddr, const char *szBuffer, + uint32_t nBufferLen ); + + + /*----------------------------------------------------------*/ + /*! \brief Executes a given XML script by it's filename, device identifier and device instance + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring + * \param targetAddr - The MOST target address (0x100, 0x401, etc..) + * \param szBuffer - The Buffer containing the XML script + * \param nBufferLen - The length of the XML script stored in szBuffer + */ + /*----------------------------------------------------------*/ + bool ExecuteXmlScriptFromMemory( TMostInstace mostInstance, TDeviceId deviceId, uint8_t devInst, + const char *szBuffer, uint32_t nBufferLen ); + + /*! + * \brief Storage class for channel related informations. This class will be returned by + * the CVodXml class. + */ + typedef struct + { + /// Determines the device type as specified in the configuration XML file and found in the group address configuration. + uint32_t deviceType; + + /// instance number of the device found in the network + uint32_t instance; + + /// Determines the used channel id, as specified in the configuration XML file. + uint32_t channelId; + } Route_t; + + /*----------------------------------------------------------*/ + /*! \brief Sends the given Control Message out to the given MOST ring. + * \param pInRoute - Pointer to the route to search the counter part of. + * \param pOutRouteArray - Pointer to pointer, which then the result array will be stored, maybe NULL! + * \return The array length of pOutRouteArray, maybe 0! + * \note The pOutRouteArray must be freed by the user after usage! + */ + /*----------------------------------------------------------*/ + uint32_t GetCounterPartOfRoute( const Route_t *pInRoute, Route_t **pOutRouteArray ); + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the stored result of the Ring Break Diagnosis + * \param mostInstance - The MOST ring instance, starting with 0 for the first MOST ring + * \param targetAddr - The MOST target address (0x100, 0x401, etc..) + * \return true, if successful, false otherwise + */ + /*----------------------------------------------------------*/ + bool GetRingBreakDiagnosisResult( TMostInstace mostInstance, uint32_t targetAddr ); + + + /*----------------------------------------------------------*/ + /*! \brief Enables or disables all MOST rings. + * \note This is thought for debug reasons only. Normally this method must not be called. + * \param enabled - TRUE, all MOST rings will start up. FALSE, all MOST rings will shutdown. + */ + /*----------------------------------------------------------*/ + void EnableMost(bool enabled); + + /*----------------------------------------------------------*/ + /*! \brief Function of background thread. + * \note Never call this method. It is used by internal threads. + */ + /*----------------------------------------------------------*/ + void Run(); + +}; + + +#endif diff --git a/Src/Network/NetworkDevice.cpp b/Src/Network/NetworkDevice.cpp new file mode 100644 index 0000000..5308215 --- /dev/null +++ b/Src/Network/NetworkDevice.cpp @@ -0,0 +1,903 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "NetworkDevice.h" +#include "DriverConfiguration.h" +#include "Board.h" +#include "ScriptXml.h" +#include "IndustrialStack_ApiV1.h" +#include "IndustrialStack_ApiV3.h" +#include "IndustrialStack_ApiGeneric.h" + +CNetworkDevice::CNetworkDevice( uint8_t index, uint8_t api, bool master, uint32_t asyncBW, bool promiscuousMode ) : +CThread( "CNetworkDevice", false ), promiscuous( promiscuousMode ), netState( NetworkState_Unknown ), + pInic( NULL ), pScriptMemoryArea( NULL ), isRunning( false ), + isActionPending( false ), deviceIndex( index ), deviceApi( api ), + isTimingMaster( master ), asyncBandwidth( asyncBW ), + controlEndpointRxHandle( -1 ), controlEndpointTxHandle( -1 ), + startV3autoForced( 0xFFFF ), startV3packetBW( 0xFFFF ) +{ + responseToWaitFor[0] = '\0'; + deviceNameControlRx[0] = '\0'; + deviceNameControlTx[0] = '\0'; + + sem_init( &methodMutex, 0, 0 ); //Mutex, initialized to 0 => sem_wait will block +} + +CNetworkDevice::~CNetworkDevice() +{ + Stop(); + CloseMostControlChannel(); + if (NULL != iStack) + { + delete iStack; + iStack = NULL; + } + if (NULL != v1Events) + { + delete v1Events; + v1Events = NULL; + } + if (NULL != v3Events) + { + delete v3Events; + v3Events = NULL; + } +} + +bool CNetworkDevice::OpenDevice( const char *systemNameControlRx, const char *systemNameControlTx, + const char *linkNameControlRx, const char *linkNameControlTx ) +{ + ConsolePrintf( PRIO_LOW, "SYS RX:%s\nSYS TX:%s\nLINK RX:%s\nLINK TX:%s\n", systemNameControlRx, + systemNameControlTx, linkNameControlRx, linkNameControlTx ); + + CloseMostChannel( systemNameControlRx ); + CloseMostChannel( systemNameControlTx ); + + bool success = ConfigureMostChannel( systemNameControlRx, EP_Control, EPDIR_OUT, 32, 128 ); + + if( success ) + { + success = ConfigureMostChannel( systemNameControlTx, EP_Control, EPDIR_IN, 32, 128 ); + } + + if( success ) + { + success = LinkToCharacterDevice( linkNameControlRx, deviceNameControlRx ); + } + + if( success ) + { + success = LinkToCharacterDevice( linkNameControlTx, deviceNameControlTx ); + } + + if( success ) + { + if( OpenMostControlChannel() ) + { + isRunning = true; + Start(); + } + else + { + ConsolePrintf( PRIO_ERROR, RED"MOST control channel startup failed"RESETCOLOR"\n" ); + } + + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"NetworkDevice: Open device aborted, because of channel configuration issues"RESETCOLOR"\n" ); + } + return success; +} + +bool CNetworkDevice::OpenUsb( uint8_t controlRxEndpointAddress, uint8_t controlTxEndpointAddress ) +{ + char systemNameControlRx[64]; + char systemNameControlTx[64]; + char linkNameControlRx[64]; + char linkNameControlTx[64]; + bool success = GetUsbDeviceNames( deviceIndex, controlRxEndpointAddress, + deviceNameControlRx, sizeof( deviceNameControlRx ), systemNameControlRx, sizeof( systemNameControlRx ), + linkNameControlRx, sizeof( linkNameControlRx ) ); + + if( success ) + success = GetUsbDeviceNames( deviceIndex, controlTxEndpointAddress, + deviceNameControlTx, sizeof( deviceNameControlTx ), systemNameControlTx, sizeof( systemNameControlTx ), + linkNameControlTx, sizeof( linkNameControlTx ) ); + + if( success ) + success = OpenDevice( systemNameControlRx, systemNameControlTx, linkNameControlRx, linkNameControlTx ); + + if( ! success ) + ConsolePrintf( PRIO_ERROR, RED"NetworkDevice: Failed to open USB device"RESETCOLOR"\n" ); + + return success; +} + +bool CNetworkDevice::OpenMlb( uint8_t controlRxChannelAddress, uint8_t controlTxChannelAddress ) +{ + char systemNameControlRx[64]; + char systemNameControlTx[64]; + char linkNameControlRx[64]; + char linkNameControlTx[64]; + bool success = GetMlbDeviceNames( deviceIndex, controlRxChannelAddress, + deviceNameControlRx, sizeof( deviceNameControlRx ), systemNameControlRx, sizeof( systemNameControlRx ), + linkNameControlRx, sizeof( linkNameControlRx ) ); + + if( success ) + success = GetMlbDeviceNames( deviceIndex, controlTxChannelAddress, + deviceNameControlTx, sizeof( deviceNameControlTx ), systemNameControlTx, sizeof( systemNameControlTx ), + linkNameControlTx, sizeof( linkNameControlTx ) ); + + if( success ) + success = OpenDevice( systemNameControlRx, systemNameControlTx, linkNameControlRx, linkNameControlTx ); + + if( ! success ) + ConsolePrintf( PRIO_ERROR, RED"NetworkDevice: Failed to open MLB device"RESETCOLOR"\n" ); + + return success; +} + +bool CNetworkDevice::OpenI2C() +{ + char systemNameControlRx[64]; + char systemNameControlTx[64]; + char linkNameControlRx[64]; + char linkNameControlTx[64]; + bool success = GetI2CDeviceNames( deviceIndex, false, deviceNameControlRx, sizeof( deviceNameControlRx ), + systemNameControlRx, sizeof( systemNameControlRx ), linkNameControlRx, sizeof( linkNameControlRx ) ); + + if( success ) + success = GetI2CDeviceNames( deviceIndex, true, deviceNameControlTx, sizeof( deviceNameControlTx ), + systemNameControlTx, sizeof( systemNameControlTx ), linkNameControlTx, sizeof( linkNameControlTx ) ); + + if( success ) + success = OpenDevice( systemNameControlRx, systemNameControlTx, linkNameControlRx, linkNameControlTx ); + + if( ! success ) + ConsolePrintf( PRIO_ERROR, RED"NetworkDevice: Failed to open I2C device"RESETCOLOR"\n" ); + + return success; +} + +void CNetworkDevice::Close() +{ + int32_t timeoutVal = 10; //10 Seconds + while( timeoutVal-- > 0 ) + { + if( iStack->ElementsPending() ) + { + ConsolePrintf( PRIO_MEDIUM, "NetworkDevice: Closing delayed, Actions pending..\n" ); + usleep( 1000000 ); + } + else + { + break; + } + } + sem_post( &methodMutex ); + isRunning = false; + Stop(); + for( uint32_t timeout = 0; timeout < 100; timeout++ ) + { + if( !IsThreadRunning() ) + break; + usleep( 10000 ); + } + ConsolePrintf( PRIO_LOW, "NetworkDevice: Closed..\n" ); +} + +uint8_t CNetworkDevice::GetDeviceApi() +{ + return deviceApi; +} + +NetworkState_t CNetworkDevice::GetNetState() +{ + return netState; +} + +void CNetworkDevice::SetNetstate( NetworkState_t newState ) +{ + netState = newState; +} + + void CNetworkDevice::SetTimingMaster(bool tm) + { + isTimingMaster = tm; + } + +bool CNetworkDevice::IsTimingMaster() +{ + return isTimingMaster; +} + +void CNetworkDevice::SetAsyncBandwidth(uint32_t bw) +{ + asyncBandwidth = bw; +} + +uint32_t CNetworkDevice::GetAsyncBandwidth() +{ + return asyncBandwidth; +} + +bool CNetworkDevice::ActionsPending() +{ + if( NULL == iStack || !isRunning ) + return true; + return iStack->ElementsPending(); +} + +void CNetworkDevice::ClearAllPendingActions() +{ + if( NULL == iStack ) + return; + iStack->ClearAllElements(); +} + +void CNetworkDevice::AddListener( CNetworkDeviceListener *listener ) +{ + allListeners.PushBack( listener ); +} + +uint32_t CNetworkDevice::GetAmountOfListners() +{ + return allListeners.Size(); +} + +CNetworkDeviceListener *CNetworkDevice::GetListener( uint32_t instance ) +{ + return allListeners[instance]; +} + +uint8_t CNetworkDevice::GetDeviceIndex() +{ + return deviceIndex; +} + +void CNetworkDevice::ScriptPause( uint32_t timeInMillis ) +{ + CISWaitElement *waitElement = new CISWaitElement(); + waitElement->SetTimeout(timeInMillis); + iStack->EnqueueElement(0x1, waitElement); + waitElement->RemoveReference(); +} + +void CNetworkDevice::ExecuteMcmScript( uint16_t nodeAddress, CSciptXml *scriptXml ) +{ + if( NULL == scriptXml ) + { + ConsolePrintf( + PRIO_ERROR, RED"ExecuteMcmScript failed to retrieve scripts from XML file"RESETCOLOR"\n" ); + return; + } + CSafeVector allScripts; + if( scriptXml->GetScripts( allScripts ) ) + { + for (uint32_t i = 0; i < allScripts.Size(); i++ ) + { + CScript *script = allScripts[i]; + if( NULL == script ) + continue; + switch( script->scriptType ) + { + case SCRIPT_MCM_SEND: + { + CGeneric_SendMostMessage *a = new CGeneric_SendMostMessage( + script->payload, script->payloadLength, nodeAddress, + script->fblockId, 0, script->functionId, script->opTypeRequest, + script->opTypeResponse); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); + break; + } + case SCRIPT_PAUSE: + { + CISWaitElement *waitElement = new CISWaitElement(); + waitElement->SetTimeout(script->pauseInMillis); + iStack->EnqueueElement(nodeAddress, waitElement); + waitElement->RemoveReference(); + break; + } + default: + ConsolePrintf( PRIO_ERROR, + YELLOW"ExecuteMcmScript ignoring unknown script type:%d"RESETCOLOR"\n", + script->scriptType ); + break; + } + } + } + allScripts.RemoveAll(true); +} + +void CNetworkDevice::ExecuteMcmScript( uint16_t nodeAddress, const char *pPathToScriptXml ) +{ + if( NULL == pPathToScriptXml ) + { + ConsolePrintf( PRIO_ERROR, RED"ExecuteMcmScript (from file) was called with invalid parameter"RESETCOLOR"\n" ); + return; + } + ConsolePrintf( PRIO_MEDIUM, "##### Executing MCM script from file: Address:%X '%s'\n", nodeAddress, + pPathToScriptXml ); + CSciptXml *scriptXml = new CSciptXml( pPathToScriptXml ); + ExecuteMcmScript( nodeAddress, scriptXml ); +} + +void CNetworkDevice::ExecuteMcmScript( uint16_t nodeAddress, const char *pStringBuffer, uint32_t bufferLength ) +{ + if( NULL == pStringBuffer || 0 == bufferLength ) + { + ConsolePrintf( PRIO_ERROR, RED"ExecuteMcmScript (from memory) was called with invalid parameter"RESETCOLOR"\n" ); + return; + } + ConsolePrintf( PRIO_MEDIUM, "##### Executing MCM script from memory: Address:%X buffer-len:\n", nodeAddress, + bufferLength ); + CSciptXml *scriptXml = new CSciptXml( pStringBuffer, bufferLength ); + ExecuteMcmScript( nodeAddress, scriptXml ); +} + +void CNetworkDevice::ToggleNotOk() +{ + CGeneric_SendConfigOk *a = new CGeneric_SendConfigOk(false); + iStack->EnqueueElement(0x3C8, a); + a->RemoveReference(); + + CGeneric_SendConfigOk *b = new CGeneric_SendConfigOk(true); + iStack->EnqueueElement(0x3C8, b); + b->RemoveReference(); +} + +void CNetworkDevice::GetMacAddress( uint16_t nodeAddress ) +{ + CGeneric_GetMacAddress *a = new CGeneric_GetMacAddress(nodeAddress); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::GetGroupAddresses( uint8_t maxNodePos ) +{ + for( uint32_t i = 0x400; maxNodePos != 0xFF && i < 0x400 + ( uint32_t )maxNodePos; i++ ) + { + uint16_t n = ( i == 0x400 ? 1 : i ); + CGeneric_GetGroupAddress *a = new CGeneric_GetGroupAddress(n); + iStack->EnqueueElement(n, a); + a->RemoveReference(); + } +} + +void CNetworkDevice::SetMostMacAddressV1( uint16_t nodeAddress, uint8_t macAddress1, uint8_t macAddress2, + uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6, bool persistent ) +{ + CV1_ChangeEUI48 *a = new CV1_ChangeEUI48(this, nodeAddress, macAddress1, macAddress2, + macAddress3, macAddress4, macAddress5, macAddress6, persistent); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::StartMostV1( bool isTimingMaster, uint16_t packetBandWidth ) +{ + CV1_NetworkStartup *a = new CV1_NetworkStartup(this, isTimingMaster, packetBandWidth); + iStack->EnqueueElement(0x1, a); + a->RemoveReference(); +} + +void CNetworkDevice::MostNetworkShutdownV1() +{ + CISWaitForPendingElements *w = new CISWaitForPendingElements(0x1); + iStack->EnqueueElement(0x1, w); + w->RemoveReference(); + + CV1_NetworkShutdown *a = new CV1_NetworkShutdown(this); + iStack->EnqueueElement(0x1, a); + a->RemoveReference(); +} + +void CNetworkDevice::OpenTsiV1( uint16_t nodeAddress, V1TsiPortInstance_t tsiPortId, V1TsiPortMode tsiPortMode ) +{ + if (V1TsiPortInstanceNotSet == tsiPortId || V1TsiPortModeNotSet == tsiPortMode) + return; + CV1_TsiPortCreate *a = new CV1_TsiPortCreate(this, nodeAddress, tsiPortId, tsiPortMode); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateTsiSocketV1( uint16_t nodeAddress, V1TsiPortInstance_t portInst, + EPDataType_t epType, EPDirection_t epDir,uint16_t blockWidthTsi, uint32_t tag ) +{ + CV1_TsiSocketCreate *a = new CV1_TsiSocketCreate(this, nodeAddress, portInst, + epType, epDir, blockWidthTsi, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::OpenMlbV1( uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed ) +{ + if( MlbSpeedNotSet == mlbSpeed ) + return; + CV1_MlbPortCreate *a = new CV1_MlbPortCreate(this, nodeAddress, mlbSpeed); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateMlbSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint16_t mlbChannelAddress, uint16_t blockWidthMlb, uint32_t tag ) +{ + CV1_MlbSocketCreate *a = new CV1_MlbSocketCreate(this, nodeAddress, epType, epDir, + mlbChannelAddress, blockWidthMlb, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateSplittedMlbSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint16_t mlbChannelAddress, uint16_t blockWidthMlb, uint16_t splittedOffset, uint16_t blockWidthCombined, + uint32_t tag ) +{ + CV1_SplittedMlbSocketCreate *a = new CV1_SplittedMlbSocketCreate(this, nodeAddress, + epType, epDir, mlbChannelAddress, blockWidthMlb, splittedOffset, blockWidthCombined, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateMostSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint16_t connectionLabel, uint16_t blockWidthMost, uint32_t tag ) +{ + CV1_MostSocketCreate *a = new CV1_MostSocketCreate(this, nodeAddress, + epType, epDir, connectionLabel, blockWidthMost, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::ConnectSocketsV1( uint16_t nodeAddress, uint8_t inHandle, uint8_t outHandle, uint32_t tag ) +{ + CV1_ConnectSockets *a = new CV1_ConnectSockets(this, nodeAddress, + inHandle, outHandle, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::DestroySocketV1( uint16_t nodeAddress, uint8_t handle, uint32_t tag ) +{ + CV1_DestroySocket *a = new CV1_DestroySocket(this, nodeAddress, handle, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::DisconnectSocketsV1( uint16_t nodeAddress, uint8_t handle, uint32_t tag ) +{ + CV1_DisconnectSockets *a = new CV1_DisconnectSockets(this, nodeAddress, handle, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::OpenStreamPortV1( uint16_t nodeAddress, V1I2SPortClkDriveMode_t portMode, + V1I2SStreamingPortMode_t streamPortMode, V1I2SStreamingDataFormat_t format, uint32_t tag ) +{ + CV1_StreamPortOpen *a = new CV1_StreamPortOpen(this, nodeAddress, portMode, streamPortMode, format, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateStreamSocketV1( uint16_t nodeAddress, EPDirection_t epDir, uint16_t blockWidthI2S, + V1I2SPin_t pin, uint32_t tag ) +{ + CV1_StreamSocketCreate *a = new CV1_StreamSocketCreate(this, nodeAddress, epDir, blockWidthI2S, pin, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateSplittedStreamSocketV1( uint16_t nodeAddress, EPDirection_t epDir, uint16_t blockWidthI2S, + uint16_t splittedOffset, uint16_t blockWidthCombined, V1I2SPin_t pin, uint32_t tag ) +{ + CV1_SplittedStreamSocketCreate *a = new CV1_SplittedStreamSocketCreate(this, + nodeAddress, epDir, blockWidthI2S, splittedOffset, blockWidthCombined, pin, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::AttachV3() +{ + CV3_DeviceAttach *a = new CV3_DeviceAttach(); + iStack->EnqueueElement(0x1, a); + a->RemoveReference(); +} + +void CNetworkDevice::InicUnsychronizeV3() +{ + CISWaitForPendingElements *w = new CISWaitForPendingElements(0x1); + iStack->EnqueueElement(0x1, w); + w->RemoveReference(); + + CV3_Unsynchronize *a = new CV3_Unsynchronize(); + iStack->EnqueueElement(0x1, a); + a->RemoveReference(); +} + +void CNetworkDevice::DeviceSyncV3( uint16_t nodeAddress, bool sync ) +{ + CV3_DeviceSync *a = new CV3_DeviceSync(nodeAddress, sync); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::MostNetworkStartupV3( uint16_t autoForcedNotAvailable, uint16_t packetBW ) +{ + startV3autoForced = autoForcedNotAvailable; + startV3packetBW = packetBW; + CV3_NetworkStartup *a = new CV3_NetworkStartup(this, autoForcedNotAvailable, packetBW); + iStack->EnqueueElement(0x1, a); + a->RemoveReference(); +} + +void CNetworkDevice::MostNetworkStartupV3() +{ + if( 0xFFFF == startV3packetBW ) + { + ConsolePrintf( PRIO_ERROR, RED"Repeated MostNetworkStartupV3 was called"\ + " without calling the overloaded MostNetworkStartupV3 with parameters"\ + RESETCOLOR"\n" ); + return; + } + CV3_NetworkStartup *a = new CV3_NetworkStartup(this, startV3autoForced, startV3packetBW); + iStack->EnqueueElement(0x1, a); + a->RemoveReference(); +} + +void CNetworkDevice::MostNetworkShutdownV3() +{ + CISWaitForPendingElements *w = new CISWaitForPendingElements(0x1); + iStack->EnqueueElement(0x1, w); + w->RemoveReference(); + + CV3_NetworkShutdown *a = new CV3_NetworkShutdown(this); + iStack->EnqueueElement(0x1, a); + a->RemoveReference(); +} + +void CNetworkDevice::SetMostMacAddressV3( uint16_t nodeAddress, uint8_t macAddress1, uint8_t macAddress2, + uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6 ) +{ + CV3_MostNetworkConfiguration *a = new CV3_MostNetworkConfiguration(this, nodeAddress, macAddress1, macAddress2, + macAddress3, macAddress4, macAddress5, macAddress6, promiscuous); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::OpenMlbV3( uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed ) +{ + if( MlbSpeedNotSet == mlbSpeed ) + return; + CV3_MlbPortCreate *a = new CV3_MlbPortCreate(this, nodeAddress, mlbSpeed); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateUsbSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint8_t endPointAddress, uint16_t packetsPerFrame, uint32_t tag ) +{ + CV3_UsbSocketCreate *a = new CV3_UsbSocketCreate(this, nodeAddress, epType, + epDir, endPointAddress, packetsPerFrame, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateSplittedUsbSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint8_t endPointAddress, uint16_t packetsPerFrame, uint16_t bytesPerPacket, uint32_t tag ) +{ + CV3_SplittedUsbSocketCreate *a = new CV3_SplittedUsbSocketCreate(this, nodeAddress, + epType, epDir, endPointAddress, packetsPerFrame, bytesPerPacket, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateMlbSocketV3( uint16_t nodeAddress, uint16_t mlbPortHandle, EPDataType_t epType, + EPDirection_t epDir, uint16_t mlbChannelAddress, uint16_t blockwidthMlb, uint32_t tag ) +{ + CV3_MlbSocketCreate *a = new CV3_MlbSocketCreate(this, nodeAddress, mlbPortHandle, + epType, epDir, mlbChannelAddress, blockwidthMlb, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateSplittedMlbSocketV3( uint16_t nodeAddress, uint16_t mlbPortHandle, EPDataType_t epType, + EPDirection_t epDir, uint16_t mlbChannelAddress, uint16_t blockwidthMlb, uint16_t bytesPerPacket, uint32_t tag ) +{ + CV3_SplittedMlbSocketCreate *a = new CV3_SplittedMlbSocketCreate(this, nodeAddress, + mlbPortHandle, epType, epDir, mlbChannelAddress, blockwidthMlb, bytesPerPacket, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateMostSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint16_t connectionLabel, uint16_t blockwidthMost, uint32_t tag ) +{ + CV3_MostSocketCreate *a = new CV3_MostSocketCreate(this, nodeAddress, epType, + epDir, connectionLabel, blockwidthMost, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::ConnectSocketsV3( uint16_t nodeAddress, EPDataType_t epType, uint16_t inHandle, + uint16_t outHandle, uint16_t offset, uint32_t tag ) +{ + CV3_ConnectSockets *a = new CV3_ConnectSockets(this, nodeAddress, epType, + inHandle, outHandle, offset, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::ResourceDestroyV3( uint16_t nodeAddress, uint8_t amountOfHandles, const uint16_t *pHandle, + uint32_t tag ) +{ + CV3_ResourceDestroy *a = new CV3_ResourceDestroy(this, nodeAddress, + amountOfHandles, pHandle, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::ConfigureStreamPortV3( uint16_t nodeAddress, uint8_t portInstance, V3I2SPortOption_t option, + V3I2SClockMode_t mode, V3I2SDelayMode_t delay, uint32_t tag ) +{ + CV3_StreamPortConfig *a = new CV3_StreamPortConfig(this, nodeAddress, + portInstance, option, mode, delay, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateStreamPortV3( uint16_t nodeAddress, uint8_t portInstance, V3I2SPortSpeed_t clock, + V3I2SAlignment_t align, uint32_t tag ) +{ + CV3_StreamPortCreate *a = new CV3_StreamPortCreate(this, nodeAddress, + portInstance, clock, align, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateStreamSocketV3( uint16_t nodeAddress, uint8_t portInstance, EPDirection_t epDir, + uint16_t blockWidthI2S, V3I2SPin_t pin, uint32_t tag ) +{ + CV3_StreamSocketCreate *a = new CV3_StreamSocketCreate(this, nodeAddress, + portInstance, epDir, blockWidthI2S, pin, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::CreateSplittedStreamSocketV3( uint16_t nodeAddress, uint8_t portInstance, EPDirection_t epDir, + uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t bytesPerPacket, uint32_t tag ) +{ + CV3_SplittedStreamSocketCreate *a = new CV3_SplittedStreamSocketCreate(this, nodeAddress, + portInstance, epDir, blockWidthI2S, pin, bytesPerPacket, tag); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +void CNetworkDevice::GetRingBreakResultV3( uint16_t nodeAddress, uint32_t tag ) +{ + CV3_GetRbdResult *a = new CV3_GetRbdResult(nodeAddress); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +bool CNetworkDevice::OpenMostControlChannel( void ) +{ + bool success = false; + for (uint8_t i = 0; !success && i < 10; i++) + { + if (-1 == controlEndpointRxHandle) + controlEndpointRxHandle = open( deviceNameControlRx, O_RDONLY ); + if (-1 == controlEndpointTxHandle) + controlEndpointTxHandle = open( deviceNameControlTx, O_WRONLY ); + success = (-1 != controlEndpointRxHandle && -1 != controlEndpointTxHandle); + if (!success) + { + ConsolePrintf( PRIO_ERROR, RED"Opened file handles failed TX:%d RX:%d, retrying.."RESETCOLOR"\n", controlEndpointTxHandle, controlEndpointRxHandle); + usleep(1000000); + } + } + + if( success ) + { + iStack = new CIndustrialStack(deviceApi, controlEndpointRxHandle, controlEndpointTxHandle); + v1Events = new CV1_OnMostRx(this); + v3Events = new CV3_OnMostRx(this); + iStack->AddInternalEventListener(this); + iStack->AddInternalEventListener(v1Events); + iStack->AddInternalEventListener(v3Events); + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Failure while opening MOST control channel" \ + " ('%s' or '%s'), error='%s'"RESETCOLOR"\n" + , deviceNameControlRx, deviceNameControlTx, GetErrnoString() ); + } + return success; +} + +void CNetworkDevice::Run( void ) +{ + iStack->ServiceStack(GetTickCount()); + usleep( 1000 ); +} + +void CNetworkDevice::CloseMostControlChannel( void ) +{ + close( controlEndpointTxHandle ); + close( controlEndpointRxHandle ); + if (NULL != iStack) + { + delete iStack; + iStack = NULL; + } +} + +void CNetworkDevice::WaitForIpcResponse( const char *responseMessage ) +{ + if( isRunning ) + { + strncpy( ( char * )responseToWaitFor, responseMessage, sizeof( responseToWaitFor ) ); + sem_wait( &methodMutex ); + responseToWaitFor[0] = '\0'; + } +} + +bool CNetworkDevice::ConfigureUsbEndpoint( AimType_t aimType, uint8_t endpointAddress, EPDataType_t epType, + EPDirection_t epDir, char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize, + uint32_t subbufferSize, uint32_t packetsPerTransaction ) +{ + char systemName[64]; + char linkName[64]; + bool success = GetUsbDeviceNames( deviceIndex, endpointAddress, deviceName, deviceNameBufLen, + systemName, sizeof( systemName ), linkName, sizeof( linkName ) ); + if( !success ) + return success; + CloseMostChannel( systemName ); + switch( epType ) + { + case EP_Synchron: + success = ConfigureSyncChannel( systemName, subbufferSize, packetsPerTransaction ); + if( success ) + success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize ); + break; + case EP_Isochron: + success = ConfigureIsocChannel( systemName, subbufferSize, packetsPerTransaction ); + if( success ) + success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize ); + break; + default: + break; + } + if( success ) + { + switch( aimType ) + { + case AIM_AUDIO: + success = LinkToAudioDevice( linkName, subbufferSize ); + break; + case AIM_V4L: + success = LinkToVideoForLinuxDevice( linkName ); + break; + case AIM_CDEV: + default: + success = LinkToCharacterDevice( linkName, deviceName ); + break; + } + } + return success; +} + +bool CNetworkDevice::ConfigureUsbEndpoint( uint8_t endpointAddress, EPDataType_t epType, EPDirection_t epDir, + char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize ) +{ + return ConfigureUsbEndpoint( AIM_CDEV, endpointAddress, epType, epDir, deviceName, deviceNameBufLen, + amountOfBuffers, bufferSize, 0, 0 ); +} + +bool CNetworkDevice::ConfigureMlbChannel( AimType_t aimType, uint8_t mlbChannelAddress, EPDataType_t epType, + EPDirection_t epDir, char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize, + uint32_t subbufferSize, uint32_t packetsPerTransaction ) +{ + char systemName[64]; + char linkName[64]; + bool success = GetMlbDeviceNames( deviceIndex, mlbChannelAddress, deviceName, deviceNameBufLen, + systemName, sizeof( systemName ), linkName, sizeof( linkName ) ); + if( !success ) + return false; + CloseMostChannel( systemName ); + switch( epType ) + { + case EP_Synchron: + success = ConfigureSyncChannel( systemName, subbufferSize, packetsPerTransaction ); + if( success ) + success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize ); + if( success ) + { + switch( aimType ) + { + case AIM_AUDIO: + success = LinkToAudioDevice( linkName, subbufferSize ); + break; + case AIM_CDEV: + default: + success = LinkToCharacterDevice( linkName, deviceName ); + break; + } + } + break; + case EP_Isochron: + success = ConfigureIsocChannel( systemName, subbufferSize, packetsPerTransaction ); + if( success ) + success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize ); + if( success ) + success = LinkToCharacterDevice( linkName, deviceName ); + break; + default: + break; + } + return success; +} + +bool CNetworkDevice::ConfigureMlbChannel( uint8_t mlbChannelAddress, EPDataType_t epType, EPDirection_t epDir, + char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize ) +{ + return ConfigureMlbChannel( AIM_CDEV, mlbChannelAddress, epType, epDir, deviceName, deviceNameBufLen, + amountOfBuffers, bufferSize, 0, 0 ); +} + +bool CNetworkDevice::SendMostControlMessage( uint8_t devInst, uint32_t targetAddr, uint32_t nFBlock, uint32_t nInst, + uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) +{ + CGeneric_SendMostMessage *a = new CGeneric_SendMostMessage(Payload, + nPayloadLen, targetAddr, nFBlock, nInst, nFunc, nOpType, 0xFF); + iStack->EnqueueElement(targetAddr, a); + a->RemoveReference(); + return true; +} + +void CNetworkDevice::GetDeviceVersion( uint16_t nodeAddress ) +{ + CV3_DeviceVersion *a = new CV3_DeviceVersion(this, nodeAddress, 0); + iStack->EnqueueElement(nodeAddress, a); + a->RemoveReference(); +} + +ISReturn_t CNetworkDevice::OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r) +{ + if (NULL == r) + return ISReturn_NoChange; + for (uint32_t i = 0; i < allListeners.Size(); i++) + { + allListeners[i]->OnMostControlMessage(this, r->SourceAddress, r->TargetAddress, + r->FBlock, r->Inst, r->Func, r->OpType, r->PayloadLen, r->Payload); + } + return ISReturn_NoChange; +} + diff --git a/Src/Network/NetworkDevice.h b/Src/Network/NetworkDevice.h new file mode 100644 index 0000000..8a67556 --- /dev/null +++ b/Src/Network/NetworkDevice.h @@ -0,0 +1,862 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CNetworkDevice class. + */ +/*----------------------------------------------------------*/ +#ifndef _NETWORKDEVICE_H_ +#define _NETWORKDEVICE_H_ + +#include +#include +#include +#include "SafeVector.h" +#include "ScriptXml.h" +#include "Types.h" +#include "Thread.h" +#include "NetworkDeviceListener.h" +#include "IndustrialStack.h" +#include "IndustrialStack_Types.h" + +typedef enum +{ + NetworkState_Unknown, + NetworkState_Unavailable, + NetworkState_Available, + NetworkState_ShutdownInProgress +} NetworkState_t; + +/*----------------------------------------------------------*/ +/*! \brief This class represents an INIC instance. + * It will be multiple instanced, for each connected OS81118. + * It generates INIC requests and parses INIC responses. + */ +/*----------------------------------------------------------*/ +class CNetworkDevice : public CThread, public CSInternalEvent +{ +private: + CIndustrialStack *iStack; + CV1_OnMostRx *v1Events; + CV3_OnMostRx *v3Events; + bool promiscuous; + NetworkState_t netState; + void *pInic; + void *pScriptMemoryArea; + bool isRunning; + bool isActionPending; + uint8_t deviceIndex; + uint8_t deviceApi; + bool isTimingMaster; + uint32_t asyncBandwidth; + sem_t methodMutex; + int controlEndpointRxHandle; + int controlEndpointTxHandle; + char *responseToWaitFor[64]; + char deviceNameControlRx[64]; + char deviceNameControlTx[64]; + CSafeVector allListeners; + + uint16_t startV3autoForced; + uint16_t startV3packetBW; + + bool OpenDevice( const char *systemNameControlRx, const char *systemNameControlTx, const char *linkNameControlRx, + const char *linkNameControlTx ); + bool OpenMostControlChannel( void ); + void CloseMostControlChannel( void ); + void WaitForIpcResponse( const char *responseMessage ); + void ExecuteMcmScript( uint16_t nodeAddress, CSciptXml *scriptXml ); +public: + /*----------------------------------------------------------*/ + /*! \brief Constructor of CNetworkDevice. + * \param deviceIndex - The index of the physical device instance of the server device. + * Starting at 0 for the first device instance. + * \param deviceApi - 1: OS81092, OS81110; 2: OS81118-C-Rev; 3:OS81118-D-Rev + * \param isTimingMaster - true, if this device shall act as timing master, otherwise it is timing slave + * \param asyncBandwidth - if set as timing master, how many bytes shall be reserved for the asynchronous channel + * \param promiscuousMode - true, promiscuous mode. false, perfect match filter with multicast and broadcast enabled. If enabled, tools like Wireshark will capture every packet. + */ + /*----------------------------------------------------------*/ + CNetworkDevice( uint8_t deviceIndex, uint8_t deviceApi, bool isTimingMaster, uint32_t asyncBandwidth, + bool promiscuousMode ); + + + /*----------------------------------------------------------*/ + /*! \brief Destructor of CNetworkDevice. + */ + /*----------------------------------------------------------*/ + virtual ~CNetworkDevice(); + + + /*----------------------------------------------------------*/ + /*! \brief Function of background thread. + * \note Do never call this method. It is used by internal threads. + */ + /*----------------------------------------------------------*/ + void Run(); + + + /*----------------------------------------------------------*/ + /*! \brief Opens the USB connection to the INIC. + * \param controlRxEndpointAddress - The USB endpoint address of the RX direction. + * \param controlTxEndpointAddress - The USB endpoint address of the TX direction. + * \return true, if successful. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool OpenUsb( uint8_t controlRxEndpointAddress, uint8_t controlTxEndpointAddress ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Opens the MLB connection to the INIC. + * \param controlRxChannelAddress - The MLB channel address of the RX direction. + * \param controlTxChannelAddress - The MLB channel address of the TX direction. + * \return true, if successful. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool OpenMlb( uint8_t controlRxChannelAddress, uint8_t controlTxChannelAddress ); + + + /*----------------------------------------------------------*/ + /*! \brief Opens the I2C connection to the INIC. + * \param inicApi - INIC API Version + * \return true, if successful. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool OpenI2C(); + + + /*----------------------------------------------------------*/ + /*! \brief Closes the USB connection to the INIC. + */ + /*----------------------------------------------------------*/ + void Close(); + + /*----------------------------------------------------------*/ + /*! \brief Returns the used API of the INIC. + * \return INIC API Version 1: OS81092, OS81110; 2: OS81118-C-Rev; 3:OS81118-D-Rev + */ + /*----------------------------------------------------------*/ + uint8_t GetDeviceApi(); + + NetworkState_t GetNetState(); + void SetNetstate( NetworkState_t newState ); + void SetTimingMaster(bool tm); + bool IsTimingMaster(); + void SetAsyncBandwidth(uint32_t bw); + uint32_t GetAsyncBandwidth(); + + /*----------------------------------------------------------*/ + /*! \brief Determines if there are jobs currently in the working queue. + * \note This method will block until the result is queried from the worker process. + * \return true, if there are pending actions enqueued. + */ + /*----------------------------------------------------------*/ + bool ActionsPending(); + + + + /*----------------------------------------------------------*/ + /*! \brief Remove all pending actions from the working queue. + */ + /*----------------------------------------------------------*/ + void ClearAllPendingActions(); + + + + /*----------------------------------------------------------*/ + /*! \brief Configure the Linux character device driver to handle an USB connection. + * \note This overloaded function is used to configure synchronous channels. + * \param aimType - Specifies the type of used Linux Driver Application Interface Module. + * \param endpointAddress - The USB endpoint address. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param deviceName - On successful configuration, this method stores the name of the character device into the given buffer. + * \param deviceNameBufLen - The length of the given buffer. + * \param amountOfBuffers - The amount of buffers used in the driver. + * \param bufferSize - The amount of bytes used in a single buffer. + * \param subbufferSize - The amount of bytes used for a single subbuffer. For isoc channel this maybe 188 or 196 byte. + * \param packetsPerTransaction - The amount of packets per transaction. + * \return true, if successful. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool ConfigureUsbEndpoint( AimType_t aimType, uint8_t endpointAddress, EPDataType_t epType, EPDirection_t epDir, + char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize, + uint32_t subbufferSize, uint32_t packetsPerTransaction ); + + + + /*----------------------------------------------------------*/ + /*! \brief Configure the Linux character device driver to handle an USB connection. + * \param endpointAddress - The USB endpoint address. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param deviceName - On successful configuration, this method stores the name of the character device into the given buffer. + * \param deviceNameBufLen - The length of the given buffer. + * \param amountOfBuffers - The amount of buffers used in the driver. + * \param bufferSize - The amount of bytes used in a single buffer. + * \return true, if successful. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool ConfigureUsbEndpoint( uint8_t endpointAddress, EPDataType_t epType, EPDirection_t epDir, char *deviceName, + uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize ); + + + /*----------------------------------------------------------*/ + /*! \brief Configure the Linux character device driver to handle an MLB connection. + * \note This overloaded function is used to configure synchronous channels. + * \param aimType - Specifies the type of used Linux Driver Application Interface Module. + * \param mlbChannelAddress - The MLB channel address + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param deviceName - On successful configuration, this method stores the name of the character device into the given buffer. + * \param deviceNameBufLen - The length of the given buffer. + * \param amountOfBuffers - The amount of buffers used in the driver. + * \param bufferSize - The amount of bytes used in a single buffer. + * \param subbufferSize - The amount of bytes used for a single subbuffer. For isoc channel this maybe 188 or 196 byte. + * \param packetsPerTransaction - The amount of packets per transaction. + * \return true, if successful. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool ConfigureMlbChannel( AimType_t aimType, uint8_t mlbChannelAddress, EPDataType_t epType, EPDirection_t epDir, + char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize, + uint32_t subbufferSize, uint32_t packetsPerTransaction ); + + + /*----------------------------------------------------------*/ + /*! \brief Configure the Linux character device driver to handle an MLB connection. + * \param mlbChannelAddress - The MLB channel address + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param deviceName - On successful configuration, this method stores the name of the character device into the given buffer. + * \param deviceNameBufLen - The length of the given buffer. + * \param amountOfBuffers - The amount of buffers used in the driver. + * \param bufferSize - The amount of bytes used in a single buffer. + * \return true, if successful. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool ConfigureMlbChannel( uint8_t mlbChannelAddress, EPDataType_t epType, EPDirection_t epDir, char *deviceName, + uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize ); + + + + /*----------------------------------------------------------*/ + /*! \brief Adds a callback class, which derives CNetworkDeviceListener. + * This class will then be informed about any results and changes of MOST transactions. + * \note It is possible to register multiple listeners. + * \param listener - The class, which wants to be registered for MOST events. + */ + /*----------------------------------------------------------*/ + void AddListener( CNetworkDeviceListener *listener ); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the amount of registered listeners. + * \note Use this method in combination of GetListener() + * \return The amount of registered listeners. + */ + /*----------------------------------------------------------*/ + uint32_t GetAmountOfListners(); + + + /*----------------------------------------------------------*/ + /*! \brief Gets a pointer to the class implementing the CNetworkDeviceListener interface. + * \note Use this method in combination of GetAmountOfListners() + * \return The pointer to the given instance of Listener. + */ + /*----------------------------------------------------------*/ + CNetworkDeviceListener *GetListener( uint32_t instance ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets the MOST instance index of the server device. + * \return The MOST instance index, starting at 0 for the first instance. + */ + /*----------------------------------------------------------*/ + uint8_t GetDeviceIndex(); + + + + /*----------------------------------------------------------*/ + /*! \brief Wait let the scripting engine wait for the given amount of time. + * \param timeInMillis - Time value in milliseconds. + */ + /*----------------------------------------------------------*/ + void ScriptPause( uint32_t timeInMillis ); + + + + /*----------------------------------------------------------*/ + /*! \brief Execute a bunch of MCM send and receive actions, as descibed in the XML file. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param pPathToScriptXml - Path to the XML file. + */ + /*----------------------------------------------------------*/ + void ExecuteMcmScript( uint16_t nodeAddress, const char *pPathToScriptXml ); + + + + /*----------------------------------------------------------*/ + /*! \brief Execute a bunch of MCM send and receive actions, as descibed in the XML zero terminated string. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param pPathToScriptXml - Path to the XML file. + */ + /*----------------------------------------------------------*/ + void ExecuteMcmScript( uint16_t nodeAddress, const char *pStringBuffer, uint32_t bufferLength ); + + + /*----------------------------------------------------------*/ + /*! \brief Sends a NetworkMaster.NOT_OK and NetworkMaster.OK, in order resolve address conflicts. + */ + /*----------------------------------------------------------*/ + void ToggleNotOk(); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the GroupAddresses of all available MOST devices in this MOST instance. + * \note The result of this request is reported in the callback method OnMostDeviceType of the CNetworkDeviceListener class. + * Use AddListener to get the result. + */ + /*----------------------------------------------------------*/ + void GetGroupAddresses( uint8_t maxNodePos ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the MOST MAC address for the given node address in this MOST instance. + * \note The result of this request is reported in the callback method OnMostMacAddress of the CNetworkDeviceListener class. + * Use AddListener to get the result. + * \param nodeAddress - The MOST node address (e.g. 0x101). + */ + /*----------------------------------------------------------*/ + void GetMacAddress( uint16_t nodeAddress ); + + + /*----------------------------------------------------------*/ + /*! \brief Starts up the MOST on the local INIC of the server device. + * \param isTimingMaster - true, if the INIC shall act as a timing master. false, if the device shall act as a timing slave. + * \param packetBandWidth - The amount of Bytes, which are reserved for asynchronous data (MEP, MHP). + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void StartMostV1( bool isTimingMaster, uint16_t packetBandWidth ); + + + + /*----------------------------------------------------------*/ + /*! \brief Performs a network shutdown sequence on the local INIC attached to the server device. + * \note This method will cause a chain reaction on the MOST network. All devices will switch of their output signal on MOST. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void MostNetworkShutdownV1(); + + + /*----------------------------------------------------------*/ + /*! \brief Enables the usage of the MLB bus for the device on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param mlbSpeed - The used MLB speed. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void OpenMlbV1( uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Sets the MOST MAC address for the device on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param macAddress1 - The 1st byte of the MAC address. + * \param macAddress2 - The 2nd byte of the MAC address. + * \param macAddress3 - The 3rd byte of the MAC address. + * \param macAddress4 - The 4th byte of the MAC address. + * \param macAddress5 - The 5th byte of the MAC address. + * \param macAddress6 - The 6th byte of the MAC address. + * \param persistent - true, if the given MAC address shall be stored persistent into the INIC flash memory. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void SetMostMacAddressV1( uint16_t nodeAddress, uint8_t macAddress1, uint8_t macAddress2, uint8_t macAddress3, + uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6, bool persistent ); + + + /*----------------------------------------------------------*/ + /*! \brief Enables the usage of the MLB bus for the device on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param tsiPortId - The instance id of the used TSI port. + * \param tsiPortMode - The used mode for this TSI port. + * \note This is an INIC API Version 1 command. Use it for OS81110 only. + */ + /*----------------------------------------------------------*/ + void OpenTsiV1( uint16_t nodeAddress, V1TsiPortInstance_t tsiPortId, V1TsiPortMode tsiPortMode ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates a TSI socket on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param portInst - The port instance id of the TSI port. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthTsi -The block width in bytes to be allocated on the MLB bus. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void CreateTsiSocketV1( uint16_t nodeAddress, V1TsiPortInstance_t portInst, + EPDataType_t epType, EPDirection_t epDir,uint16_t blockWidthTsi, uint32_t tag ); + + /*----------------------------------------------------------*/ + /*! \brief Creates a MLB socket on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param mlbChannelAddress - The MLB channel address (even value). + * \param blockWidthMlb -The block width in bytes to be allocated on the MLB bus. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void CreateMlbSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, uint16_t mlbChannelAddress, + uint16_t blockWidthMlb, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Creates a MLB socket on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param mlbChannelAddress - The MLB channel address (even value). + * \param blockWidthMlb -The block width in bytes of this singular splitted socked. + * \param splittedOffset - Offset in between the splitted socket. + * \param blockWidthCombined - The block width in bytes of all splitted sockets combined. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void CreateSplittedMlbSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint16_t mlbChannelAddress, uint16_t blockWidthMlb, uint16_t splittedOffset, uint16_t blockWidthCombined, + uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates a MOST socket on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param connectionLabel - Only in case of if epDir is "EPDIR_IN": The connection label, which this socket will be bound. + * Otherwise, the value will be ignored. + * \param blockwidthMost - The block width in bytes to be allocated on the MOST bus. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void CreateMostSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, uint16_t connectionLabel, + uint16_t blockwidthMost, uint32_t tag ); + + + + + /*----------------------------------------------------------*/ + /*! \brief on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param handle - The handle of socket (will be reported by the callback callback methods of the create socket methods). + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void DestroySocketV1( uint16_t nodeAddress, uint8_t handle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param handle - The handle of socket (will be reported by the callback callback methods of the connect socket method). + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void DisconnectSocketsV1( uint16_t nodeAddress, uint8_t handle, uint32_t tag ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Connects two different sockets, which then is called a connection, on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param inHandle - The handle of the in-socket (will be reported by the callback callback methods of the create socket methods). + * \param outHandle - * The handle of out-socket (will be reported by the callback callback methods of the create socket methods). + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 1 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void ConnectSocketsV1( uint16_t nodeAddress, uint8_t inHandle, uint8_t outHandle, uint32_t tag ); + + /*----------------------------------------------------------*/ + /*! \brief Configures an I2S port on the given node address. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void OpenStreamPortV1( uint16_t nodeAddress, V1I2SPortClkDriveMode_t portMode, + V1I2SStreamingPortMode_t streamPortMode, V1I2SStreamingDataFormat_t format, uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates an I2S socket on the given node address. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param epDir - he Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthI2S - The block width in bytes allocated on the I2S bus. + * \param pin - The physical I2S data pin of the INIC. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void CreateStreamSocketV1( uint16_t nodeAddress, EPDirection_t epDir, uint16_t blockWidthI2S, V1I2SPin_t pin, + uint32_t tag ); + + /*----------------------------------------------------------*/ + /*! \brief Creates an I2S socket on the given node address. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param epDir - he Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthI2S - The block width in bytes allocated on the I2S bus. + * \param splittedOffset - Offset in between the splitted socket. + * \param blockWidthCombined - The block width in bytes of all splitted sockets combined. + * \param pin - The physical I2S data pin of the INIC. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81110 + */ + /*----------------------------------------------------------*/ + void CreateSplittedStreamSocketV1( uint16_t nodeAddress, EPDirection_t epDir, uint16_t blockWidthI2S, + uint16_t splittedOffset, uint16_t blockWidthCombined, V1I2SPin_t pin, uint32_t tag ); + + /*----------------------------------------------------------*/ + /*! \brief Attach to a local INIC on the server device. + * \note This command has to be called before any other commands to the local INIC. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void AttachV3(); + + + + /*----------------------------------------------------------*/ + /*! \brief Destroyes any resource on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param sync - true, if the device shall be used afterwise. false, if the device is going to suspend (shutdown). + * \note In any case (regardless if sync is true or false), all ports, sockets and connects are going to be destroyed). + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void DeviceSyncV3( uint16_t nodeAddress, bool sync ); + + + + /*----------------------------------------------------------*/ + /*! \brief Performs a network startup sequence on the local INIC attached to the server device. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118-D Revision + */ + /*----------------------------------------------------------*/ + void MostNetworkStartupV3( uint16_t autoForcedNotAvailable, uint16_t packetBW ); + + + + /*----------------------------------------------------------*/ + /*! \brief Performs a network startup sequence on the local INIC attached to the server device, after MostNetworkStartupV3 with parameters had failed. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118-D Revision + * \note Call this method only if the MostNetworkStartupV3( uint16_t autoForcedNotAvailable, uint16_t packetBW ) had failed. This will be treated as retry. + */ + /*----------------------------------------------------------*/ + void MostNetworkStartupV3(); + + + /*----------------------------------------------------------*/ + /*! \brief Performs a network shutdown sequence on the local INIC attached to the server device. + * \note This method will cause a chain reaction on the MOST network. All devices will switch of their output signal on MOST. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void MostNetworkShutdownV3(); + + + + /*----------------------------------------------------------*/ + /*! \brief Closes the IPC connection to local attached INIC. + * \note This will also cleanup any used resource in the INIC. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void InicUnsychronizeV3(); + + + + + /*----------------------------------------------------------*/ + /*! \brief Sets the MOST MAC address for the device on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param macAddress1 - The 1st byte of the MAC address. + * \param macAddress2 - The 2nd byte of the MAC address. + * \param macAddress3 - The 3rd byte of the MAC address. + * \param macAddress4 - The 4th byte of the MAC address. + * \param macAddress5 - The 5th byte of the MAC address. + * \param macAddress6 - The 6th byte of the MAC address. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118-D Rev + */ + /*----------------------------------------------------------*/ + void SetMostMacAddressV3( uint16_t nodeAddress, uint8_t macAddress1, uint8_t macAddress2, uint8_t macAddress3, + uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6 ); + + + /*----------------------------------------------------------*/ + /*! \brief Enables the usage of the MLB bus for the device on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param mlbSpeed - The speed of the MLB bus. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void OpenMlbV3( uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates an USB socket on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param endPointAddress - The USB endpoint address. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \param packetsPerFrame - The amount of packets stored in a single USB frame (512 byte). + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateUsbSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, uint8_t endPointAddress, + uint16_t packetsPerFrame, uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates an splitted USB socket on the given node address to use with Combiner or Splitter. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param endPointAddress - The USB endpoint address. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \param packetsPerFrame - The amount of packets stored in a single USB frame (512 byte). + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateSplittedUsbSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, + uint8_t endPointAddress, uint16_t packetsPerFrame, uint16_t bytesPerPacket, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Creates a MLB socket on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param mlbChannelAddress - The MLB channel address (even value). + * \param blockwidthMlb - The block width in bytes to be allocated on the MLB bus. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateMlbSocketV3( uint16_t nodeAddress, uint16_t mlbPortHandle, EPDataType_t epType, EPDirection_t epDir, + uint16_t mlbChannelAddress, uint16_t blockwidthMlb, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Creates a splitted MLB socket on the given node address to use with Combiner or Splitter. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param mlbChannelAddress - The MLB channel address (even value). + * \param blockwidthMlb - The block width in bytes to be allocated on the MLB bus. + * \param bytesPerPacket - The total number of data bytes to be transfered each MOST frame. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateSplittedMlbSocketV3( uint16_t nodeAddress, uint16_t mlbPortHandle, EPDataType_t epType, + EPDirection_t epDir, uint16_t mlbChannelAddress, uint16_t blockwidthMlb, uint16_t bytesPerPacket, + uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates a MOST socket on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this MOST channel. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param connectionLabel - Only in case of if epDir is "EPDIR_IN": The connection label, which this socket will be bound. + * Otherwise, the value will be ignored. + * \param blockwidthMost - The block width in bytes to be allocated on the MOST bus. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateMostSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir, uint16_t connectionLabel, + uint16_t blockwidthMost, uint32_t tag ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Connects two different sockets, which then is called a connection, on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param epType - The data type, which then will be transmitted on this connection. + * \param inHandle - The handle of the in-socket (will be reported by the callback callback methods of the create socket methods). + * \param outHandle - * The handle of out-socket (will be reported by the callback callback methods of the create socket methods). + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void ConnectSocketsV3( uint16_t nodeAddress, EPDataType_t epType, uint16_t inHandle, uint16_t outHandle, + uint16_t offset, uint32_t tag ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Destroyes up to three resources (Ports or connections) on the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \param amountOfHandles - The amount of resources to be destroyed. (value must be between 1 to 3). + * \param handle1 - The first resource to be destroyed. + * \param handle2 - The second resource to be destroyed. + * \param handle3 - The third resource to be destroyed. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void ResourceDestroyV3( uint16_t nodeAddress, uint8_t amountOfHandles, const uint16_t *pHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Configures an I2S port on the given node address. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param option - The I2S port option. + * \param mode - The I2S clock mode. + * \param delay - The I2S delay mode. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void ConfigureStreamPortV3( uint16_t nodeAddress, uint8_t portInstance, V3I2SPortOption_t option, + V3I2SClockMode_t mode, V3I2SDelayMode_t delay, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Creates an I2S port on the given node address. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param clock - The I2S port speed in multiple of MOST base clock. + * \param align - The I2S data format alignment. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateStreamPortV3( uint16_t nodeAddress, uint8_t portInstance, V3I2SPortSpeed_t clock, + V3I2SAlignment_t align, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Creates an I2S socket on the given node address. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param epDir - he Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthI2S - The block width in bytes allocated on the I2S bus. + * \param pin - The physical I2S data pin of the INIC. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateStreamSocketV3( uint16_t nodeAddress, uint8_t portInstance, EPDirection_t epDir, uint16_t blockWidthI2S, + V3I2SPin_t pin, uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates an splitted I2S socket on the given node address to use with Combiner or Splitter. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param epDir - he Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthI2S - The block width in bytes allocated on the I2S bus. + * \param pin - The physical I2S data pin of the INIC. + * \param bytesPerPacket - The total number of data bytes to be transfered each MOST frame. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void CreateSplittedStreamSocketV3( uint16_t nodeAddress, uint8_t portInstance, EPDirection_t epDir, + uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t bytesPerPacket, uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates an I2S socket on the given node address. + * \param nodeAddr -The device with this MOST node address raised this event. + * \param tag - Any 32 bit value. This value will be unmodified passed back in the corresponding callback method. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void GetRingBreakResultV3( uint16_t nodeAddress, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Sends the given Control Message out to MOST ring. + * \param targetAddr - The MOST target address + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + bool SendMostControlMessage( uint8_t devInst, uint32_t targetAddr, uint32_t nFBlock, uint32_t nInst, + uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ); + + /*----------------------------------------------------------*/ + /*! \brief Gets the device information for the given node address. + * \param nodeAddress - The MOST node address (e.g. 0x101). + * \note The corresponding callback to this request is OnDeviceVersion. + * \note This is an INIC API Version 3 command. Use it for e.g. OS81118 + */ + /*----------------------------------------------------------*/ + void GetDeviceVersion( uint16_t nodeAddress ); + + /* Callback from underlying layers */ + virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r); +}; + +#endif //_NETWORKDEVICE_H_ diff --git a/Src/Network/NetworkDeviceListener.cpp b/Src/Network/NetworkDeviceListener.cpp new file mode 100644 index 0000000..be62d33 --- /dev/null +++ b/Src/Network/NetworkDeviceListener.cpp @@ -0,0 +1,306 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include "Console.h" +#include "NetworkDeviceListener.h" + +void CNetworkDeviceListener::OnSync( void *source, bool isSynced ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnSync\n" ); +} + +void CNetworkDeviceListener::OnNetworkState( void *source, bool mpValChanged, bool systemNotOk, + bool mostAvailable, uint8_t availableSubState, uint8_t availableTransition, uint16_t nodeAddress, uint8_t nodePos, + uint8_t maxPos, uint16_t packetBW ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnNetworkState\n" ); +} + +void CNetworkDeviceListener::OnNetworkStartupV3( void *source, bool success ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnNetworkStartupV3, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnNetworkShutdownV3( void *source, bool success ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnNetworkShutdownV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnMostMacAddress( void *source, bool success, uint16_t nodeAddress, uint8_t macAddress1, + uint8_t macAddress2, uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6 ) +{ + if( success ) + { + ConsolePrintf( PRIO_LOW, + "BASE->OnMostMacAddress:nodeAddress:0x%X, MAC-Address:%02X-%02X-%02X-%02X-%02X-%02X)\n", nodeAddress, + macAddress1, macAddress2, macAddress3, macAddress4, macAddress5, macAddress6 ); + } + else + { + ConsolePrintf( PRIO_ERROR, + RED"BASE->OnMostMacAddress reports failure for nodeAddress:0x%X"RESETCOLOR"\n", nodeAddress ); + } +} + +void CNetworkDeviceListener::OnMostDeviceType( void *source, bool success, uint16_t nodeAddress, uint16_t deviceType ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnMostDeviceType, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnInitCompleteV1( void *source ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnInitCompleteV1\n" ); +} + +void CNetworkDeviceListener::OnNetOnV1( void *source, bool isMaster ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnNetOnV1, mode=%s\n", ( isMaster ? "master" : "slave" ) ); +} + +void CNetworkDeviceListener::OnShutDownV1( void *source ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnShutDownV1\n" ); +} + +void CNetworkDeviceListener::OnNprV1( void *source, uint8_t npr ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnNprV1, NPR=%d\n", npr ); +} + +void CNetworkDeviceListener::OnUnlockV1( void *source ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnUnlockV1\n" ); +} + +void CNetworkDeviceListener::OnMprV1( void *source, uint8_t oldMpr, uint8_t newMpr ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnMprV1, old MPR=%d, new MPR:%d\n", oldMpr, newMpr ); +} + +void CNetworkDeviceListener::OnOpenTsiV1( void *source, bool success, uint16_t nodeAddress ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnOpenTsiV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateTsiSocketV1( void *source, bool success, uint16_t nodeAddr, V1TsiPortInstance_t tsiPortInst, + EPDataType_t epType, EPDirection_t epDir, uint16_t blockWidthTsi, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateTsiSocketV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnOpenMlbV1( void *source, bool success, uint16_t nodeAddress ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnOpenMlbV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateMlbSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateMlbSocketV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateMostSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateMostSocketV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnConnectSocketsV1( void *source, bool success, uint16_t nodeAddr, + uint16_t inSocketHandle, uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnConnectSocketsV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnDestroySocketV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, + uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnDestroySocketV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnDisconnectSocketsV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, + uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnDisconnectSocketsV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnOpenI2SPortV1( void *source, bool success, uint16_t nodeAddress, + V1I2SPortClkDriveMode_t portMode, V1I2SStreamingDataFormat_t format, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnOpenI2SPortV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateI2SSocketV1( void *source, bool success, uint16_t nodeAddr, EPDirection_t epDir, + uint16_t blockWidthI2S, V1I2SPin_t pin, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateI2SSocketV1, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnOpenMlbV3( void *source, bool success, uint16_t nodeAddress, uint16_t mlbPortHandle ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnOpenMlbV2, success=%s, Handle:%X\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ), mlbPortHandle ); +} + +void CNetworkDeviceListener::OnCreateUsbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint8_t endPointAddress, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateUsbSocketV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateSplittedUsbSocketV3( void *source, bool success, uint16_t nodeAddr, + EPDataType_t epType, EPDirection_t epDir, uint8_t endPointAddress, uint16_t usbHandle, uint16_t splitterHandle, + uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateSplittedUsbSocketV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateMlbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateMlbSocketV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateSplittedMlbSocketV3( void *source, bool success, uint16_t nodeAddr, + EPDataType_t epType, EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, + uint16_t mlbSocketHandle, uint16_t splittertHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateSplittedMlbSocketV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateMostSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateMostSocketV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnConnectSocketsV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + uint16_t inSocketHandle, uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnConnectSocketsV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + + +void CNetworkDeviceListener::OnResourceDestroyV3( void *source, bool success, uint16_t nodeAddress, + uint8_t amountOfHandles, const uint16_t *pHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnResourceDestroyV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnConfigureI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortOption_t option, V3I2SClockMode_t mode, V3I2SDelayMode_t delay, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnConfigureI2SPortV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortSpeed_t clock, V3I2SAlignment_t align, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateI2SPortV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateI2SSocketV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t socketHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateI2SSocketV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnCreateSplittedI2SSocketV3( void *source, bool success, uint16_t nodeAddr, + uint8_t portInstance, EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t i2sSocketHandle, + uint16_t splitterHandle, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->CNetworkDeviceListener::OnCreateSplittedI2SSocketV2, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} + +void CNetworkDeviceListener::OnRbdResultV3( void *source, uint16_t nodeAddress, uint8_t result, uint8_t position, + uint8_t status, uint16_t id ) +{ + ConsolePrintfStart( PRIO_HIGH, YELLOW"BASE->OnRbdResultV3: Diagnosis Result from node=0x%X, result=0x%X (", + nodeAddress, result ); + switch( result ) + { + case 0: + ConsolePrintfContinue( "NoError" ); + break; + case 1: + ConsolePrintfContinue( "PosDetected" ); + break; + case 2: + ConsolePrintfContinue( "DiagFailed" ); + break; + case 3: + ConsolePrintfContinue( "Pos0WeakSig" ); + break; + case 0xFF: + ConsolePrintfContinue( "No result" ); + break; + default: + ConsolePrintfContinue( "UNKNOWN" ); + break; + } + ConsolePrintfExit( ") Position: %d"RESETCOLOR"\n", position ); +} + +void CNetworkDeviceListener::OnControlChannelReadEnd( void *source ) +{ + ConsolePrintf( PRIO_LOW, "BASE->OnControlChannelReadEnd\n" ); +} + +void CNetworkDeviceListener::OnMostControlMessage( void *source, uint32_t sourceAddr, uint32_t targetAddr, + uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) +{ + ConsolePrintf( PRIO_LOW, "Base->OnMostControlMessage\n" ); +} + +void CNetworkDeviceListener::OnDeviceVersion( void *source, bool success, uint32_t sourceAddr, uint32_t productId, + uint32_t fwVersion, uint32_t buildVersion, uint8_t hwVersion, uint16_t diagnosisId, uint32_t tag ) +{ + ConsolePrintf( PRIO_LOW, "BASE->OnDeviceVersion::OnDeviceVersion, success=%s\n", + ( success ? GREEN"yes"RESETCOLOR : RED"no"RESETCOLOR ) ); +} diff --git a/Src/Network/NetworkDeviceListener.h b/Src/Network/NetworkDeviceListener.h new file mode 100644 index 0000000..719bc5f --- /dev/null +++ b/Src/Network/NetworkDeviceListener.h @@ -0,0 +1,568 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CNetworkDeviceListener class. + */ +/*----------------------------------------------------------*/ +#ifndef _NETWORKDEVICELISTENER_H_ +#define _NETWORKDEVICELISTENER_H_ + +#include +#include "Types.h" + +/*----------------------------------------------------------*/ +/*! \brief This class defines callbacks methods which will be called by CNetworkDevice to any + * registered listener. Also this class implements empty implementation of each method. + * So the integrator has only to override methods, he is interessted in. + */ +/*----------------------------------------------------------*/ +class CNetworkDeviceListener +{ +public: + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a sync event has been detected on the server network device. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnSync( void *source, bool isSynced ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a Network State event has been detected on the server network device. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param mpValChanged - true, there was a change in the Maximum Position Information. false, no change. + * \param systemNotOk - true, system state changes to NotOk. false, no change. + * \param mostAvailable - true, the MOST network is available. false, The MOST network is not available for sending control/packet data. + * \param availableSubState - 0=Regular, 2=Diagnosis, 4=RxOnBlocked, 6=ForcedNA, 0x10=Unstable, 0x11=Stable + * \param availableTransition - 0=Command, 1=RxActivity, 2=DiagnosisReady, 0x10=Normal, 0x11=SuddenSignalOff, 0x12=CriticalUnlock + * 0x13=ErrorSystem, 0xFF=NoTransition + * \param nodeAddress - The assigned MOST node address. + * \param nodePos - The position in the MOST ring. + * \param maxPos - The maximum amount of devices in the MOST ring. + * \param packetBW - The amount of bytes reserved for packet channel (MEP/MHP) + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnNetworkState( void *source, bool mpValChanged, bool systemNotOk, + bool mostAvailable, uint8_t availableSubState, uint8_t availableTransition, uint16_t nodeAddress, + uint8_t nodePos, uint8_t maxPos, uint16_t packetBW ); + + + + virtual void OnInitCompleteV1( void *source ); + virtual void OnUnlockV1( void *source ); + virtual void OnNetOnV1( void *source, bool isMaster ); + virtual void OnShutDownV1( void *source ); + virtual void OnNprV1( void *source, uint8_t npr ); + virtual void OnMprV1( void *source, uint8_t oldMpr, uint8_t newMpr ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever the MOST had been tried to start. + * \param success - true, when the operation was successful. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 D-Rev + */ + /*----------------------------------------------------------*/ + virtual void OnNetworkStartupV3( void *source, bool success ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever the MOST had been tried to shutdown. + * \param success - true, when the operation was successful. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 D-Rev + */ + /*----------------------------------------------------------*/ + virtual void OnNetworkShutdownV3( void *source, bool success ); + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MAC address has been changed in the network. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddress - The device with this MOST node address raised this event. + * \param macAddress1 - The 1st byte of the MAC address. + * \param macAddress2 - The 2nd byte of the MAC address. + * \param macAddress3 - The 3rd byte of the MAC address. + * \param macAddress4 - The 4th byte of the MAC address. + * \param macAddress5 - The 5th byte of the MAC address. + * \param macAddress6 - The 6th byte of the MAC address. + * \note This is event will be raised by all INIC APIS (V1 & V2). + */ + /*----------------------------------------------------------*/ + virtual void OnMostMacAddress( void *source, bool success, uint16_t nodeAddress, uint8_t macAddress1, + uint8_t macAddress2, uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6 ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a Device type has been detected. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param nodeAddress - The device with this MOST node address raised this event. + * \param deviceType - Identifier which sorts the MOST device to a specific function group. + * \note In this implementation the Device Type is simply derived from the Group Address set in the INIC config string! + * \note This is event will be raised by all INIC APIS (V1 & V2). + */ + /*----------------------------------------------------------*/ + virtual void OnMostDeviceType( void *source, bool success, uint16_t nodeAddress, uint16_t deviceType ); + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever the TSI bus was opened. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddress - The device with this MOST node address raised this event. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnOpenTsiV1( void *source, bool success, uint16_t nodeAddress ); + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a TSI socket was created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param tsiPortInst - The use TSI port. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthMlb - The block width in bytes allocated on the MLB bus. + * \param mlbChannelAddress - The MLB channel address (even value). + * \param socketHandle - Unique handle for this socket. Use this for creating connections or closing this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateTsiSocketV1( void *source, bool success, uint16_t nodeAddr, V1TsiPortInstance_t tsiPortInst, + EPDataType_t epType, EPDirection_t epDir, uint16_t blockWidthTsi, uint16_t socketHandle, uint32_t tag ); + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever the MLB bus was opened. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddress - The device with this MOST node address raised this event. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnOpenMlbV1( void *source, bool success, uint16_t nodeAddress ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MLB socket was created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthMlb - The block width in bytes allocated on the MLB bus. + * \param mlbChannelAddress - The MLB channel address (even value). + * \param socketHandle - Unique handle for this socket. Use this for creating connections or closing this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateMlbSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MOST socket was created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockwidthMost - The block width in bytes allocated on the MOST bus. + * \param connectionLabel - The MOST connection label, which this socket will be bound. + * \param socketHandle - Unique handle for this socket. Use this for creating connections or closing this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateMostSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever two MOST sockets where connected. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param inSocketHandle - The user given IN-handle. + * \param outSocketHandle - The user given OUT-handle. + * \param connectionHandle - Unique handle for this connection. Use this for disconnecting this connection again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnConnectSocketsV1( void *source, bool success, uint16_t nodeAddr, uint16_t inSocketHandle, + uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MOST socket was deestroyed. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddress - The device with this MOST node address raised this event. + * \param handle - The unique handle for this socket. After this call, the handle is invalid. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnDestroySocketV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MOST connection was disconnected. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddress - The device with this MOST node address raised this event. + * \param handle - The unique handle for this connection. After this call, the handle is invalid. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 1 event. It is raised from e.g. OS81110 + */ + /*----------------------------------------------------------*/ + virtual void OnDisconnectSocketsV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, uint32_t tag ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a I2S port has been configured. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnOpenI2SPortV1( void *source, bool success, uint16_t nodeAddress, V1I2SPortClkDriveMode_t portMode, + V1I2SStreamingDataFormat_t format, uint32_t tag ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a I2S socket has been created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param epDir - he Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthI2S - The block width in bytes allocated on the I2S bus. + * \param pin - The physical I2S data pin of the INIC. + * \param socketHandle - Unique handle for this socket. Use this for creating connections or destroying this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateI2SSocketV1( void *source, bool success, uint16_t nodeAddr, EPDirection_t epDir, + uint16_t blockWidthI2S, V1I2SPin_t pin, uint16_t socketHandle, uint32_t tag ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever the MLB bus was opened. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddress - The device with this MOST node address raised this event. + * \param mlbPortHandle - The device with this MOST node address raised this event. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnOpenMlbV3( void *source, bool success, uint16_t nodeAddress, uint16_t mlbPortHandle ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a USB socket was created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param endPointAddress - The USB endpoint address, which is now usable. + * \param socketHandle - Unique handle for this socket. Use this for creating connections or closing this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateUsbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint8_t endPointAddress, uint16_t socketHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a USB socket was created, which uses a Splitter or a Combiner. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param endPointAddress - The USB endpoint address, which is now usable. + * \param usbHandle - Unique handle for this USB socket. Do not use this for creating connections! + * \param splitterHandle - Unique handle for this Splitter socket. Use this for creating connections or closing this socket again! + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateSplittedUsbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint8_t endPointAddress, uint16_t usbHandle, uint16_t splitterHandle, uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MLB socket was created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthMlb - The block width in bytes allocated on the MLB bus. + * \param mlbChannelAddress - The MLB channel address (even value). + * \param socketHandle - Unique handle for this socket. Use this for creating connections or closing this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateMlbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MLB socket was created, which uses a Splitter or a Combiner. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthMlb - The block width in bytes allocated on the MLB bus. + * \param mlbChannelAddress - The MLB channel address (even value). + * \param mlbSocketHandle - Unique handle for this socket. Use this for creating connections or closing this socket again. + * \param splitterHandle - Unique handle for this Splitter socket. Use this for creating connections or closing this socket again! + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateSplittedMlbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t mlbSocketHandle, + uint16_t splitterHandle, uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a MOST socket was created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this endpoint. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockwidthMost - The block width in bytes allocated on the MOST bus. + * \param connectionLabel - The MOST connection label, which this socket will be bound. + * \param socketHandle - Unique handle for this socket. Use this for creating connections or destroying this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateMostSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever two MOST sockets where connected. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param epType - The data type, which then will be transmitted on this connection. + * \param inSocketHandle - The user given IN-handle. + * \param outSocketHandle - The user given OUT-handle. + * \param connectionHandle - Unique handle for this connection. Use this for destroying this connection again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnConnectSocketsV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + uint16_t inSocketHandle, uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever MOST resources where destroyed. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param amountOfHandles - The amount of resources to be destroyed. (value will be between 1 to 3). + * \param pHandle - Pointer to the resource list + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnResourceDestroyV3( void *source, bool success, uint16_t nodeAddr, uint8_t amountOfHandles, + const uint16_t *pHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a I2S port has been configured. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param option - The I2S port option. + * \param mode - The I2S clock mode. + * \param delay - The I2S delay mode. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnConfigureI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortOption_t option, V3I2SClockMode_t mode, V3I2SDelayMode_t delay, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a I2S port has been created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param clock - The I2S port speed in multiple of MOST base clock. + * \param align - The I2S data format alignment. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortSpeed_t clock, V3I2SAlignment_t align, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a I2S socket has been created. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param epDir - he Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthI2S - The block width in bytes allocated on the I2S bus. + * \param pin - The physical I2S data pin of the INIC. + * \param socketHandle - Unique handle for this socket. Use this for creating connections or destroying this socket again. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateI2SSocketV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t socketHandle, uint32_t tag ); + + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when ever a I2S socket has been created, which uses a Splitter or a Combiner. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param nodeAddr - The device with this MOST node address raised this event. + * \param portInstance - The I2S port instance. 0 = Port A, 1 = Port B. + * \param epDir - The Direction of the stream. (EPDIR_IN == Data from the INIC to the EHC, EPDIR_OUT == Data from the EHC to the INIC) + * \param blockWidthI2S - The block width in bytes allocated on the I2S bus. + * \param pin - The physical I2S data pin of the INIC. + * \param i2sSocketHandle - Unique handle for this socket. Use this for creating connections or destroying this socket again. + * \param splitterHandle - Unique handle for this Splitter socket. Use this for creating connections or closing this socket again! + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnCreateSplittedI2SSocketV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t i2sSocketHandle, uint16_t splitterHandle, + uint32_t tag ); + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when a Ring Break Diagnosis Result was received. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param nodeAddr - The device with this MOST node address raised this event. + * \praram result - The ring break diagnosis result + * \param position - Relative position to the ring break. + * \param status - Gives information about the activity state. + * \param id - The RBD identifier as configured in config string. + * \note This is an INIC API Version 3 event. It is raised from e.g. OS81118 + */ + /*----------------------------------------------------------*/ + virtual void OnRbdResultV3( void *source, uint16_t nodeAddress, uint8_t result, uint8_t position, + uint8_t status, uint16_t id ); + + + /*----------------------------------------------------------*/ + /*! \brief Callback, when the MOST control channel receive thread has ended. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + */ + /*----------------------------------------------------------*/ + virtual void OnControlChannelReadEnd( void *source ); + + + /*----------------------------------------------------------*/ + /*! \brief This callback method is called whenever a MOST control message was sent or received. + * \note In order to be informed about this event, derive this class, implement this method and register the class with AddListener method of CNetwork class. + * \note The user may interpret the data and sent a corresponding MOST control message via CNetwork::SendMostControlMessage. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param sourceAddr - The MOST source address (may be not accurate) + * \param targetAddr - The MOST target address + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + * + */ + /*----------------------------------------------------------*/ + virtual void OnMostControlMessage( void *source, uint32_t sourceAddr, uint32_t targetAddr, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ); + + /*----------------------------------------------------------*/ + /*! \brief This callback method is as result of the DeviceVersion query. + * \param source - Cast this pointer to (CNetworkDevice*), it points to server network device, which received this event. + * \param success - true, when the operation was successful. + * \param sourceAddr - The device with this MOST node address raised this event. + * \param productId - Unique identifier that represents the product name. + * \param fwVersion - INIC firmware version: Byte[3] Major, Byte[2] Minor, Byte[1] Release. + * \param buildVersion - Build version of the firmware. + * \param hwVersion - Chip revision number. + * \param diagnosisId - Unique diagnosis identifier of the device. + * \param tag - Any 32 bit value. This value was passed to the corresponding function call of the CNetworkDevice class. + */ + /*----------------------------------------------------------*/ + virtual void OnDeviceVersion( void *source, bool success, uint32_t sourceAddr, + uint32_t productId, uint32_t fwVersion, uint32_t buildVersion, uint8_t hwVersion, uint16_t diagnosisId, uint32_t tag ); + +}; + +#endif //_NETWORKDEVICELISTENER_H_ diff --git a/Src/Network/Network_CB.cpp b/Src/Network/Network_CB.cpp new file mode 100644 index 0000000..3a91cc2 --- /dev/null +++ b/Src/Network/Network_CB.cpp @@ -0,0 +1,1823 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include "SafeVector.h" +#include "Network.h" +#include "NetworkDevice.h" +#include "DriverConfiguration.h" +#include "MacAddr.h" +#include "Board.h" +#include "Console.h" +#include "NodeDatabase.h" + +#define PRINT_ALL_INFOS() + +void CNetwork::OnSync( void *source, bool isSynced ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + ConsolePrintf( PRIO_MEDIUM, "CNetwork::OnSync(deviceInstance:%d, isSynced:%d\n", devInstance, isSynced ); + + if( isSynced ) + { + device->SetNetstate( NetworkState_Unknown ); + device->AttachV3(); + } +} + +void CNetwork::OnNetworkState( void *source, bool mpValChanged, bool systemNotOk, bool mostAvailable, + uint8_t availableSubState, uint8_t availableTransition, uint16_t nodeAddress, uint8_t nodePos, uint8_t maxPos, + uint16_t packetBW ) +{ + if( NULL == source || !allowNetworkRun ) + return; + if (0x100 != nodeAddress && 0x1 != nodeAddress && 0xFFFF != nodeAddress) + return; + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + ConsolePrintf( PRIO_HIGH, + YELLOW"CNetwork::OnNetworkState(inst:%d, mpChanged:%d, " \ + "notOk:%d, available:%d, sub:0x%X, trans:0x%X, address:0x%X, pos:%d, " \ + "mpr:%d, bw:%d"RESETCOLOR"\n", devInstance, mpValChanged, + systemNotOk, mostAvailable, availableSubState, availableTransition, + nodeAddress, nodePos, maxPos, packetBW ); + + NetworkState_t curState = device->GetNetState(); + if (NetworkState_ShutdownInProgress == curState) + { + ConsolePrintf( PRIO_HIGH, "Ignoring OnNetworkState, because shutdown is still in progress\n"); + return; + } + + for( uint32_t i = 0; i < allListeners.Size(); i++ ) + { + allListeners[i]->OnNetworkState(devInstance, mostAvailable, maxPos, packetBW); + } + + if( mostAvailable ) + { + if( NetworkState_Available == curState ) + { + if( 0x10 == availableSubState ) + { + ConsolePrintf( PRIO_ERROR, RED"Unlock detected, shutting down MOST."RESETCOLOR"\n" ); + device->SetNetstate( NetworkState_ShutdownInProgress ); + device->ClearAllPendingActions(); + device->MostNetworkShutdownV3(); + return; + } + else if( mpValChanged && 0xFF != maxPos ) + { + ConsolePrintf( PRIO_ERROR, RED"MPR change detected, shutting down MOST."RESETCOLOR"\n" ); + device->SetNetstate( NetworkState_ShutdownInProgress ); + device->ClearAllPendingActions(); + device->MostNetworkShutdownV3(); + return; + } + else if( 0x11 == availableSubState ) + { + ConsolePrintf( PRIO_MEDIUM, YELLOW"CNetwork::OnNetworkState was called with the same mostAvailable (%d) value as before, ignoring.."RESETCOLOR"\n", + mostAvailable ); + return; + } + } + } + else + { + if( 0x02 == availableSubState ) + { + ConsolePrintf( PRIO_HIGH, YELLOW"INIC enters Ring Break Diagnosis mode!"RESETCOLOR"\n" ); + device->GetRingBreakResultV3( 1, 0 ); + } + if( NetworkState_Unavailable == curState ) + { + ConsolePrintf( PRIO_MEDIUM, YELLOW"CNetwork::OnNetworkState was called with the same mostAvailable (%d) value as before, ignoring.."RESETCOLOR"\n", + mostAvailable ); + return; + } + } + + bool isTimingMaster = device->IsTimingMaster(); + uint32_t asyncBandwidth = device->GetAsyncBandwidth(); + + if( !mostAvailable ) + { + device->SetNetstate( NetworkState_Unavailable ); + device->ClearAllPendingActions(); + DestroyAllResources( device, 0x1 ); + //Clear all existing connection information for this MOST instance + while( true ) + { + bool deleted = false; + for( uint16_t i = 0; i < CNodeEntry::GetAmountOfNodeEntries(); i++ ) + { + CNodeEntry *entry = CNodeEntry::GetNodeEntry( i ); + if( NULL == entry ) + continue; + if( devInstance == entry->deviceInstance ) + { + for( uint16_t j = 0; j < entry->GetAmountOfConnections(); j++ ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByIndex( j ); + RaiseUnavailable( connection ); + } + CNodeEntry::DeleteNodeEntry( i ); + deleted = true; + break; + } + } + if( !deleted ) + break; + } + if( 2 == device->GetDeviceApi() ) + { + ConsolePrintf( PRIO_ERROR, RED"Error, device API V2 is not supported by "\ + "this version of NetworkManager!"RESETCOLOR ); + } + else if( 3 == device->GetDeviceApi() && isTimingMaster ) + { + ConsolePrintf( PRIO_MEDIUM, "CNetwork::OnNetworkState calling MostNetworkStartupV3\n" ); + device->MostNetworkStartupV3( 0xFFFF, asyncBandwidth ); + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Error, device API '%d' is not supported by "\ + "this version of NetworkManager!"RESETCOLOR, device->GetDeviceApi() ); + } + } + else if (asyncBandwidth != packetBW) + { + ConsolePrintf( PRIO_ERROR, RED"Packet Bandwidth does not match."\ + " Expected:%d Is:%d, forcing MOST shutdown"RESETCOLOR"\n", asyncBandwidth, packetBW ); + device->SetNetstate( NetworkState_ShutdownInProgress ); + device->MostNetworkShutdownV3(); + } + else if( !systemNotOk + && ( maxPos <= 64 ) + && ( packetBW <= 372 ) + && ( 0x11 == availableSubState ) ) + { + //Everything is ok. Start query network devices + device->SetNetstate( NetworkState_Available ); + if( isTimingMaster ) + { + device->ToggleNotOk(); + } + device->GetGroupAddresses( maxPos ); + } +} + +void CNetwork::OnNetworkStartupV3( void *source, bool success ) +{ + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + if( success ) + { + ConsolePrintf( PRIO_MEDIUM, GREEN"CNetwork::OnNetworkStartupV3 success!"RESETCOLOR"\n" ); + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnNetworkStartupV3 failed, retrying.."RESETCOLOR"\n" ); + device->ScriptPause(500); + device->MostNetworkStartupV3(); + } +} + +void CNetwork::OnNetworkShutdownV3( void *source, bool success ) +{ + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + if( success ) + { + ConsolePrintf( PRIO_HIGH, GREEN"CNetwork::OnNetworkShutdownV2 success"RESETCOLOR"\n" ); + } + else + { + ConsolePrintf( PRIO_ERROR, RED"CNetwork::OnNetworkShutdownV2 failed"RESETCOLOR"\n" ); + } + device->SetNetstate( NetworkState_Unknown ); + device->InicUnsychronizeV3(); +} + +void CNetwork::OnMostDeviceType( void *source, bool success, uint16_t nodeAddress, uint16_t deviceType ) +{ + if( NULL == source ) + return; + if( nodeAddress >= 0x400 && nodeAddress <= 0x4FF ) + nodeAddress = nodeAddress - 0x300; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnMostDeviceType reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddress ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, "CNetwork::OnMostDeviceType(deviceInstance:%d, nodeAddress:0x%X, deviceType: 0x%X)\n", + devInstance, nodeAddress, deviceType ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddress ); + sem_wait( &vodXmlMutex ); + if( ( NULL == entry ) || ( NULL == vodXml ) ) + { + sem_post( &vodXmlMutex ); + return; + } + entry->deviceType = deviceType; + CSafeVector allChannels; + uint32_t apiVer = vodXml->GetDeviceApi( deviceType ); + + if( 0xFFFFFFFF == apiVer ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnMostDeviceType reports unknown API version '%d' for"\ + " device 0x%X with node address 0x%X, please check GroupAddress in ConfigString or edit configuration file"\ + RESETCOLOR"\n", apiVer, deviceType, nodeAddress ); + sem_post( &vodXmlMutex ); + RaiseUknownConnection( devInstance, nodeAddress, EP_Unknown ); + return; + } + else if( 2 == apiVer || apiVer > 3 ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnMostDeviceType reports API version '%d' for"\ + " device 0x%X with node address 0x%X, this is not longer supported by this version of NetworkManager"\ + RESETCOLOR"\n", apiVer, deviceType, nodeAddress ); + sem_post( &vodXmlMutex ); + RaiseUknownConnection( devInstance, nodeAddress, EP_Unknown ); + return; + } + + //Set MAC address regardless if set permanently or not. This MAC address contains MOST specific informations. + //This is necessary, because the bytes in the MAC address are used to identify the streams in the multiplexer. + CMacAddr *macAddress = SetMacAddress( device, devInstance, nodeAddress, apiVer ); + if( NULL != macAddress ) + { + if( NULL != entry->macAddress ) + { + CMacAddr *temp = entry->macAddress; + entry->macAddress = macAddress; + delete temp; + } + else + { + entry->macAddress = macAddress; + } + for( uint16_t i = 0; i < entry->GetAmountOfConnections(); i++ ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByIndex( i ); + if( NULL != connection ) + { + RaiseAvailable( connection ); + } + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Setting MAC address failed!"RESETCOLOR"\n" ); + RaiseUknownConnection( devInstance, nodeAddress, EP_Unknown ); + } + + if( vodXml->GetChannelConfigurations( deviceType, allChannels ) ) + { + if( 0 == allChannels.Size() ) + { + RaiseUknownConnection( devInstance, nodeAddress, EP_Unused ); + } + else + for( uint32_t i = 0; i < allChannels.Size(); i++ ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( allChannels[i]->channelId, true ); + if( NULL == connection->channelConfig ) + { + connection->channelConfig = allChannels[i]; + } + + if( 1 == apiVer ) + { + /*------------------------------------*/ + /*- INIC API VERSION 1 */ + /*------------------------------------*/ + + if( ( PORT_MLB == connection->channelConfig->inSocket.port ) + || ( PORT_MLB == connection->channelConfig->outSocket.port ) ) + { + EPDirection_t direction = EPDIR_Unknown; + CSocketConfiguration *conf = NULL; + if( PORT_MLB == connection->channelConfig->inSocket.port ) + { + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + direction = EPDIR_IN; + conf = &connection->channelConfig->inSocket; + } + else + { + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + direction = EPDIR_OUT; + conf = &connection->channelConfig->outSocket; + } + if( !entry->isMlbPortOpened ) + { + entry->isMlbPortOpened = true; + device->OpenMlbV1( nodeAddress, vodXml->GetDeviceMlbPortSpeed( deviceType ) ); + } + if( 0xFFFFFFFF == conf->splittedOffset ) + { + //Normal socket is used + device->CreateMlbSocketV1( nodeAddress, conf->type, direction, conf->address, + conf->blockWidth, allChannels[i]->channelId ); + } + else + { + //Enhanced (Splitted) Sockets are used + device->CreateSplittedMlbSocketV1( nodeAddress, conf->type, direction, conf->address, + conf->blockWidth, conf->splittedOffset, conf->subbufferSize, + allChannels[i]->channelId ); + } + } + if( ( PORT_TSI == connection->channelConfig->inSocket.port ) + || ( PORT_TSI == connection->channelConfig->outSocket.port ) ) + { + EPDirection_t direction = EPDIR_Unknown; + CSocketConfiguration *conf = NULL; + if( PORT_TSI == connection->channelConfig->inSocket.port ) + { + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + direction = EPDIR_IN; + conf = &connection->channelConfig->inSocket; + } + else + { + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + direction = EPDIR_OUT; + conf = &connection->channelConfig->outSocket; + } + if( !entry->isTsiPortOpened ) + { + entry->isTsiPortOpened = true; + device->OpenTsiV1( nodeAddress, conf->tsiConfig.tsiPortInstance, + conf->tsiConfig.tsiPortMode ); + } + device->CreateTsiSocketV1( nodeAddress, conf->tsiConfig.tsiPortInstance, conf->type, direction, + conf->blockWidth, allChannels[i]->channelId ); + } + if( ( PORT_I2S == connection->channelConfig->inSocket.port ) + || ( PORT_I2S == connection->channelConfig->outSocket.port ) ) + { + EPDirection_t direction = EPDIR_Unknown; + CSocketConfiguration *conf = NULL; + if( PORT_I2S == connection->channelConfig->inSocket.port ) + { + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + direction = EPDIR_IN; + conf = &connection->channelConfig->inSocket; + } + else + { + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + direction = EPDIR_OUT; + conf = &connection->channelConfig->outSocket; + } + if( !entry->isI2SPortOpened ) + { + if( V1I2sClockModeUnknown != conf->i2sConfig.portClkDriveModeV1 + && V1I2sDataFormatUnknown != conf->i2sConfig.streamingDataFormatV1 ) + { + entry->isI2SPortOpened = true; + device->OpenStreamPortV1( nodeAddress, conf->i2sConfig.portClkDriveModeV1, + conf->i2sConfig.portOptionV1, conf->i2sConfig.streamingDataFormatV1, + allChannels[i]->channelId ); + } + } + if( 0xFFFFFFFF == conf->splittedOffset ) + { + //Normal socket is used + device->CreateStreamSocketV1( nodeAddress, direction, conf->blockWidth, conf->i2sConfig.pinV1, + allChannels[i]->channelId ); + } + else + { + //Enhanced (Splitted) Sockets are used + device->CreateSplittedStreamSocketV1( nodeAddress, direction, + conf->blockWidth, conf->splittedOffset, conf->subbufferSize, conf->i2sConfig.pinV1, + allChannels[i]->channelId ); + } + + } + + if( PORT_MOST == connection->channelConfig->outSocket.port ) + { + //Open MOST in case of out-connections + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + CSocketConfiguration *conf = &connection->channelConfig->outSocket; + device->CreateMostSocketV1( nodeAddress, conf->type, EPDIR_OUT, conf->address, + conf->blockWidth, allChannels[i]->channelId ); + } + else if( ( PORT_MOST == connection->channelConfig->inSocket.port ) + && ( 0xFFFFFFFF != connection->channelConfig->inSocket.address ) ) + { + //Or open MOST in case of in-connections with set connection label + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + CSocketConfiguration *conf = &connection->channelConfig->inSocket; + ConsolePrintf( PRIO_MEDIUM, + "Creating MOST socket for preconfigured Connection Label %d (0x%X)\n", conf->address, + conf->address ); + device->CreateMostSocketV1( nodeAddress, conf->type, EPDIR_IN, conf->address, conf->blockWidth, + allChannels[i]->channelId ); + } + } + else if( 3 == apiVer ) + { + /*--------------------------------------------*/ + /*-INIC API VERSION 3 (OS81118-D-Rev FW2.3.0) */ + /*--------------------------------------------*/ + if (0 == i) + device->GetDeviceVersion( nodeAddress ); + + //Destroy all resources + if( !entry->isUsbPortOpened && ( 0x1 != nodeAddress ) ) + { + entry->isUsbPortOpened = true; + device->DeviceSyncV3( nodeAddress, true ); + } + + if( ( PORT_USB == connection->channelConfig->inSocket.port ) + || ( PORT_USB == connection->channelConfig->outSocket.port ) ) + { + EPDirection_t direction = EPDIR_Unknown; + CSocketConfiguration *conf = NULL; + if( PORT_USB == connection->channelConfig->inSocket.port ) + { + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + direction = EPDIR_IN; + conf = &connection->channelConfig->inSocket; + } + else + { + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + direction = EPDIR_OUT; + conf = &connection->channelConfig->outSocket; + } + if( 0xFFFFFFFF == conf->splittedOffset ) + { + //No Splitter / No Combiner + device->CreateUsbSocketV3( nodeAddress, conf->type, direction, conf->address, + conf->packetsPerTransaction, allChannels[i]->channelId ); + } + else + { + //Splitter or Combiner is used + //Do not create multiple USB sockets for a single Splitter / Combiner connection + bool skip = false; + for( uint16_t j = 0; j < entry->GetAmountOfConnections(); j++ ) + { + CNodeConnectionEntry *checkConn = entry->GetConnectionByIndex( j ); + CSocketConfiguration *checkConf = NULL; + if( EPDIR_IN == direction ) + checkConf = &checkConn->channelConfig->inSocket; + else + checkConf = &checkConn->channelConfig->outSocket; + + if( ( checkConf != conf ) && ( conf->address == checkConf->address ) ) + { + skip = true; + break; + } + } + RaiseAvailable( connection ); + if( !skip ) + device->CreateSplittedUsbSocketV3( nodeAddress, conf->type, direction, conf->address, + conf->packetsPerTransaction, conf->subbufferSize, allChannels[i]->channelId ); + } + } + if( ( PORT_MLB == connection->channelConfig->inSocket.port ) + || ( PORT_MLB == connection->channelConfig->outSocket.port ) ) + { + EPDirection_t direction = EPDIR_Unknown; + CSocketConfiguration *conf = NULL; + if( PORT_MLB == connection->channelConfig->inSocket.port ) + { + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + direction = EPDIR_IN; + conf = &connection->channelConfig->inSocket; + } + else + { + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + direction = EPDIR_OUT; + conf = &connection->channelConfig->outSocket; + } + if( !entry->isMlbPortOpened ) + { + entry->isMlbPortOpened = true; + device->OpenMlbV3( nodeAddress, vodXml->GetDeviceMlbPortSpeed( deviceType ) ); + } + if( 0xFFFFFFFF == conf->splittedOffset ) + { + //No Splitter / No Combiner + device->CreateMlbSocketV3( nodeAddress, ( uint16_t )0x0A00, //Const value for first instance of MLB bus + conf->type, direction, conf->address, conf->blockWidth, allChannels[i]->channelId ); + } + else + { + //Splitter or Combiner is used + //Do not create multiple MLB sockets for a single Splitter / Combiner connection + bool skip = false; + for( uint16_t j = 0; j < entry->GetAmountOfConnections(); j++ ) + { + CNodeConnectionEntry *checkConn = entry->GetConnectionByIndex( j ); + CSocketConfiguration *checkConf = NULL; + if( EPDIR_IN == direction ) + checkConf = &checkConn->channelConfig->inSocket; + else + checkConf = &checkConn->channelConfig->outSocket; + + if( ( checkConf != conf ) && ( conf->address == checkConf->address ) ) + { + skip = true; + break; + } + } + RaiseAvailable( connection ); + if( !skip ) + device->CreateSplittedMlbSocketV3( nodeAddress, ( uint16_t )0x0A00, //Const value for first instance of MLB bus + conf->type, direction, conf->address, conf->blockWidth, conf->subbufferSize, + allChannels[i]->channelId ); + } + } + if( ( PORT_I2S == connection->channelConfig->inSocket.port ) + || ( PORT_I2S == connection->channelConfig->outSocket.port ) ) + { + EPDirection_t direction = EPDIR_Unknown; + CSocketConfiguration *conf = NULL; + uint8_t portInst = 0xFF; + if( PORT_I2S == connection->channelConfig->inSocket.port ) + { + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + direction = EPDIR_IN; + conf = &connection->channelConfig->inSocket; + } + else + { + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + direction = EPDIR_OUT; + conf = &connection->channelConfig->outSocket; + } + switch( conf->i2sConfig.pin ) + { + case V3I2SSRXA0: + case V3I2SSRXA1: + portInst = 0; + break; + case V3I2SSRXB0: + case V3I2SSRXB1: + portInst = 1; + break; + default: + break; + } + if( !entry->isI2SPortOpened ) + { + if( V3I2SOptionUnknown != conf->i2sConfig.portOption ) + { + entry->isI2SPortOpened = true; + device->ConfigureStreamPortV3( nodeAddress, 0, conf->i2sConfig.portOption, + conf->i2sConfig.clockMode, conf->i2sConfig.delayMode, allChannels[i]->channelId ); + device->ConfigureStreamPortV3( nodeAddress, 1, conf->i2sConfig.portOption, + V3I2SClockModeWildcard, V3I2SDelayWildcard, allChannels[i]->channelId ); + } + if( V3I2SSpeedUnknown != conf->i2sConfig.portSpeed ) + { + entry->isI2SPortOpened = true; + device->CreateStreamPortV3( nodeAddress, 0, conf->i2sConfig.portSpeed, + conf->i2sConfig.alignment, allChannels[i]->channelId ); + device->CreateStreamPortV3( nodeAddress, 1, V3I2SSpeedWildcard, + conf->i2sConfig.alignment, allChannels[i]->channelId ); + } + } + if( 0xFFFFFFFF == conf->splittedOffset ) + { + //No Splitter / No Combiner + device->CreateStreamSocketV3( nodeAddress, portInst, direction, conf->blockWidth, + conf->i2sConfig.pin, allChannels[i]->channelId ); + } + else + { + //Splitter or Combiner is used + //Do not create multiple I2S sockets for a single Splitter / Combiner connection + bool skip = false; + for( uint16_t j = 0; j < entry->GetAmountOfConnections(); j++ ) + { + CNodeConnectionEntry *checkConn = entry->GetConnectionByIndex( j ); + CSocketConfiguration *checkConf = NULL; + if( EPDIR_IN == direction ) + checkConf = &checkConn->channelConfig->inSocket; + else + checkConf = &checkConn->channelConfig->outSocket; + + if( ( checkConf != conf ) && ( conf->i2sConfig.pin == checkConf->i2sConfig.pin ) ) + { + skip = true; + break; + } + } + RaiseAvailable( connection ); + if( !skip ) + device->CreateSplittedStreamSocketV3( nodeAddress, portInst, direction, + conf->blockWidth, conf->i2sConfig.pin, conf->subbufferSize, + allChannels[i]->channelId ); + } + + } + if( PORT_MOST == connection->channelConfig->outSocket.port ) + { + //Open MOST in case of out-connections + if( connection->outSocketState != NodeConnection_NotUsed ) + continue; + connection->outSocketState = NodeConnection_Pending_Up; + CSocketConfiguration *conf = &connection->channelConfig->outSocket; + device->CreateMostSocketV3( nodeAddress, conf->type, EPDIR_OUT, conf->address, + conf->blockWidth, allChannels[i]->channelId ); + } + else if( ( PORT_MOST == connection->channelConfig->inSocket.port ) + && ( 0xFFFFFFFF != connection->channelConfig->inSocket.address ) ) + { + //Or open MOST in case of in-connections with set connection label + if( connection->inSocketState != NodeConnection_NotUsed ) + continue; + connection->inSocketState = NodeConnection_Pending_Up; + CSocketConfiguration *conf = &connection->channelConfig->inSocket; + ConsolePrintf( PRIO_MEDIUM, + "Creating MOST socket for preconfigured Connection Label %d (0x%X)\n", conf->address, + conf->address ); + device->CreateMostSocketV3( nodeAddress, conf->type, EPDIR_IN, conf->address, conf->blockWidth, + allChannels[i]->channelId ); + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Unknown INIC api version:%d"RESETCOLOR"\n", apiVer ); + } + //Perform external script at the end + if( i == ( allChannels.Size() - 1 ) ) + { + if( NULL != connection->channelConfig->externalScipt ) + { + char configPath[300]; + if( NULL != searchPath ) + snprintf( configPath, sizeof ( configPath ), "%s/%s", searchPath, + connection->channelConfig->externalScipt ); + else + strncpy( configPath, connection->channelConfig->externalScipt, sizeof ( configPath ) ); + device->ExecuteMcmScript( nodeAddress, configPath ); + } + } + } + } + else + { + ConsolePrintf( PRIO_ERROR, + YELLOW"CNetwork::OnMostDeviceType(deviceType: 0x%X) there is no channel configuration for this device type."RESETCOLOR"\n", + deviceType ); + RaiseUknownConnection( devInstance, nodeAddress, EP_Unknown ); + } + sem_post( &vodXmlMutex ); +} + +void CNetwork::OnCreateTsiSocketV1( void *source, bool success, uint16_t nodeAddr, V1TsiPortInstance_t tsiPortInst, + EPDataType_t epType, EPDirection_t epDir, uint16_t blockWidthTsi, uint16_t socketHandle, uint32_t tag ) +{ + assert(EP_Isochron == epType); + if( NULL == source || EP_Isochron != epType ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateTsiSocketV1 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateTsiSocketV1, inst:%d, addr:0x%X, port:%d, type:%d, dir:%d, bwTsi:%d, handle:0x%X, tag:0x%X\n", + devInstance, nodeAddr, tsiPortInst, epType, epDir, blockWidthTsi, socketHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateTsiSocketV1, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + } + TryToConnectSockets( devInstance, nodeAddr, tag ); + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateMlbSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateMlbSocketV1 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateMlbSocketV1, inst:%d, addr:0x%X, type:%d, dir:%d, bwMlb:%d, mlbAddr:0x%X, handle:0x%X, tag:0x%X\n", devInstance, nodeAddr, epType, epDir, blockWidthMlb, mlbChannelAddress, socketHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + AimType_t aimType = AIM_UNKNOWN; + uint32_t amountOfBuffers = 0xFFFFFFFF; + uint32_t bufferSize = 0xFFFFFFFF; + uint32_t subbufferSize = 0xFFFFFFFF; + uint32_t packetsPerTransaction = 0xFFFFFFFF; + uint32_t splittedOffset = 0xFFFFFFFF; + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + amountOfBuffers = connection->channelConfig->inSocket.amountOfBuffers; + bufferSize = connection->channelConfig->inSocket.bufferSize; + subbufferSize = connection->channelConfig->inSocket.subbufferSize; + packetsPerTransaction = + connection->channelConfig->inSocket.packetsPerTransaction; + aimType = connection->channelConfig->inSocket.aimType; + splittedOffset = connection->channelConfig->inSocket.splittedOffset; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + amountOfBuffers = connection->channelConfig->outSocket.amountOfBuffers; + bufferSize = connection->channelConfig->outSocket.bufferSize; + subbufferSize = connection->channelConfig->outSocket.subbufferSize; + packetsPerTransaction = + connection->channelConfig->outSocket.packetsPerTransaction; + aimType = connection->channelConfig->outSocket.aimType; + splittedOffset = connection->channelConfig->outSocket.splittedOffset; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateMlbSocketV1, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + } + if( EP_Asynchron == epType ) + { + connection->channelConfig->outSocket.type = EP_Asynchron; + connection->channelConfig->inSocket.type = EP_Asynchron; + connection->inSocketState = NodeConnection_Used; + connection->outSocketState = NodeConnection_Used; + connection->connectedSocketState = NodeConnection_Used; + connection->mostConnectionLabel = 0xA; + } + else if( EP_Unknown == epType || EP_Unused == epType ) + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateMlbSocketV1, Channel configuration is invalid, because data type is unknown"RESETCOLOR"\n" ); + } + + TryToConnectSockets( devInstance, nodeAddr, tag ); + if( 0x1 == entry->nodeAddress && ( 0xFFFFFFFF == splittedOffset || 0x0 == splittedOffset ) ) + { + connection->bufferSize = bufferSize; + switch( epType ) + { + case EP_Synchron: + case EP_Isochron: + device->ConfigureMlbChannel( aimType, mlbChannelAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize, + subbufferSize, packetsPerTransaction ); + break; + case EP_Unknown: + case EP_Unused: + //Do nothing + break; + default: + device->ConfigureMlbChannel( mlbChannelAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize ); + break; + } + } + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateMostSocketV1( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateMostSocketV1 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateMostSocketV1, inst:%d, addr:0x%X, type:%d, dir:%d, bwMost:%d, conLabel:0x%X, handle:0x%X, tag:0x%X\n", devInstance, nodeAddr, epType, epDir, blockwidthMost, connectionLabel, socketHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + connection->mostConnectionLabel = connectionLabel; + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + } + TryToConnectSockets( devInstance, nodeAddr, tag ); + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnConnectSocketsV1( void *source, bool success, uint16_t nodeAddr, uint16_t inSocketHandle, + uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnConnectSocketsV1 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnConnectSocketsV1, inst:%d, addr:0x%X, inHandle:0x%X, outHandle:0x%X, conHandle:0x%X, tag:0x%X\n", + devInstance, nodeAddr, inSocketHandle, outSocketHandle, connectionHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + connection->connectedSocketState = NodeConnection_Used; + connection->connectHandle = connectionHandle; + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnDestroySocketV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, uint32_t tag ) +{ + if( NULL == source ) + return; + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnDestroySocketV1 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "CNetwork::OnDestroySocketV1(deviceInstance:%d, nodeAddress:0x%X, handle:0x%X, tag:0x%X\n", devInstance, + nodeAddr, handle, tag ); + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + if( ( handle == connection->inHandle ) && + ( NodeConnection_Pending_Down == connection->inSocketState ) ) + { + connection->inHandle = ( 0xFFFFFFFF ); + connection->inSocketState = NodeConnection_NotUsed; + if( ( NULL != connection->channelConfig ) + && ( PORT_MOST == connection->channelConfig->inSocket.port ) ) + { + connection->mostConnectionLabel = 0xFFFFFFFF; + } + } + else if( ( handle == connection->outHandle ) && + ( NodeConnection_Pending_Down == connection->outSocketState ) ) + { + connection->outHandle = ( 0xFFFFFFFF ); + connection->outSocketState = NodeConnection_NotUsed; + if( ( NULL != connection->channelConfig ) + && ( PORT_MOST == connection->channelConfig->outSocket.port ) ) + { + connection->mostConnectionLabel = 0xFFFFFFFF; + } + } + RaiseUnavailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnDisconnectSocketsV1( void *source, bool success, uint16_t nodeAddr, uint16_t handle, uint32_t tag ) +{ + if( NULL == source ) + return; + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnDisconnectSocketsV1 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "CNetwork::OnDisconnectSocketsV1(deviceInstance:%d, nodeAddress:0x%X, handle:0x%X, tag:0x%X\n", devInstance, + nodeAddr, handle, tag ); + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + if( ( handle == connection->connectHandle ) + && ( NodeConnection_Pending_Down == connection->connectedSocketState ) ) + { + connection->connectHandle = ( 0xFFFFFFFF ); + connection->connectedSocketState = NodeConnection_NotUsed; + } + RaiseUnavailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateI2SSocketV1( void *source, bool success, uint16_t nodeAddr, EPDirection_t epDir, + uint16_t blockWidthI2S, V1I2SPin_t pin, uint16_t socketHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateI2SSocketV1 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateI2SSocketV1, addr:0x%X, port:%d, dir:%d, blockwidth:%d, pin:%d, handle:0x%X, tag:0x%X\n", + devInstance, nodeAddr, epDir, blockWidthI2S, pin, socketHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateI2SSocket, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + } + TryToConnectSockets( devInstance, nodeAddr, tag ); + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateUsbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint8_t endPointAddress, uint16_t socketHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateUsbSocketV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateUsbSocketV2, inst:%d, addr:0x%X, type:%d, dir:%d, epAddr:%d, handle:0x%X, tag:0x%X\n", + devInstance, nodeAddr, epType, epDir, endPointAddress, socketHandle, tag ); + + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + AimType_t aimType = AIM_UNKNOWN; + uint32_t amountOfBuffers = 0xFFFFFFFF; + uint32_t bufferSize = 0xFFFFFFFF; + uint32_t subbufferSize = 0xFFFFFFFF; + uint32_t packetsPerTransaction = 0xFFFFFFFF; + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + amountOfBuffers = connection->channelConfig->inSocket.amountOfBuffers; + bufferSize = connection->channelConfig->inSocket.bufferSize; + subbufferSize = connection->channelConfig->inSocket.subbufferSize; + packetsPerTransaction = + connection->channelConfig->inSocket.packetsPerTransaction; + aimType = connection->channelConfig->inSocket.aimType; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + amountOfBuffers = connection->channelConfig->outSocket.amountOfBuffers; + bufferSize = connection->channelConfig->outSocket.bufferSize; + subbufferSize = connection->channelConfig->outSocket.subbufferSize; + packetsPerTransaction = + connection->channelConfig->outSocket.packetsPerTransaction; + aimType = connection->channelConfig->outSocket.aimType; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateUsbSocketV2, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + } + TryToConnectSockets( devInstance, nodeAddr, tag ); + + if( 0x1 == entry->nodeAddress ) + { + switch( epType ) + { + case EP_Synchron: + connection->bufferSize = bufferSize; + device->ConfigureUsbEndpoint( aimType, endPointAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize, + subbufferSize, packetsPerTransaction ); + break; + case EP_Isochron: + connection->bufferSize = bufferSize; + device->ConfigureUsbEndpoint( aimType, endPointAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize, + subbufferSize, packetsPerTransaction ); + break; + case EP_Unknown: + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateUsbSocketV2, Channel configuration is invalid, because data type is unknown"RESETCOLOR"\n" ); + break; + default: + connection->bufferSize = bufferSize; + device->ConfigureUsbEndpoint( endPointAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize ); + break; + } + } + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateSplittedUsbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint8_t endPointAddress, uint16_t usbHandle, uint16_t splitterHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateSplittedUsbSocketV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateSplittedUsbSocketV2, inst:%d, addr:0x%X, type:%d, dir:%d, epAddr:%d, USB-handle:0x%X, Splitter-handle:0x%X, tag:0x%X\n", devInstance, nodeAddr, epType, epDir, endPointAddress, usbHandle, splitterHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, false ); + if( NULL != connection ) + { + AimType_t aimType = AIM_UNKNOWN; + uint32_t amountOfBuffers = 0xFFFFFFFF; + uint32_t bufferSize = 0xFFFFFFFF; + uint32_t subbufferSize = 0xFFFFFFFF; + uint32_t packetsPerTransaction = 0xFFFFFFFF; + bool valSet = false; + for( uint16_t j = 0; j < entry->GetAmountOfConnections(); j++ ) + { + CNodeConnectionEntry *checkConn = entry->GetConnectionByIndex( j ); + if( EPDIR_IN == epDir ) + { + if( connection->channelConfig->inSocket.address != checkConn->channelConfig->inSocket.address ) + continue; + checkConn->inSocketState = NodeConnection_Used; + checkConn->inHandle = splitterHandle; + checkConn->splittedSourceHandle = usbHandle; + if( !valSet ) + { + valSet = true; + amountOfBuffers = checkConn->channelConfig->inSocket.amountOfBuffers; + bufferSize = checkConn->channelConfig->inSocket.bufferSize; + subbufferSize = checkConn->channelConfig->inSocket.subbufferSize; + packetsPerTransaction = + checkConn->channelConfig->inSocket.packetsPerTransaction; + aimType = connection->channelConfig->inSocket.aimType; + } + TryToConnectSockets( devInstance, nodeAddr, checkConn->channelId ); + } + else if( EPDIR_OUT == epDir ) + { + if( connection->channelConfig->outSocket.address != checkConn->channelConfig->outSocket.address ) + continue; + checkConn->outSocketState = NodeConnection_Used; + checkConn->outHandle = splitterHandle; + checkConn->splittedSourceHandle = usbHandle; + if( !valSet ) + { + valSet = true; + amountOfBuffers = checkConn->channelConfig->outSocket.amountOfBuffers; + bufferSize = checkConn->channelConfig->outSocket.bufferSize; + subbufferSize = checkConn->channelConfig->outSocket.subbufferSize; + packetsPerTransaction = + checkConn->channelConfig->outSocket.packetsPerTransaction; + aimType = connection->channelConfig->outSocket.aimType; + } + TryToConnectSockets( devInstance, nodeAddr, checkConn->channelId ); + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateUsbSocketV2, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + return; + } + } + + if( 0x1 == entry->nodeAddress ) + { + switch( epType ) + { + case EP_Synchron: + connection->bufferSize = bufferSize; + device->ConfigureUsbEndpoint( aimType, endPointAddress, epType, epDir, + connection->deviceName, sizeof ( connection->deviceName ), amountOfBuffers, bufferSize, + subbufferSize, packetsPerTransaction ); + break; + case EP_Isochron: + connection->bufferSize = bufferSize; + device->ConfigureUsbEndpoint( aimType, endPointAddress, epType, epDir, + connection->deviceName, sizeof ( connection->deviceName ), amountOfBuffers, bufferSize, + subbufferSize, packetsPerTransaction ); + break; + case EP_Unknown: + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateUsbSocketV2, Channel configuration is invalid, because data type is unknown"RESETCOLOR"\n" ); + break; + default: + connection->bufferSize = bufferSize; + device->ConfigureUsbEndpoint( endPointAddress, epType, epDir, + connection->deviceName, sizeof ( connection->deviceName ), amountOfBuffers, bufferSize ); + break; + } + } + RaiseAvailable( connection ); + } + PRINT_ALL_INFOS(); + } +} + +void CNetwork::OnCreateMlbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t socketHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateMlbSocketV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateMlbSocketV2, inst:%d, addr:0x%X, type:%d, dir:%d, mlbAddr:%d, handle:0x%X, tag:0x%X\n", + devInstance, nodeAddr, epType, epDir, mlbChannelAddress, socketHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + AimType_t aimType = AIM_UNKNOWN; + uint32_t amountOfBuffers = 0xFFFFFFFF; + uint32_t bufferSize = 0xFFFFFFFF; + uint32_t subbufferSize = 0xFFFFFFFF; + uint32_t packetsPerTransaction = 0xFFFFFFFF; + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + amountOfBuffers = connection->channelConfig->inSocket.amountOfBuffers; + bufferSize = connection->channelConfig->inSocket.bufferSize; + subbufferSize = connection->channelConfig->inSocket.subbufferSize; + packetsPerTransaction = + connection->channelConfig->inSocket.packetsPerTransaction; + aimType = connection->channelConfig->inSocket.aimType; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + amountOfBuffers = connection->channelConfig->outSocket.amountOfBuffers; + bufferSize = connection->channelConfig->outSocket.bufferSize; + subbufferSize = connection->channelConfig->outSocket.subbufferSize; + packetsPerTransaction = + connection->channelConfig->outSocket.packetsPerTransaction; + aimType = connection->channelConfig->outSocket.aimType; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateMlbSocketV2, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + } + TryToConnectSockets( devInstance, nodeAddr, tag ); + if( 0x1 == entry->nodeAddress ) + { + connection->bufferSize = bufferSize; + switch( epType ) + { + case EP_Synchron: + case EP_Isochron: + device->ConfigureMlbChannel( aimType, mlbChannelAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize, + subbufferSize, packetsPerTransaction ); + break; + case EP_Unknown: + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateMlbSocketV2, Channel configuration is invalid, because data type is unknown"RESETCOLOR"\n" ); + break; + default: + device->ConfigureMlbChannel( mlbChannelAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize ); + break; + } + } + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateSplittedMlbSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockWidthMlb, uint8_t mlbChannelAddress, uint16_t mlbSocketHandle, + uint16_t splitterHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateSplittedMlbSocketV3 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateSplittedMlbSocketV3, inst:%d, addr:0x%X, type:%d, dir:%d, mlbAddr:%d, mlb-handle:0x%X, Splitter-handle:0x%X, tag:0x%X\n", devInstance, nodeAddr, epType, epDir, mlbChannelAddress, mlbSocketHandle, splitterHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + AimType_t aimType = AIM_UNKNOWN; + uint32_t amountOfBuffers = 0xFFFFFFFF; + uint32_t bufferSize = 0xFFFFFFFF; + uint32_t subbufferSize = 0xFFFFFFFF; + uint32_t packetsPerTransaction = 0xFFFFFFFF; + bool valSet = false; + for( uint16_t j = 0; j < entry->GetAmountOfConnections(); j++ ) + { + CNodeConnectionEntry *checkConn = entry->GetConnectionByIndex( j ); + if( EPDIR_IN == epDir ) + { + if( connection->channelConfig->inSocket.address != checkConn->channelConfig->inSocket.address ) + continue; + checkConn->inSocketState = NodeConnection_Used; + checkConn->inHandle = splitterHandle; + checkConn->splittedSourceHandle = mlbSocketHandle; + if( !valSet ) + { + valSet = true; + amountOfBuffers = checkConn->channelConfig->inSocket.amountOfBuffers; + bufferSize = checkConn->channelConfig->inSocket.bufferSize; + subbufferSize = checkConn->channelConfig->inSocket.subbufferSize; + packetsPerTransaction = + checkConn->channelConfig->inSocket.packetsPerTransaction; + aimType = connection->channelConfig->inSocket.aimType; + } + TryToConnectSockets( devInstance, nodeAddr, checkConn->channelId ); + } + else if( EPDIR_OUT == epDir ) + { + if( connection->channelConfig->outSocket.address != checkConn->channelConfig->outSocket.address ) + continue; + checkConn->outSocketState = NodeConnection_Used; + checkConn->outHandle = splitterHandle; + checkConn->splittedSourceHandle = mlbSocketHandle; + if( !valSet ) + { + valSet = true; + amountOfBuffers = checkConn->channelConfig->outSocket.amountOfBuffers; + bufferSize = checkConn->channelConfig->outSocket.bufferSize; + subbufferSize = checkConn->channelConfig->outSocket.subbufferSize; + packetsPerTransaction = + checkConn->channelConfig->outSocket.packetsPerTransaction; + aimType = connection->channelConfig->outSocket.aimType; + } + TryToConnectSockets( devInstance, nodeAddr, checkConn->channelId ); + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateSplittedMlbSocketV3, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + return; + } + } + if( 0x1 == entry->nodeAddress ) + { + connection->bufferSize = bufferSize; + switch( epType ) + { + case EP_Synchron: + case EP_Isochron: + device->ConfigureMlbChannel( aimType, mlbChannelAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize, + subbufferSize, packetsPerTransaction ); + break; + case EP_Unknown: + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateMlbSocketV2, Channel configuration is invalid, because data type is unknown"RESETCOLOR"\n" ); + break; + default: + device->ConfigureMlbChannel( mlbChannelAddress, epType, epDir, + connection->deviceName, sizeof( connection->deviceName ), amountOfBuffers, bufferSize ); + break; + } + } + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateI2SSocketV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t socketHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateI2SSocket reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateI2SSocket, inst:%d, addr:0x%X, port:%d, dir:%d, blockwidth:%d, pin:%d, handle:0x%X, tag:0x%X\n", devInstance, nodeAddr, portInstance, epDir, blockWidthI2S, pin, socketHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateI2SSocket, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + } + TryToConnectSockets( devInstance, nodeAddr, tag ); + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateSplittedI2SSocketV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + EPDirection_t epDir, uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t i2sSocketHandle, uint16_t splitterHandle, + uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateSplittedI2SSocketV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateI2SSocket, inst:%d, addr:0x%X, port:%d, dir:%d, blockwidth:%d, pin:%d, i2s-handle:0x%X, Splitter-handle:0x%X, tag:0x%X\n", devInstance, nodeAddr, portInstance, epDir, blockWidthI2S, pin, i2sSocketHandle, splitterHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + if( EPDIR_IN == epDir ) + { + //Find all related pin configurations, this is the use case of splitted I2S sockets + V3I2SPin_t pin = connection->channelConfig->inSocket.i2sConfig.pin; + uint16_t all = entry->GetAmountOfConnections(); + for (uint16_t i = 0; i < all; i++) + { + CNodeConnectionEntry *con = entry->GetConnectionByIndex(i); + if (pin == con->channelConfig->inSocket.i2sConfig.pin) + { + con->inSocketState = NodeConnection_Used; + con->inHandle = splitterHandle; + con->splittedSourceHandle = i2sSocketHandle; + TryToConnectSockets( devInstance, nodeAddr, con->channelId ); + } + } + } + else if( EPDIR_OUT == epDir ) + { + //Find all related pin configurations, this is the use case of splitted I2S sockets + V3I2SPin_t pin = connection->channelConfig->outSocket.i2sConfig.pin; + uint16_t all = entry->GetAmountOfConnections(); + for (uint16_t i = 0; i < all; i++) + { + CNodeConnectionEntry *con = entry->GetConnectionByIndex(i); + if (pin == con->channelConfig->outSocket.i2sConfig.pin) + { + con->outSocketState = NodeConnection_Used; + con->outHandle = splitterHandle; + con->splittedSourceHandle = i2sSocketHandle; + TryToConnectSockets( devInstance, nodeAddr, con->channelId ); + } + } + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"Network::OnCreateI2SSocket, Channel configuration is invalid, because direction of stream is unknown"RESETCOLOR"\n" ); + } + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnCreateMostSocketV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + EPDirection_t epDir, uint16_t blockwidthMost, uint16_t connectionLabel, uint16_t socketHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateMostSocketV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnCreateMostSocketV2, inst:%d, addr:0x%X, type:%d, dir:%d, bwMost:%d, conLabel:0x%X, handle:0x%X, tag:0x%X\n", devInstance, nodeAddr, epType, epDir, blockwidthMost, connectionLabel, socketHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + connection->mostConnectionLabel = connectionLabel; + if( EPDIR_IN == epDir ) + { + connection->inSocketState = NodeConnection_Used; + connection->inHandle = socketHandle; + } + else if( EPDIR_OUT == epDir ) + { + connection->outSocketState = NodeConnection_Used; + connection->outHandle = socketHandle; + } + TryToConnectSockets( devInstance, nodeAddr, tag ); + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnConnectSocketsV3( void *source, bool success, uint16_t nodeAddr, EPDataType_t epType, + uint16_t inSocketHandle, uint16_t outSocketHandle, uint16_t connectionHandle, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnConnectSocketsV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } + ConsolePrintf( PRIO_MEDIUM, + "Network::OnConnectSocketsV2, inst:%d, addr:0x%X, Type0x%X, inHandle:0x%X, outHandle:0x%X, conHandle:0x%X, tag:0x%X\n", devInstance, nodeAddr, epType, inSocketHandle, outSocketHandle, connectionHandle, tag ); + + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( tag, true ); + if( NULL != connection ) + { + connection->connectedSocketState = NodeConnection_Used; + connection->connectHandle = connectionHandle; + RaiseAvailable( connection ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::OnControlChannelReadEnd( void *source ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + connectionBitMask = connectionBitMask & ~( 1 << devInstance ); + ConsolePrintf( PRIO_ERROR, RED"Destroying NetworkDevice with instance %d, because the reader thread has ended"RESETCOLOR"\n", + devInstance ); + + allNetworkDevices.Remove(device); + delete device; +} + +void CNetwork::OnMostControlMessage( void *source, uint32_t sourceAddr, uint32_t targetAddr, uint32_t nFBlock, + uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload ) +{ + if( NULL == source ) + return; + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + for( uint32_t i = 0; i < allListeners.Size(); i++ ) + { + allListeners[i]->OnMostControlMessage( devInstance, sourceAddr, targetAddr, nFBlock, nInst, nFunc, nOpType, + nPayloadLen, Payload ); + } +} + +void CNetwork::OnRbdResultV3( void *source, uint16_t nodeAddress, uint8_t result, uint8_t position, + uint8_t status, uint16_t id ) +{ + CNetworkDeviceListener::OnRbdResultV3( source, nodeAddress, result, position, status, id ); + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + for( uint32_t i = 0; i < allListeners.Size(); i++ ) + { + allListeners[i]->OnRingBreakDiagnosisResultV3( devInstance, nodeAddress, result, position, status, id ); + } +} + +void CNetwork::OnMostMacAddress( void *source, bool success, uint16_t nodeAddress, uint8_t macAddress1, + uint8_t macAddress2, uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6 ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnMostMacAddress reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddress ); + ShutdownMostBecauseOfErrors( device ); + return; + } +} + +void CNetwork::OnConfigureI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortOption_t option, V3I2SClockMode_t mode, V3I2SDelayMode_t delay, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnConfigureI2SPortV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + ShutdownMostBecauseOfErrors( device ); + return; + } +} + +void CNetwork::OnCreateI2SPortV3( void *source, bool success, uint16_t nodeAddr, uint8_t portInstance, + V3I2SPortSpeed_t clock, V3I2SAlignment_t align, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnCreateI2SPortV2 reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, nodeAddr ); + return; + } +} + +void CNetwork::OnDeviceVersion( void *source, bool success, uint32_t sourceAddr, + uint32_t productId, uint32_t fwVersion, uint32_t buildVersion, + uint8_t hwVersion, uint16_t diagnosisId, uint32_t tag ) +{ + if( NULL == source ) + return; + + CNetworkDevice *device = ( ( CNetworkDevice * )source ); + uint32_t devInstance = device->GetDeviceIndex(); + if( !success ) + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::OnDeviceVersion reports failure for device:%d, nodeAddress:0x%X"RESETCOLOR"\n", + devInstance, sourceAddr ); + return; + } + ConsolePrintf( PRIO_MEDIUM, "Got Device version for nodeAddress:0x%X, productId:0x%X, fwVersion:0x%X, "\ + "buildVersion:0x%X, hwVersion:%d, diagnosisId:0x%X\n", sourceAddr, productId, fwVersion, + buildVersion, hwVersion, diagnosisId); + if( 0x81118 == productId ) + { + uint8_t exMajor = 2; + uint8_t exMinor = 4; + uint8_t exRelease = 0; + uint32_t expectedFW = exMajor << 24 | exMinor << 16 | exRelease << 8; + if (fwVersion < expectedFW) + { + ConsolePrintf( PRIO_ERROR, RED"Warning, the device with node address:0x%X uses"\ + " too old firmware! Please update to: V%d.%d.%d"RESETCOLOR"\n", + sourceAddr, exMajor, exMinor, exRelease); + } + } +} \ No newline at end of file diff --git a/Src/Network/Network_Private.cpp b/Src/Network/Network_Private.cpp new file mode 100644 index 0000000..2aaef6e --- /dev/null +++ b/Src/Network/Network_Private.cpp @@ -0,0 +1,695 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include "SafeVector.h" +#include "Network.h" +#include "NetworkDevice.h" +#include "DriverConfiguration.h" +#include "MacAddr.h" +#include "Board.h" +#include "Console.h" +#include "NodeDatabase.h" + +#define PRINT_ALL_INFOS() +#define MAX_MOST_DEVICES 4 + +void CNetwork::DestroyAllResources( CNetworkDevice *device, uint16_t nodeAddress ) +{ + if( NULL == device ) + return; + + CNodeEntry *node = CNodeEntry::GetNodeEntry( device->GetDeviceIndex(), nodeAddress ); + if( NULL == node ) + return; + + sem_wait( &vodXmlMutex ); + uint32_t apiVer = vodXml->GetDeviceApi( node->deviceType ); + sem_post( &vodXmlMutex ); + if( 1 == apiVer ) + { + for( int i = 0; i < node->GetAmountOfConnections(); i++ ) + { + CNodeConnectionEntry *connection = node->GetConnectionByIndex( i ); + if( NULL == connection || NULL == connection->channelConfig ) + continue; + + uint32_t inHandle = connection->inHandle; + uint32_t outHandle = connection->outHandle; + uint32_t conHandle = connection->connectHandle; + NodeConnectionState_t inState = connection->inSocketState; + NodeConnectionState_t outState = connection->outSocketState; + NodeConnectionState_t conState = connection->connectedSocketState; + uint32_t channelId = connection->channelConfig->channelId; + connection->deviceName[0] = '\0'; + connection->mostConnectionLabel = 0xFFFFFFFF; + connection->inHandle = 0xFFFFFFFF; + connection->outHandle = 0xFFFFFFFF; + connection->connectHandle = 0xFFFFFFFF; + connection->inSocketState = NodeConnection_NotUsed; + connection->outSocketState = NodeConnection_NotUsed; + connection->connectedSocketState = NodeConnection_NotUsed; + if( NodeConnection_NotUsed != conState && 0xFFFFFFFF != conHandle ) + { + device->DisconnectSocketsV1( nodeAddress, conHandle, channelId ); + } + if( NodeConnection_NotUsed != inState && 0xFFFFFFFF != inHandle ) + { + device->DestroySocketV1( nodeAddress, inHandle, channelId ); + } + if( NodeConnection_NotUsed != outState && 0xFFFFFFFF != outHandle ) + { + device->DestroySocketV1( nodeAddress, outHandle, channelId ); + } + RaiseUnavailable( connection ); + } + } + else if( 2 == apiVer || 3 == apiVer ) + { + uint8_t amount; +#define MAX_DESTROY_ELEMENTS 10 + uint16_t handles[MAX_DESTROY_ELEMENTS]; + + //Start to destroy the connections first: + do + { + amount = 0; + for( int i = 0; amount < MAX_DESTROY_ELEMENTS && i < node->GetAmountOfConnections(); i++ ) + { + CNodeConnectionEntry *connection = node->GetConnectionByIndex( i ); + if( NULL == connection || NULL == connection->channelConfig ) + continue; + if( NodeConnection_NotUsed != connection->connectedSocketState ) + { + handles[amount++] = connection->connectHandle; + connection->connectHandle = 0xFFFFFFFF; + connection->connectedSocketState = NodeConnection_NotUsed; + } + } + if( amount != 0 ) + { + device->ResourceDestroyV3( nodeAddress, amount, handles, 0 ); + } + } + while( amount != 0 ); + + do + { + amount = 0; + //Continue to destroy the In and Out sockets: + for( int i = 0; amount < MAX_DESTROY_ELEMENTS && i < node->GetAmountOfConnections(); i++ ) + { + CNodeConnectionEntry *connection = node->GetConnectionByIndex( i ); + if( NULL == connection || NULL == connection->channelConfig ) + continue; + if( ( NodeConnection_NotUsed != connection->inSocketState ) + && ( 0xFFFFFFFF != connection->inHandle ) ) + { + handles[amount++] = connection->inHandle; + for( int j = 0; j < node->GetAmountOfConnections(); j++ ) + { + if( i == j ) + continue; + CNodeConnectionEntry *otherConn = node->GetConnectionByIndex( j ); + if( ( otherConn != NULL ) && ( otherConn->inHandle == connection->inHandle ) ) + { + otherConn->inHandle = 0xFFFFFFFF; + otherConn->inSocketState = NodeConnection_NotUsed; + } + } + connection->inHandle = 0xFFFFFFFF; + connection->inSocketState = NodeConnection_NotUsed; + } + if( amount < MAX_DESTROY_ELEMENTS && + ( NodeConnection_NotUsed != connection->outSocketState ) + && ( 0xFFFFFFFF != connection->outHandle ) ) + { + handles[amount++] = connection->outHandle; + for( int j = 0; j < node->GetAmountOfConnections(); j++ ) + { + if( i == j ) + continue; + CNodeConnectionEntry *otherConn = node->GetConnectionByIndex( j ); + if( ( otherConn != NULL ) && ( otherConn->outHandle == connection->outHandle ) ) + { + otherConn->outHandle = 0xFFFFFFFF; + otherConn->outSocketState = NodeConnection_NotUsed; + } + } + connection->outHandle = 0xFFFFFFFF; + connection->outSocketState = NodeConnection_NotUsed; + RaiseUnavailable( connection ); + } + + } + if( amount != 0 ) + { + device->ResourceDestroyV3( nodeAddress, amount, handles, 0 ); + } + } + while( amount != 0 ); + + //Last step destroy Splitter / Combiner input / output + do + { + amount = 0; + for( int i = 0; i < node->GetAmountOfConnections(); i++ ) + { + CNodeConnectionEntry *connection = node->GetConnectionByIndex( i ); + if( NULL == connection || NULL == connection->channelConfig ) + continue; + if( 0xFFFFFFFF != connection->splittedSourceHandle ) + { + handles[amount++] = connection->splittedSourceHandle; + for( int j = 0; j < node->GetAmountOfConnections(); j++ ) + { + if( i == j ) + continue; + CNodeConnectionEntry *otherConn = node->GetConnectionByIndex( j ); + if( ( otherConn != NULL ) && ( otherConn->splittedSourceHandle == connection-> + splittedSourceHandle ) ) + { + otherConn->splittedSourceHandle = 0xFFFFFFFF; + } + } + connection->splittedSourceHandle = 0xFFFFFFFF; + } + } + if( amount != 0 ) + { + device->ResourceDestroyV3( nodeAddress, amount, handles, 0 ); + } + } + while( amount != 0 ); + } +} + +void CNetwork::DestroyAllResources() +{ + CNetworkDevice *device; + uint16_t nodeAmount = CNodeEntry::GetAmountOfNodeEntries(); + for( int i = 0; i < nodeAmount; i++ ) + { + CNodeEntry *node = CNodeEntry::GetNodeEntry( i ); + if( NULL == node ) + continue; + CNetworkDevice *device = GetNetworkDevice( node->deviceInstance ); + uint16_t nodeAddress = node->nodeAddress; + DestroyAllResources( device, nodeAddress ); + } + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + device = allNetworkDevices[i]; + if( NULL != device ) + { + sem_wait( &vodXmlMutex ); + if( NULL != vodXml ) + { + sem_post( &vodXmlMutex ); + if( device->IsTimingMaster() ) + { + ConsolePrintf( PRIO_MEDIUM, "DestroyAllResources, Performing MOST Shutdown index: %d\n", + device->GetDeviceIndex() ); + device->ToggleNotOk(); + device->SetNetstate( NetworkState_ShutdownInProgress ); + device->MostNetworkShutdownV3(); + } + } + else + sem_post( &vodXmlMutex ); + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::CloseAllNetworkDevices() +{ + DestroyAllResources(); + CNetworkDevice *device; + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + device = allNetworkDevices[i]; + if( NULL != device ) + { + ConsolePrintf( PRIO_MEDIUM, "CloseAllNetworkDevices, Closing device index: %d\n", + device->GetDeviceIndex() ); + device->Close(); + } + } + allNetworkDevices.RemoveAll(true); + connectionBitMask = 0; +} + +void CNetwork::FindNewNetworkDevices() +{ + sem_wait( &vodXmlMutex ); + if( NULL == vodXml ) + { + sem_post( &vodXmlMutex ); + return; + } + bool success = false; + bool infoQueried = false; + uint8_t *controlRxAddress = NULL; + uint8_t *controlTxAddress = NULL; + uint32_t *apiVersion = NULL; + SocketPort_t *hwPort = NULL; + bool *isTimingMaster = NULL; + uint32_t *asyncBW = NULL; + uint8_t serverNum = 0; + for( uint8_t devInstance = 0; devInstance < MAX_MOST_DEVICES; devInstance++ ) + { + if( 0 != ( connectionBitMask & ( 1 << devInstance ) ) ) + continue; + + if( !infoQueried ) + infoQueried = vodXml->GetLocalInicConfigurations( &controlRxAddress, &controlTxAddress, &apiVersion, + &hwPort, &isTimingMaster, &asyncBW, &serverNum ); + + if( !infoQueried ) + { + ConsolePrintf( PRIO_ERROR, RED"Can not parse XML file to configure server device"RESETCOLOR"\n" ); + sem_post( &vodXmlMutex ); + return; + } + for( uint8_t conIndex = 0; conIndex < serverNum; conIndex++ ) + { + if( ( ( PORT_MLB == hwPort[conIndex] ) && ExistsMlbDeviceInstance( devInstance ) ) + || ( ( PORT_USB == hwPort[conIndex] ) && ExistsUsbDeviceInstance( devInstance ) ) ) + { + connectionBitMask += ( 1 << devInstance ); + ConsolePrintf( PRIO_MEDIUM, "=======Server %d device uses %s, INIC API V%d=======\n", + devInstance, ( PORT_USB == hwPort[conIndex] ? "USB" : PORT_MLB == hwPort[conIndex] ? "MLB" : + "UNKNOWN" ), apiVersion[conIndex] ); + ConsolePrintf( PRIO_MEDIUM, "Opening device instance %d\n", devInstance ); + CNetworkDevice *newDevice = new CNetworkDevice( devInstance, + apiVersion[conIndex], isTimingMaster[conIndex], asyncBW[conIndex], promiscuous ); + switch( hwPort[conIndex] ) + { + case PORT_USB: + success = newDevice->OpenUsb( controlRxAddress[conIndex], controlTxAddress[conIndex] ); + break; + case PORT_MLB: + success = newDevice->OpenMlb( controlRxAddress[conIndex], controlTxAddress[conIndex] ); + break; + default: + success = false; + break; + } + if( success ) + { + allNetworkDevices.PushBack( newDevice ); + newDevice->AddListener( this ); + } + else + { + if( ( PORT_USB == hwPort[conIndex] ) && ( apiVersion[conIndex] < 3 ) ) + ConsolePrintf( PRIO_ERROR, RED"Failed to open device instance %d"\ + RESETCOLOR"\n", devInstance ); + delete newDevice; + } + } + } + } + if( NULL != controlRxAddress ) + free( controlRxAddress ); + if( NULL != controlTxAddress ) + free( controlTxAddress ); + if( NULL != apiVersion ) + free( apiVersion ); + if( NULL != hwPort ) + free( hwPort ); + sem_post( &vodXmlMutex ); +} + +void CNetwork::TryToCreateRoute( uint32_t devInstance, bool mostIsTx, void *e, void *t ) +{ + if( NULL == e || NULL == t ) + return; + CNodeEntry *entry = ( CNodeEntry * )e; + CRouteTerminal *terminal = ( CRouteTerminal * )t; + CSafeVector connectedTerminals; + + sem_wait( &vodXmlMutex ); + bool found = vodXml->GetRouteTerminals( terminal, connectedTerminals ); + sem_post( &vodXmlMutex ); + if( !found ) + return; + CNodeEntry *oppositeEntry = NULL; + for( uint32_t i = 0; i < connectedTerminals.Size(); i++ ) + { + if( NULL != ( oppositeEntry = CNodeEntry::GetNodeEntry( devInstance, connectedTerminals[i]->deviceType, + connectedTerminals[i]->instance ) ) ) + { + if( mostIsTx ) + ConnectSourceToSink( entry, oppositeEntry, terminal->channelId, connectedTerminals[i]->channelId ); + else + ConnectSourceToSink( oppositeEntry, entry, connectedTerminals[i]->channelId, terminal->channelId ); + } + delete connectedTerminals[i]; + } +} + +void CNetwork::TryToConnectSockets( uint32_t devInstance, uint16_t nodeAddr, uint32_t channelId ) +{ + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddr ); + if( NULL != entry ) + { + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( channelId, true ); + CNetworkDevice *device = GetNetworkDevice( devInstance ); + CChannelConfiguration *config = connection->channelConfig; + sem_wait( &vodXmlMutex ); + uint32_t deviceApi = vodXml->GetDeviceApi( connection->channelConfig->deviceType ); + sem_post( &vodXmlMutex ); + if( ( NULL != connection ) && ( NULL != device ) && ( NULL != config ) ) + { + CRouteTerminal terminal; + terminal.channelId = channelId; + terminal.deviceType = entry->deviceType; + terminal.instance = CNodeEntry::GetTerminalInstance( devInstance, nodeAddr, entry->deviceType ); + + if( ( NodeConnection_Used == connection->inSocketState ) + && ( NodeConnection_Used == connection->outSocketState ) + && ( NodeConnection_NotUsed == connection->connectedSocketState ) ) + { + if( deviceApi > 3 ) + { + ConsolePrintf( + PRIO_ERROR, RED"TryToConnectSockets found unknown device API version: %d"RESETCOLOR"\n", + deviceApi ); + return; + } + connection->connectedSocketState = NodeConnection_Pending_Up; + if( 1 == deviceApi ) + { + device->ConnectSocketsV1( nodeAddr, connection->inHandle, connection->outHandle, channelId ); + } + else if( ( 2 == deviceApi ) || ( 3 == deviceApi ) ) + { + uint32_t offset = config->inSocket.splittedOffset; + if( 0xFFFFFFFF == offset ) + { + offset = config->outSocket.splittedOffset; + if( 0xFFFFFFFF == offset ) + offset = 0; + } + device->ConnectSocketsV3( nodeAddr, config->outSocket.type, connection->inHandle, + connection->outHandle, offset, channelId ); + } + TryToCreateRoute( devInstance, true, entry, &terminal ); + } + else if( ( NodeConnection_NotUsed == connection->inSocketState ) + && ( NodeConnection_Used == connection->outSocketState ) + && ( PORT_MOST == config->inSocket.port ) ) + { + TryToCreateRoute( devInstance, false, entry, &terminal ); + } + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::TryToCloseExistingMostConnection( void *inNode, uint32_t inChannelId, void *outNode, + uint32_t outChannelId ) +{ + if( ( NULL == inNode ) || ( NULL == outNode ) ) + return; + + for( uint8_t i = 0; i < 2; i++ ) + { + CNodeConnectionEntry *closeEntry = NULL; + switch( i ) + { + case 0: + closeEntry = ( ( CNodeEntry * )outNode )->GetConnectionByChannelId( outChannelId, false ); + break; + case 1: + closeEntry = ( ( CNodeEntry * )inNode )->GetConflictingConnection( inChannelId, + ( ( CNodeEntry * )outNode ) ); + break; + } + if( NULL == closeEntry || NULL == closeEntry->channelConfig + || NULL == closeEntry->parentNode ) + continue; + + if( ( NodeConnection_Used == closeEntry->inSocketState ) && + ( NodeConnection_Used == closeEntry->connectedSocketState ) && + ( PORT_MOST == closeEntry->channelConfig->inSocket.port ) ) + { + uint8_t oppInst = closeEntry->parentNode->deviceInstance; + CNetworkDevice *oppDevice = GetNetworkDevice( oppInst ); + if( NULL != oppDevice ) + { + uint16_t oppNodeAddress = closeEntry->parentNode->nodeAddress; + uint32_t oppApiVer = closeEntry->parentNode->deviceType; + uint32_t oppConHandle = closeEntry->connectHandle; + uint32_t oppInHandle = closeEntry->inHandle; + uint32_t oppChannelId = closeEntry->channelId; + + closeEntry->connectedSocketState = NodeConnection_Pending_Down; + closeEntry->inSocketState = NodeConnection_Pending_Down; + + if( 1 == oppApiVer ) + { + oppDevice->DisconnectSocketsV1( oppNodeAddress, oppConHandle, oppChannelId ); + oppDevice->DestroySocketV1( oppNodeAddress, oppInHandle, oppChannelId ); + } + else if( 2 == oppApiVer || 3 == oppApiVer ) + { + uint16_t a[2]; + a[0] = oppConHandle; + a[1] = oppInHandle; + oppDevice->ResourceDestroyV3( oppNodeAddress, 2, a, oppChannelId ); + } + } + } + } + PRINT_ALL_INFOS(); +} + +void CNetwork::RaiseAvailable( void *con ) +{ + CNodeConnectionEntry *connection = ( CNodeConnectionEntry * )con; + if( ( NULL != connection ) + && ( NULL != connection->parentNode ) + && ( NULL != connection->channelConfig ) + ) + { + uint32_t blockWidth = connection->channelConfig->outSocket.blockWidth; + if( 0xFFFFFFFF == blockWidth ) + blockWidth = connection->channelConfig->inSocket.blockWidth; + + uint16_t xact = 0; + if ( PORT_USB == connection->channelConfig->inSocket.port ) + xact = connection->channelConfig->inSocket.packetsPerTransaction; + else if ( PORT_USB == connection->channelConfig->outSocket.port ) + xact = connection->channelConfig->outSocket.packetsPerTransaction; + + uint32_t devInst = CNodeEntry::GetTerminalInstance( connection->parentNode->deviceInstance, + connection->parentNode->nodeAddress, connection->parentNode->deviceType ); + for( uint32_t i = 0; i < allListeners.Size(); i++ ) + { + int offset = -1; + int maxBW = -1; + if( 0xFFFFFFFF != connection->channelConfig->inSocket.splittedOffset ) + { + offset = connection->channelConfig->inSocket.splittedOffset; + maxBW = connection->channelConfig->inSocket.subbufferSize; + } + else if( 0xFFFFFFFF != connection->channelConfig->outSocket.splittedOffset ) + { + offset = connection->channelConfig->outSocket.splittedOffset; + maxBW = connection->channelConfig->outSocket.subbufferSize; + } + allListeners[i]->OnChannelAvailable( connection->parentNode->macAddress, + connection->parentNode->deviceType, devInst, connection->channelId, + connection->parentNode->deviceInstance, connection->channelConfig->outSocket.type, + blockWidth, ( PORT_MOST == connection->channelConfig->outSocket.port ), ( NodeConnection_Used == + connection->inSocketState ), ( NodeConnection_Used == connection->outSocketState ), + ( NodeConnection_Used == connection->connectedSocketState ), connection->bufferSize, + connection->mostConnectionLabel, offset, maxBW, xact, connection->deviceName ); + } + } +} + +void CNetwork::RaiseUnavailable( void *con ) +{ + CNodeConnectionEntry *connection = ( CNodeConnectionEntry * )con; + if( ( NULL != connection ) + && ( NULL != connection->parentNode ) + && ( NULL != connection->channelConfig ) + && ( NULL != connection->parentNode->macAddress ) + ) + { + for( uint32_t i = 0; i < allListeners.Size(); i++ ) + { + allListeners[i]->OnChannelUnavailable( connection->parentNode->macAddress, connection->channelId, + connection->parentNode->deviceInstance ); + } + } +} + +CNetworkDevice *CNetwork::GetNetworkDevice( uint32_t devInstance ) +{ + CNetworkDevice *device = NULL; + for( uint32_t i = 0; i < allNetworkDevices.Size(); i++ ) + { + if( devInstance == allNetworkDevices[i]->GetDeviceIndex() ) + { + device = allNetworkDevices[i]; + break; + } + } + return device; +} + +CMacAddr *CNetwork::SetMacAddress( CNetworkDevice *device, uint32_t devInstance, uint16_t nodeAddress, + uint8_t inicApiVersion ) +{ + if( NULL == device ) + return NULL; + CMacAddr *mac = new CMacAddr( devInstance, nodeAddress ); + if( NULL == mac ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to instance CMacAddr object"RESETCOLOR"\n" ); + return NULL; + } + ConsolePrintf( PRIO_MEDIUM, + "Creating local MAC for deviceInstance:%d: nodeAddress:0x%X, MAC-Address:%02X-%02X-%02X-%02X-%02X-%02X)\n", + devInstance, nodeAddress, ( *mac )[0], ( *mac )[1], ( *mac )[2], ( *mac )[3], ( *mac )[4], ( *mac )[5] ); + + switch( inicApiVersion ) + { + case 1: + device->SetMostMacAddressV1( nodeAddress, ( *mac )[0], ( *mac )[1], ( *mac )[2], ( *mac )[3], ( *mac )[4], + ( *mac )[5], false ); + break; + case 2: + device->SetMostMacAddressV3( nodeAddress, ( *mac )[0], ( *mac )[1], ( *mac )[2], ( *mac )[3], ( *mac )[4], + ( *mac )[5] ); + break; + case 3: + device->SetMostMacAddressV3( nodeAddress, ( *mac )[0], ( *mac )[1], ( *mac )[2], ( *mac )[3], ( *mac )[4], + ( *mac )[5] ); + break; + default: + ConsolePrintf( + PRIO_ERROR, RED"Can not set local MAC, because device API version (%d) is unknown"RESETCOLOR"\n", + inicApiVersion ); + delete mac; + mac = NULL; + break; + } + return mac; +} + +bool CNetwork::ConnectSourceToSink( void *mostTxNode, void *mostRxNode, uint32_t sourceChannelId, + uint32_t sinkChannelId ) +{ + bool success = false; + PRINT_ALL_INFOS(); + if( ( NULL != mostTxNode ) && ( NULL != mostRxNode ) ) + { + CNodeEntry *txNode = ( CNodeEntry * )mostTxNode; + CNodeEntry *rxNode = ( CNodeEntry * )mostRxNode; + CNodeConnectionEntry *inConnection = txNode->GetConnectionByChannelId( sourceChannelId, false ); + CNodeConnectionEntry *outConnection = rxNode->GetConnectionByChannelId( sinkChannelId, false ); + CNetworkDevice *outDevice = GetNetworkDevice( rxNode->deviceInstance ); + if( ( NULL != inConnection ) && ( NULL != outConnection ) && ( NULL != outDevice ) + && ( NULL != outConnection->channelConfig ) && ( 0xFFFFFFFF != inConnection->mostConnectionLabel ) ) + { + if( ( NodeConnection_Used == inConnection->outSocketState ) + && ( NodeConnection_NotUsed == outConnection->inSocketState ) ) + { + uint32_t connectionLabel = inConnection->mostConnectionLabel; + sem_wait( &vodXmlMutex ); + uint32_t inApiVer = vodXml->GetDeviceApi( txNode->deviceType ); + uint32_t outApiVer = vodXml->GetDeviceApi( rxNode->deviceType ); + sem_post( &vodXmlMutex ); + ConsolePrintf( PRIO_MEDIUM, + BLUE"CNetwork::ConnectSourceToSink Connection Label:0x%X, IN-API-Ver:%d, OUT-API-Ver:%d, devIndex:%d, address:0x%X"RESETCOLOR"\n", + connectionLabel, inApiVer, outApiVer, rxNode->deviceInstance, + rxNode->nodeAddress ); + TryToCloseExistingMostConnection( txNode, sourceChannelId, rxNode, sinkChannelId ); + if( 1 == outApiVer ) + { + outDevice->CreateMostSocketV1( rxNode->nodeAddress, outConnection->channelConfig->inSocket.type, + EPDIR_IN, connectionLabel, outConnection->channelConfig->inSocket.blockWidth, sinkChannelId ); + success = true; + } + else if( 2 == outApiVer || 3 == outApiVer ) + { + outDevice->CreateMostSocketV3( rxNode->nodeAddress, outConnection->channelConfig->inSocket.type, + EPDIR_IN, connectionLabel, outConnection->channelConfig->inSocket.blockWidth, sinkChannelId ); + success = true; + } + if( success ) + { + outConnection->inSocketState = NodeConnection_Pending_Up; + outConnection->mostConnectionLabel = connectionLabel; + } + } + } + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"CNetwork::SetSinkToChannel failed to resolve given MAC addresses"RESETCOLOR"\n" ); + } + PRINT_ALL_INFOS(); + return success; +} + +void CNetwork::RaiseUknownConnection( uint32_t devInstance, uint16_t nodeAddress, EPDataType_t dType ) +{ + CNodeEntry *entry = CNodeEntry::GetNodeEntry( devInstance, nodeAddress ); + CNodeConnectionEntry *connection = entry->GetConnectionByChannelId( 0xFF, true ); + entry->macAddress = new CMacAddr( devInstance, nodeAddress ); + connection->channelConfig = new CChannelConfiguration(); + connection->channelConfig->inSocket.type = dType; + connection->channelConfig->outSocket.type = dType; + RaiseAvailable( connection ); +} + +void CNetwork::ShutdownMost( CNetworkDevice *device ) +{ + if( NULL == device ) + return; + device->ClearAllPendingActions(); + device->SetNetstate( NetworkState_ShutdownInProgress ); + device->MostNetworkShutdownV3(); +} + +void CNetwork::ShutdownMostBecauseOfErrors( CNetworkDevice *device ) +{ + if (retryCounter < 5) + { + retryExecTime = GetTickCount(); + retryCounter++; + ConsolePrintf( + PRIO_ERROR, RED"Restart MOST because of resources allocation /"\ + " configuration problems, retry count=%d"RESETCOLOR"\n", retryCounter ); + ShutdownMost( device ); + } +} \ No newline at end of file diff --git a/Src/Network/NodeDatabase.cpp b/Src/Network/NodeDatabase.cpp new file mode 100644 index 0000000..d53d2d1 --- /dev/null +++ b/Src/Network/NodeDatabase.cpp @@ -0,0 +1,287 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include "Console.h" +#include "NodeDatabase.h" + +CSafeVector CNodeEntry::allNodes; + +CNodeConnectionEntry::CNodeConnectionEntry( CNodeEntry *parent ) : parentNode( parent ), channelConfig( NULL ), + channelId( 0xFFFFFFFF ), inHandle( 0xFFFFFFFF ), + outHandle( 0xFFFFFFFF ), + connectHandle( 0xFFFFFFFF ), + splittedSourceHandle( 0xFFFFFFFF ), + mostConnectionLabel( 0xFFFFFFFF ), + inSocketState( NodeConnection_NotUsed ), + outSocketState( NodeConnection_NotUsed ), + connectedSocketState( NodeConnection_NotUsed ), + bufferSize( -1 ) +{ + memset( deviceName, 0, sizeof( deviceName ) ); +}; + +CNodeConnectionEntry::~CNodeConnectionEntry() +{ + if( NULL != channelConfig ) + { + delete channelConfig; + channelConfig = NULL; + } +} + +CNodeEntry::CNodeEntry() : deviceInstance( 0xFF ), nodeAddress( 0xFFFF ), deviceType( 0xFFFF ), macAddress( NULL ), + isTsiPortOpened(false), isMlbPortOpened( false ), isUsbPortOpened( false ), isI2SPortOpened( false ) +{ +} + +CNodeEntry::~CNodeEntry() +{ + if( NULL != macAddress ) + delete macAddress; +} + +uint16_t CNodeEntry::GetAmountOfNodeEntries() +{ + return allNodes.Size(); +} + +CNodeEntry *CNodeEntry::GetNodeEntry( uint16_t index ) +{ + return allNodes[index]; +} + +CNodeEntry *CNodeEntry::GetNodeEntry( uint8_t deviceInstance, uint16_t nodeAddress ) +{ + CNodeEntry *entry = NULL; + for( uint32_t i = 0; i < allNodes.Size(); i++ ) + { + if( ( allNodes[i]->deviceInstance == deviceInstance ) + && ( allNodes[i]->nodeAddress == nodeAddress ) ) + { + entry = allNodes[i]; + break; + } + } + if( NULL == entry ) + { + entry = new CNodeEntry(); + entry->deviceInstance = deviceInstance; + entry->nodeAddress = nodeAddress; + allNodes.PushBack( entry ); + } + return entry; +} + +CNodeEntry *CNodeEntry::GetNodeEntry( CMacAddr *macAddress ) +{ + if( NULL == macAddress ) + return NULL; + CNodeEntry *entry = NULL; + for( uint32_t i = 0; i < allNodes.Size(); i++ ) + { + CNodeEntry *node = allNodes[i]; + if( ( NULL != node->macAddress ) && ( *node->macAddress == *macAddress ) ) + { + entry = node; + break; + } + } + return entry; +} + +CNodeEntry *CNodeEntry::GetNodeEntry( uint8_t deviceInstance, uint32_t deviceType, uint32_t terminalInstance ) +{ + uint32_t inst = 0; + CNodeEntry *entry = NULL; + for( uint32_t i = 0; i < allNodes.Size(); i++ ) + { + CNodeEntry *node = allNodes[i]; + if( ( allNodes[i]->deviceInstance == deviceInstance ) + && ( allNodes[i]->deviceType == deviceType ) ) + { + if( inst == terminalInstance ) + { + entry = node; + break; + } + else + { + ++inst; + } + } + } + return entry; +} + +uint32_t CNodeEntry::GetTerminalInstance( uint8_t deviceInstance, uint16_t nodeAddress, uint32_t deviceType ) +{ + uint32_t terminalInst = 0; + for( uint32_t i = 0; i < allNodes.Size(); i++ ) + { + if( ( allNodes[i]->deviceInstance == deviceInstance ) + && ( allNodes[i]->deviceType == deviceType ) ) + { + if( ( allNodes[i]->nodeAddress == nodeAddress ) ) + break; + else + ++terminalInst; + } + } + return terminalInst; +} + +void CNodeEntry::DeleteAllNodeEntries() +{ + allNodes.RemoveAll(true); +} + +void CNodeEntry::DeleteNodeEntry( uint16_t index ) +{ + CNodeEntry *ent = allNodes[index]; + if( NULL == ent ) + return; + + allNodes.Remove(ent); + delete ent; +} + +const char *CNodeEntry::GetConnectionStateString( NodeConnectionState_t conState ) +{ + switch( conState ) + { + case NodeConnection_NotUsed: + return "Idle"; + case NodeConnection_Pending_Up: + return "Pending-Up"; + case NodeConnection_Pending_Down: + return "Pending-Down"; + case NodeConnection_Used: + return "Established"; + default: + return "Unknown State"; + } +} + +void CNodeEntry::PrintAllInformations() +{ + ConsolePrintfStart( PRIO_LOW, "==================================================================\n" ); + for( uint32_t i = 0; i < allNodes.Size(); i++ ) + { + CNodeEntry *entry = allNodes[i]; + ; + if( NULL != entry ) + { + ConsolePrintfContinue( "Inst:%d, NodeAddr:0x%X, DevType:0x%X, MAC:%s, open:%d\n", entry->deviceInstance, + entry->nodeAddress, entry->deviceType, ( NULL != entry->macAddress ? entry->macAddress->ToString() : + "None" ), ( entry->isUsbPortOpened || entry->isMlbPortOpened ) ); + for( uint32_t j = 0; j < entry->GetAmountOfConnections(); j++ ) + { + CNodeConnectionEntry *con = entry->GetConnectionByIndex( j ); + ConsolePrintfContinue( + "Id:%d, inHandle:0x%X, outHandle:0x%X, conHandle:0x%X, conLabel:0x%X, inState:%s, outState:%s, conState:%s, bufferSize:%d, name:%s\n", con->channelId, con->inHandle, con->outHandle, con->connectHandle, con->mostConnectionLabel, GetConnectionStateString( con->inSocketState ), GetConnectionStateString( con->outSocketState ), GetConnectionStateString( con->connectedSocketState ), con->bufferSize, con->deviceName ); + } + } + if( i < allNodes.Size() - 1 ) + ConsolePrintfContinue( "------------------------------------------------------------------\n" ); + } + ConsolePrintfExit( "==================================================================\n" ); +} + +uint16_t CNodeEntry::GetAmountOfConnections() +{ + return allConnections.Size(); +} + +CNodeConnectionEntry *CNodeEntry::GetConnectionByIndex( uint16_t index ) +{ + return allConnections[index]; +} + +CNodeConnectionEntry *CNodeEntry::GetConnectionByChannelId( uint32_t channelId, bool createNewIfUnknown ) +{ + CNodeConnectionEntry *entry = NULL; + for( uint32_t i = 0; i < allConnections.Size(); i++ ) + { + if( allConnections[i]->channelId == channelId ) + { + entry = allConnections[i]; + break; + } + } + if( createNewIfUnknown && NULL == entry ) + { + entry = new CNodeConnectionEntry( this ); + entry->channelId = channelId; + allConnections.PushBack( entry ); + } + return entry; +} + +CNodeConnectionEntry *CNodeEntry::GetConflictingConnection( uint32_t channelId, CNodeEntry *opposite ) +{ + if( NULL == opposite ) + return NULL; + if( opposite->deviceInstance != deviceInstance ) + return NULL; + CNodeConnectionEntry *entry = NULL; + uint32_t connectionLabel = 0xFFFFFFFF; + for( uint32_t i = 0; i < allConnections.Size(); i++ ) + { + if( allConnections[i]->channelId == channelId ) + { + connectionLabel = allConnections[i]->mostConnectionLabel; + break; + } + } + if( 0xFFFFFFFF != connectionLabel ) + { + for( uint32_t i = 0; i < opposite->allConnections.Size(); i++ ) + { + if( opposite->allConnections[i]->mostConnectionLabel == connectionLabel ) + { + entry = opposite->allConnections[i]; + break; + } + } + } + return entry; +} + +void CNodeEntry::DeleteConnection( uint32_t channelId ) +{ + uint32_t index = 0xFFFFFFFF; + for( uint32_t i = 0; i < allConnections.Size(); i++ ) + { + if( allConnections[i]->channelId == channelId ) + { + index = i; + } + } + if( index >= allNodes.Size() ) + return; + CNodeConnectionEntry *ent = allConnections[index]; + allConnections.Remove(ent); + delete ent; +} diff --git a/Src/Network/NodeDatabase.h b/Src/Network/NodeDatabase.h new file mode 100644 index 0000000..16eb863 --- /dev/null +++ b/Src/Network/NodeDatabase.h @@ -0,0 +1,379 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CNodeEntry and the CNodeConnectionEntry class. + */ +/*----------------------------------------------------------*/ +#ifndef NODEDATABASE_H +#define NODEDATABASE_H + +#include +#include +#include "SafeVector.h" +#include "VodXml.h" +#include "MacAddr.h" + +class CNodeEntry; +class CNodeConnectionEntry; + +/*----------------------------------------------------------*/ +/*! \brief Enumeration describing the state of a Node socket + */ +/*----------------------------------------------------------*/ +typedef enum NodeConnectionState_tag +{ + NodeConnection_NotUsed, + NodeConnection_Pending_Up, + NodeConnection_Pending_Down, + NodeConnection_Used +} NodeConnectionState_t; + +/*----------------------------------------------------------*/ +/*! \brief Storage class holding informations of a specific MOST connection (Ports) + */ +/*----------------------------------------------------------*/ +class CNodeConnectionEntry +{ +public: + /*----------------------------------------------------------*/ + /*! \brief Pointer to the device, which this connection belongs to. + */ + /*----------------------------------------------------------*/ + CNodeEntry *parentNode; + + + /*----------------------------------------------------------*/ + /*! \brief Pointer to the XML generated configuration, holding the user specific part of this connection. + */ + /*----------------------------------------------------------*/ + CChannelConfiguration *channelConfig; + + + /*----------------------------------------------------------*/ + /*! \brief Identifier determinating the connection based on the XML configuration. + */ + /*----------------------------------------------------------*/ + uint32_t channelId; + + + /*----------------------------------------------------------*/ + /*! \brief In-Handle provided from INIC when the In-Socket was created + */ + /*----------------------------------------------------------*/ + uint32_t inHandle; + + + /*----------------------------------------------------------*/ + /*! \brief Out-Handle provided from INIC when the Out-Socket was created + */ + /*----------------------------------------------------------*/ + uint32_t outHandle; + + + /*----------------------------------------------------------*/ + /*! \brief Connection-Handle provided from INIC when the In- and the Out-Socket were connected. + */ + /*----------------------------------------------------------*/ + uint32_t connectHandle; + + + /*----------------------------------------------------------*/ + /*! \brief If this connection uses combiner or splitter, this handle points to the source / sink of the splitter / combiner. + * \note The idea of storing this handle, is only for cleanup purposes. + * \warning Do not use this handle for creating connections. + */ + /*----------------------------------------------------------*/ + uint32_t splittedSourceHandle; + + + /*----------------------------------------------------------*/ + /*! \brief Connection-Handle provided from INIC when the MOST-Socket was created. + */ + /*----------------------------------------------------------*/ + uint32_t mostConnectionLabel; + + + /*----------------------------------------------------------*/ + /*! \brief The state of the In-Socket + */ + /*----------------------------------------------------------*/ + NodeConnectionState_t inSocketState; + + + /*----------------------------------------------------------*/ + /*! \brief The state of the Out-Socket + */ + /*----------------------------------------------------------*/ + NodeConnectionState_t outSocketState; + + + /*----------------------------------------------------------*/ + /*! \brief The connection state between In-Socket and Out-Socket + */ + /*----------------------------------------------------------*/ + NodeConnectionState_t connectedSocketState; + + /*----------------------------------------------------------*/ + /*! \brief The used buffer size for this connection. + * \note bufferSize is -1 in case, there were no buffers configured. + */ + /*----------------------------------------------------------*/ + int32_t bufferSize; + + /*----------------------------------------------------------*/ + /*! \brief The Linux character device name, when this connection belongs to a local connection. May be strlen of 0. + */ + /*----------------------------------------------------------*/ + char deviceName[64]; + + + /*----------------------------------------------------------*/ + /*! \brief Constructor of CNodeConnectionEntry. + * \param Pointer to the device this connection belongs to. + */ + /*----------------------------------------------------------*/ + CNodeConnectionEntry( CNodeEntry *parentNode ); + + + /*----------------------------------------------------------*/ + /*! \brief Destructor of CNodeConnectionEntry. + */ + /*----------------------------------------------------------*/ + ~CNodeConnectionEntry(); +}; + +/*----------------------------------------------------------*/ +/*! \brief Storage class holding information of a specific MOST device + */ +/*----------------------------------------------------------*/ +class CNodeEntry +{ +private: + static CSafeVector allNodes; + CSafeVector allConnections; + + CNodeEntry(); + static const char *GetConnectionStateString( NodeConnectionState_t state ); +public: + ~CNodeEntry(); + + /*----------------------------------------------------------*/ + /*! \brief MOST instance. 0 for the first instance. If the server has multiple MOST devices, this value will be increased. + */ + /*----------------------------------------------------------*/ + uint8_t deviceInstance; + + + /*----------------------------------------------------------*/ + /*! \brief The unique address of the MOST device. + */ + /*----------------------------------------------------------*/ + uint16_t nodeAddress; + + + /*----------------------------------------------------------*/ + /*! \brief The type of the device (derived from the group address), as described in the XML configuration. + */ + /*----------------------------------------------------------*/ + uint16_t deviceType; + + + /*----------------------------------------------------------*/ + /*! \brief The unique MAC address of the device. + */ + /*----------------------------------------------------------*/ + CMacAddr *macAddress; + + + /*----------------------------------------------------------*/ + /*! \brief Determines if the TSI port is opened. + * \return true, if the port is opened. false + */ + /*----------------------------------------------------------*/ + bool isTsiPortOpened; + + /*----------------------------------------------------------*/ + /*! \brief Determines if the MLB port is opened. + * \return true, if the port is opened. false + */ + /*----------------------------------------------------------*/ + bool isMlbPortOpened; + + + /*----------------------------------------------------------*/ + /*! \brief Determines if the USB port is opened. + * \return true, if the port is opened. false + */ + /*----------------------------------------------------------*/ + bool isUsbPortOpened; + + + /*----------------------------------------------------------*/ + /*! \brief Determines if the USB port A is opened. + * \return true, if the port is opened. false + */ + /*----------------------------------------------------------*/ + bool isI2SPortOpened; + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of available MOST devices. + * \return The number of MOST devices. + */ + /*----------------------------------------------------------*/ + static uint16_t GetAmountOfNodeEntries(); + + + /*----------------------------------------------------------*/ + /*! \brief Gets an instance of CNodeEntry by the given instance index + * \param index - starting at 0 for the first instance. "GetAmountOfNodeEntries() - 1" for the last instance. + * \return The requested instance, when successful. Otherwise NULL pointer. + */ + /*----------------------------------------------------------*/ + static CNodeEntry *GetNodeEntry( uint16_t index ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets an instance of CNodeEntry by the given MOST instance and the unique node address of the device. + * \param deviceInstance - MOST instance id, starting at 0 for the first MOST ring. When the server have multiple MOST devices, + * This value may be above 0. + * \param nodeAddress - The MOST node address of the specific device (0x100 e.g.). + * \return The requested instance, when successful. Otherwise NULL pointer. + */ + /*----------------------------------------------------------*/ + static CNodeEntry *GetNodeEntry( uint8_t deviceInstance, uint16_t nodeAddress ); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets an instance of CNodeEntry by the given unique MAC address of the device. + * \param macAddress - Unique MAC address of the device + * \return The requested instance, when successful. Otherwise NULL pointer. + */ + /*----------------------------------------------------------*/ + static CNodeEntry *GetNodeEntry( CMacAddr *macAddress ); + + /*----------------------------------------------------------*/ + /*! \brief Gets an instance of CNodeEntry by the given device type and terminal instance + * \param deviceInstance - MOST instance id, starting at 0 for the first MOST ring. When the server have multiple MOST devices, + * This value may be above 0. + * \param deviceType - The type of the device (derived from the group address), as described in the XML configuration. + * \param terminalInstance - The instance of the terminal, starting at 0 for the first terminal. + * + * \return The requested instance, when successful. Otherwise NULL pointer. + */ + /*----------------------------------------------------------*/ + static CNodeEntry *GetNodeEntry( uint8_t deviceInstance, uint32_t deviceType, uint32_t terminalInstance ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets the terminal instance number for the given device type. + * \param deviceInstance - MOST instance id, starting at 0 for the first MOST ring. When the server have multiple MOST devices, + * This value may be above 0. + * \param nodeAddress - The MOST node address of the specific device (0x100 e.g.). + * \param deviceType - The type of the device (derived from the group address), as described in the XML configuration. + * + * \return The requested instance, when successful. Otherwise NULL pointer. + */ + /*----------------------------------------------------------*/ + static uint32_t GetTerminalInstance( uint8_t deviceInstance, uint16_t nodeAddress, uint32_t deviceType ); + + /*----------------------------------------------------------*/ + /*! \brief Deleting all informations of any device. + */ + /*----------------------------------------------------------*/ + static void DeleteAllNodeEntries(); + + + + /*----------------------------------------------------------*/ + /*! \brief Deleting all informations of a specific device. + * \param index - starting at 0 for the first instance. "GetAmountOfNodeEntries() - 1" for the last instance. + */ + /*----------------------------------------------------------*/ + static void DeleteNodeEntry( uint16_t index ); + + + + /*----------------------------------------------------------*/ + /*! \brief Dumps out all information of all devices to stdout + */ + /*----------------------------------------------------------*/ + static void PrintAllInformations(); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of connection for the current instance of MOST device. + * \return The amount of connections. + */ + /*----------------------------------------------------------*/ + uint16_t GetAmountOfConnections(); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets an instance of CNodeConnectionEntry by the given instance index. + * \param index - starting at 0 for the first instance. "GetAmountOfConnections() - 1" for the last instance. + * \return The requested instance, when successful. Otherwise NULL pointer. + */ + /*----------------------------------------------------------*/ + CNodeConnectionEntry *GetConnectionByIndex( uint16_t index ); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets an instance of CNodeConnectionEntry by the given channel identifier. + * \param channelId - The channel identifier as specified in the XML file. + * \param createNewIfUnknown - TRUE, if this function shall create a new entry, when there is no existing object. + * \return The requested instance, when successful. Otherwise NULL pointer. + */ + /*----------------------------------------------------------*/ + CNodeConnectionEntry *GetConnectionByChannelId( uint32_t channelId, bool createNewIfUnknown ); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves a conflicting connection by the given parameters.# + * This method checks if the in and out configurations are unique for one device. + * \param channelId - The channel identifier as specified in the XML file. + * \param opposite - The other side to be connected to. + * \return The instance, which causes an conflict. If there is no conflict, a NULL pointer will be returned. + */ + /*----------------------------------------------------------*/ + CNodeConnectionEntry *GetConflictingConnection( uint32_t channelId, CNodeEntry *opposite ); + + + + /*----------------------------------------------------------*/ + /*! \brief Delets all informations of the given channel identifier. + * \param channelId - The channel identifier as specified in the XML file. + */ + /*----------------------------------------------------------*/ + void DeleteConnection( uint32_t channelId ); +}; + +#endif //NODEDATABASE_H diff --git a/Src/Network/base/.svn/dir-prop-base b/Src/Network/base/.svn/dir-prop-base new file mode 100644 index 0000000..c4fbe3b --- /dev/null +++ b/Src/Network/base/.svn/dir-prop-base @@ -0,0 +1,14 @@ +K 16 +bugtraq:logregex +V 67 +(?:[Bb]ugs?|[Ii]ssues?|[Rr]eports?)+\s+#(?:(?:\d+)[#,\.\s]*)+ +(\d+) +K 15 +bugtraq:message +V 21 +Mantis issue #%BUGID% +K 11 +bugtraq:url +V 33 +http://mantis/view.php?id=%BUGID% +END diff --git a/Src/Network/base/.svn/entries b/Src/Network/base/.svn/entries new file mode 100644 index 0000000..eba0b2c --- /dev/null +++ b/Src/Network/base/.svn/entries @@ -0,0 +1,164 @@ +10 + +dir +6611 +svn://kar-vm-svn01.mchp-main.com/svn/AppsTeam/Samples/iMX6+OS81118/Industrial/VideoOnDemand/tags/V3.0.4/source/Examples/NetworkManager/Server/Src/Network/base +svn://kar-vm-svn01.mchp-main.com/svn/AppsTeam + + + +2016-03-08T10:11:00.246092Z +5263 +tkummermehr +has-props + + + + + + + + + + + + + +c50209ca-6af6-4c1a-9d47-4b11eae79d1c + +Board.c +file + + + + +2016-11-29T13:32:01.206172Z +002b1b0937a1a1653ff724364be62050 +2016-03-08T10:11:00.246092Z +5263 +tkummermehr + + + + + + + + + + + + + + + + + + + + + +1685 + +DriverConfiguration.h +file + + + + +2016-11-29T13:32:01.206172Z +05273e72ca5b7af8c2f55a35fcdaa5a0 +2015-12-17T09:32:42.832988Z +5040 +ashvetsov +has-props + + + + + + + + + + + + + + + + + + + + +11839 + +Board.h +file + + + + +2016-11-29T13:32:01.206172Z +40cb461e8d0039b063b790d626d37f1a +2016-03-08T10:11:00.246092Z +5263 +tkummermehr + + + + + + + + + + + + + + + + + + + + + +1799 + +DriverConfiguration.c +file + + + + +2016-11-29T13:32:01.202172Z +b8e55c1b0e2437a8513afd32ae4f5446 +2015-12-17T09:32:42.832988Z +5040 +ashvetsov +has-props + + + + + + + + + + + + + + + + + + + + +18468 + diff --git a/Src/Network/base/.svn/prop-base/DriverConfiguration.c.svn-base b/Src/Network/base/.svn/prop-base/DriverConfiguration.c.svn-base new file mode 100644 index 0000000..3160658 --- /dev/null +++ b/Src/Network/base/.svn/prop-base/DriverConfiguration.c.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mergeinfo +V 0 + +END diff --git a/Src/Network/base/.svn/prop-base/DriverConfiguration.h.svn-base b/Src/Network/base/.svn/prop-base/DriverConfiguration.h.svn-base new file mode 100644 index 0000000..3160658 --- /dev/null +++ b/Src/Network/base/.svn/prop-base/DriverConfiguration.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mergeinfo +V 0 + +END diff --git a/Src/Network/base/.svn/text-base/Board.c.svn-base b/Src/Network/base/.svn/text-base/Board.c.svn-base new file mode 100644 index 0000000..50daf0c --- /dev/null +++ b/Src/Network/base/.svn/text-base/Board.c.svn-base @@ -0,0 +1,51 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file */ +/*! \brief Base Board initialisation */ +/*----------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "Board.h" +#include "Console.h" + +uint32_t GetTickCount( void ) +{ + struct timespec currentTime; + if (clock_gettime(CLOCK_MONOTONIC_RAW, ¤tTime)) + { + ConsolePrintf( PRIO_ERROR, RED"GetTickCount failed!!"RESETCOLOR"\n" ); + return 0; + } + return ( currentTime.tv_sec * 1000 ) + ( currentTime.tv_nsec / 1000000 ); +} + +uint16_t GetTickCountWord() +{ + return ( uint16_t )( GetTickCount() & UINT16_MAX ); +} diff --git a/Src/Network/base/.svn/text-base/Board.h.svn-base b/Src/Network/base/.svn/text-base/Board.h.svn-base new file mode 100644 index 0000000..585a862 --- /dev/null +++ b/Src/Network/base/.svn/text-base/Board.h.svn-base @@ -0,0 +1,63 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This component abstracts platform specific functionalities. + */ +/*----------------------------------------------------------*/ +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + /*----------------------------------------------------------*/ + /*! \brief reads the ms tick counter. + * + * \return value of the ms counter. + */ + /*----------------------------------------------------------*/ + uint32_t GetTickCount( void ); + + + + + /*----------------------------------------------------------*/ + /*! \brief reads the ms tick counter + * + * \return value of the ms counter as word + */ + /*----------------------------------------------------------*/ + uint16_t GetTickCountWord( void ); + + +#ifdef __cplusplus +} +#endif + +#endif // _BOARD_H_ diff --git a/Src/Network/base/.svn/text-base/DriverConfiguration.c.svn-base b/Src/Network/base/.svn/text-base/DriverConfiguration.c.svn-base new file mode 100644 index 0000000..7a98d25 --- /dev/null +++ b/Src/Network/base/.svn/text-base/DriverConfiguration.c.svn-base @@ -0,0 +1,608 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include +#include +#include "DriverConfiguration.h" +#include "Console.h" + +static bool WriteCharactersToFile( const char *pFileName, const char *pString ) +{ + bool success = false; + FILE *fh = fopen( pFileName, "a" ); + if( NULL != fh ) + { + int result = fputs( pString, fh ); + if( result >= 0 ) + fputc( '\n', fh ); + if( result >= 0 ) + success = true; + fclose( fh ); + } + if( success ) + ConsolePrintf( PRIO_MEDIUM, "*** configured device: '%s = %s', success:%d\n", pFileName, pString, success ); + else + ConsolePrintf( PRIO_ERROR, RED"DriverConfiguration.WriteCharactersToFile failed for file '%s', errno:'%s'"RESETCOLOR"\n", + pFileName, GetErrnoString() ); + return success; +} + +static bool WriteIntegerToFile( const char *pFileName, int intValue ) +{ + char tempBuffer[16]; + snprintf( tempBuffer, sizeof( tempBuffer ), "%d", intValue ); + return WriteCharactersToFile( pFileName, tempBuffer ); +} + +static bool ReadFromFile( const char *pFileName, char *pString, uint16_t bufferLen ) +{ + bool success = false; + if( NULL == pString || 0 == bufferLen ) + return success; + FILE *fh = fopen( pFileName, "r" ); + if( NULL != fh ) + { + success = ( NULL != fgets( pString, bufferLen, fh ) ); + fclose( fh ); + } + if( !success ) + ConsolePrintf( PRIO_ERROR, RED"DriverConfiguration.ReadFromFile failed for file '%s', errno:'%s'"RESETCOLOR"\n", + pFileName, GetErrnoString() ); + return success; +} + +static bool ExistsDevice( const char *pDeviceName ) +{ + struct stat buffer; + return ( stat( pDeviceName, &buffer ) == 0 ); +} + +static bool WaitForDevice( const char *pDeviceName ) +{ + int timeout; + bool deviceExists = false; + for( timeout = 0; timeout < 40; timeout++ ) + { + deviceExists = ExistsDevice( pDeviceName ); + if( deviceExists ) + { + break; + } + else + { + usleep( 2500 ); + } + } + if( !deviceExists ) + ConsolePrintf( PRIO_ERROR, RED"Waiting for device '%s' to appear, timed out"RESETCOLOR"\n", + pDeviceName ); + return deviceExists; +} + +static bool GetDeviceDescription( uint8_t deviceInstance, char *deviceString, uint32_t stringLen ) +{ + bool descriptionFound = false; + char descriptionPath[64]; + char descriptionValue[32]; + + if( NULL == deviceString ) + return descriptionFound; + + snprintf( descriptionPath, sizeof( descriptionPath ), + "/sys/devices/virtual/most/mostcore/devices/mdev%d/description", deviceInstance ); + + if( !ExistsDevice( descriptionPath ) ) + return descriptionFound; + + descriptionFound = ReadFromFile( descriptionPath, descriptionValue, sizeof( descriptionValue ) ); + if( descriptionFound ) + { + strncpy( deviceString, descriptionValue, stringLen ); + } + return descriptionFound; +} + +static bool ExistsDeviceWithType( uint8_t deviceInstance, const char *deviceString, uint32_t stringLen ) +{ + bool deviceFound = false; + char interfacePath[64]; + char interfaceValue[32]; + + if( NULL == deviceString ) + return deviceFound; + + snprintf( interfacePath, sizeof( interfacePath ), "/sys/devices/virtual/most/mostcore/devices/mdev%d/interface", + deviceInstance ); + + if( !ExistsDevice( interfacePath ) ) + return deviceFound; + + deviceFound = ReadFromFile( interfacePath, interfaceValue, sizeof( interfaceValue ) ); + if( deviceFound ) + { + deviceFound = ( 0 == strncmp( interfaceValue, deviceString, stringLen ) ); + } + return deviceFound; +} + +static bool GetAlsaConfiguration( uint16_t subBufferSize, uint8_t *amountOfChannels, uint8_t *bitDepth ) +{ + if( NULL == amountOfChannels || NULL == bitDepth ) + { + ConsolePrintf( PRIO_ERROR, RED"GetAlsaConfiguration was called with invalid parameters"RESETCOLOR"\n" ); + return false; + } + switch( subBufferSize ) + { + case 2: + *amountOfChannels = 1; + *bitDepth = 16; + break; + case 4: + *amountOfChannels = 2; + *bitDepth = 16; + break; + case 6: + *amountOfChannels = 2; + *bitDepth = 24; + break; + case 8: + *amountOfChannels = 2; + *bitDepth = 32; + break; + case 12: + *amountOfChannels = 6; + *bitDepth = 16; + break; + case 18: + *amountOfChannels = 6; + *bitDepth = 24; + break; + case 16: + *amountOfChannels = 8; + *bitDepth = 16; + break; + case 24: + *amountOfChannels = 8; + *bitDepth = 24; + break; + default: + ConsolePrintf( PRIO_ERROR, RED"Configure ALSA device was called"\ + " with unknown sub-buffer size: %d"RESETCOLOR"\n", subBufferSize ); + return false; + } + return true; +} + +bool ExistsUsbDeviceInstance( uint8_t deviceInstance ) +{ + uint32_t i; + bool found = false; + uint8_t curDevInst = 0; + char lastDescr[64]; + lastDescr[0] = '\0'; + for( i = 0; i < 12; i++ ) + { + uint32_t curDescrLen; + char curDescr[64]; + if( !ExistsDeviceWithType( i, "usb", 3 ) ) + continue; + + if( !GetDeviceDescription( i, curDescr, sizeof( curDescr ) ) ) + continue; + + curDescrLen = strnlen( curDescr, sizeof( curDescr ) ); + if( curDescrLen <= 2 ) + continue; + + //Cut away the last two characters, as they are different for the 3 interfaces of INIC + curDescrLen -= 2; + + if( 0 == strncmp( curDescr, lastDescr, curDescrLen ) ) + continue; + + strncpy( lastDescr, curDescr, curDescrLen ); + lastDescr[curDescrLen] = '\0'; + + if( curDevInst++ != deviceInstance ) + continue; + + found = true; + break; + } + return found; +} + +bool ExistsMlbDeviceInstance( uint8_t deviceInstance ) +{ + return ExistsDeviceWithType( deviceInstance, "mlb", 3 ); +} + +bool ExistsI2CDeviceInstance( uint8_t deviceInstance ) +{ + return ExistsDeviceWithType( deviceInstance, "i2c", 3 ); +} + +bool GetUsbDeviceNames( uint8_t deviceInstance, uint8_t endpointAddress, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ) +{ + uint32_t i; + bool systemFound = false; + uint8_t curDevInst = 0; + char lastDescr[64]; + lastDescr[0] = '\0'; + char endpointBuffer[16]; + char systemSourceDir[64]; + DIR *d; + struct dirent *dir; + + if( NULL == deviceName || NULL == systemName || NULL == linkName ) + return false; + + deviceName[0] = '\0'; + systemName[0] = '\0'; + + for( i = 0; !systemFound && i < 12; i++ ) + { + uint32_t curDescrLen; + char curDescr[64]; + if( !ExistsDeviceWithType( i, "usb", 3 ) ) + continue; + + if( !GetDeviceDescription( i, curDescr, sizeof( curDescr ) ) ) + continue; + + curDescrLen = strnlen( curDescr, sizeof( curDescr ) ); + if( curDescrLen <= 2 ) + continue; + + //Cut away the last two characters, as they are different for the 3 interfaces of INIC + curDescrLen -= 2; + + if( ( '\0' != lastDescr[0] ) + && ( 0 != strncmp( curDescr, lastDescr, curDescrLen ) ) ) + { + ++curDevInst; + } + + strncpy( lastDescr, curDescr, curDescrLen ); + lastDescr[curDescrLen] = '\0'; + + if( curDevInst < deviceInstance ) + continue; + else if( curDevInst > deviceInstance ) + break; + + snprintf( endpointBuffer, sizeof( endpointBuffer ), "ep%02x", endpointAddress ); + snprintf( systemSourceDir, sizeof( systemSourceDir ), "/sys/devices/virtual/most/mostcore/devices/mdev%d", i ); + d = opendir( systemSourceDir ); + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + if( strstr( dir->d_name, endpointBuffer ) ) + { + snprintf( systemName, systemNameLength, "%s/%s", systemSourceDir, dir->d_name ); + snprintf( deviceName, deviceNameLength, "/dev/mdev%d-%s", deviceInstance, dir->d_name ); + snprintf( linkName, linkNameLength, "mdev%d:%s:mdev%d-%s", i, dir->d_name, deviceInstance, + dir->d_name ); + systemFound = true; + break; + } + } + closedir( d ); + } + } + return systemFound; +} + +bool GetMlbDeviceNames( uint8_t deviceInstance, uint8_t mlbChannelAddress, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ) +{ + bool systemFound = false; + char channelBuffer[16]; + char systemSourceDir[64]; + DIR *d; + struct dirent *dir; + + if( NULL == deviceName || NULL == systemName || NULL == linkName ) + return false; + + deviceName[0] = '\0'; + systemName[0] = '\0'; + + snprintf( channelBuffer, sizeof( channelBuffer ), "ca%d", mlbChannelAddress ); + snprintf( systemSourceDir, sizeof( systemSourceDir ), "/sys/devices/virtual/most/mostcore/devices/mdev%d", + deviceInstance ); + d = opendir( systemSourceDir ); + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + if( strstr( dir->d_name, channelBuffer ) ) + { + snprintf( systemName, systemNameLength, "%s/%s", systemSourceDir, dir->d_name ); + snprintf( deviceName, deviceNameLength, "/dev/mdev%d-%s", deviceInstance, dir->d_name ); + snprintf( linkName, linkNameLength, "mdev%d:%s:mdev%d-%s", deviceInstance, dir->d_name, deviceInstance, + dir->d_name ); + systemFound = true; + break; + } + } + closedir( d ); + } + return systemFound; +} + +bool GetI2CDeviceNames( uint8_t deviceInstance, bool isTx, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ) +{ + bool systemFound = false; + char systemSourceDir[64]; + DIR *d; + struct dirent *dir; + + if( NULL == deviceName || NULL == systemName || NULL == linkName ) + return false; + + deviceName[0] = '\0'; + systemName[0] = '\0'; + + snprintf( systemSourceDir, sizeof( systemSourceDir ), "/sys/devices/virtual/most/mostcore/devices/mdev%d", + deviceInstance ); + d = opendir( systemSourceDir ); + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + if( strstr( dir->d_name, ( isTx ? "tx" : "rx" ) ) ) + { + snprintf( systemName, systemNameLength, "%s/%s", systemSourceDir, dir->d_name ); + snprintf( deviceName, deviceNameLength, "/dev/mdev%d-%s", deviceInstance, dir->d_name ); + snprintf( linkName, linkNameLength, "mdev%d:%s:mdev%d-%s", deviceInstance, dir->d_name, deviceInstance, + dir->d_name ); + systemFound = true; + break; + } + } + closedir( d ); + } + return systemFound; +} + +bool CloseMostChannel( const char *device ) +{ + return true; +} + +bool ConfigureMostChannel( const char *device, EPDataType_t mostType, EPDirection_t direction, uint32_t numBuf, + uint32_t bufSize ) +{ + static const char *controlType = "control"; + static const char *asyncType = "async"; + static const char *syncType = "sync"; + static const char *isocType = "isoc_avp"; + static const char *rxDirection = "dir_rx"; + static const char *txDirection = "dir_tx"; + bool success = true; + char tempBuffer[128]; + const char *typeString = NULL; + const char *directionString = NULL; + + switch( mostType ) + { + case EP_Control: + typeString = controlType; + break; + case EP_Asynchron: + typeString = asyncType; + break; + case EP_Synchron: + typeString = syncType; + break; + case EP_Isochron: + typeString = isocType; + break; + default: + return false; + } + + switch( direction ) + { + case EPDIR_IN: + directionString = txDirection; + break; + case EPDIR_OUT: + directionString = rxDirection; + break; + default: + return false; + } + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_datatype", device ); /// Setting data type + success = WriteCharactersToFile( tempBuffer, typeString ); + if( success ) + { + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_direction", device ); /// Setting direction + success = WriteCharactersToFile( tempBuffer, directionString ); + } + if( success ) + { + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_number_of_buffers", device ); /// Setting amount of buffers + success = WriteIntegerToFile( tempBuffer, numBuf ); + } + if( success ) + { + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_buffer_size", device ); /// Setting amount of buffers + success = WriteIntegerToFile( tempBuffer, bufSize ); + } + return success; +} + +bool ConfigureIsocChannel( const char *device, uint32_t subbufferSize, uint32_t packetsPerTransaction ) +{ + char tempBuffer[128]; + bool success; + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_subbuffer_size", device ); /// Setting the subbuffer size in bytes + success = WriteIntegerToFile( tempBuffer, subbufferSize ); + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_packets_per_xact", device ); /// Setting the packets per transaction + success = WriteIntegerToFile( tempBuffer, ( packetsPerTransaction & 0xFF ) ); + return success; +} + +bool ConfigureSyncChannel( const char *device, uint32_t syncBlockWidth, uint32_t amounOfSyncFrames ) +{ + char tempBuffer[128]; + bool success; + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_packets_per_xact", device ); /// Setting the amount of frames in one USB frame + success = WriteIntegerToFile( tempBuffer, amounOfSyncFrames ); + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_subbuffer_size", device ); /// Setting the blockwidth of a single frame + success = WriteIntegerToFile( tempBuffer, syncBlockWidth ); + return success; +} + +bool LinkToCharacterDevice( const char *linkName, const char *deviceName ) +{ + bool success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/cdev/add_link", linkName ); + if( success ) + { + success = WaitForDevice( deviceName ); + } + if( success ) + { + chmod( deviceName, ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ) ); + } + return success; +} + +bool LinkToAudioDevice( const char *linkName, uint16_t subBufferSize ) +{ + char tempBuffer[128]; + uint8_t amountOfChannels; + uint8_t bitDepth; + if( !GetAlsaConfiguration( subBufferSize, &amountOfChannels, &bitDepth ) ) + return false; + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s.%dx%d", linkName, amountOfChannels, bitDepth ); /// Add the channel information behind the Link Name + + //Be compatible to all versions of MOST Linux Driver. The name was changed of Sound AIM. + bool success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/sound/add_link", tempBuffer ); + if( !success ) + success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/audio/add_link", tempBuffer ); + return success; +} + +bool LinkToVideoForLinuxDevice( const char *linkName ) +{ + bool success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/v4l/add_link", linkName ); + return success; +} + +const char *GetErrnoString() +{ + switch( errno ) + { + case 0: + return "Nothing stored in errno"; + case 1: + return "Operation not permitted"; + case 2: + return "No such file or directory"; + case 3: + return "No such process"; + case 4: + return "Interrupted system call"; + case 5: + return "I/O error"; + case 6: + return "No such device or address"; + case 7: + return "Argument list too long"; + case 8: + return "Exec format error"; + case 9: + return "Bad file number"; + case 10: + return "No child processes"; + case 11: + return "Try again"; + case 12: + return "Out of memory"; + case 13: + return "Permission denied"; + case 14: + return "Bad address"; + case 15: + return "Block device required"; + case 16: + return "Device or resource busy"; + case 17: + return "File exists"; + case 18: + return "Cross-device link"; + case 19: + return "No such device"; + case 20: + return "Not a directory"; + case 21: + return "Is a directory"; + case 22: + return "Invalid argument"; + case 23: + return "File table overflow"; + case 24: + return "Too many open files"; + case 25: + return "Not a typewriter"; + case 26: + return "Text file busy"; + case 27: + return "File too large"; + case 28: + return "No space left on device"; + case 29: + return "Illegal seek"; + case 30: + return "Read-only file system"; + case 31: + return "Too many links"; + case 32: + return "Broken pipe"; + case 33: + return "Math argument out of domain of func"; + case 34: + return "Math result not representable"; + default: + break; + } + return "Unknown"; +} diff --git a/Src/Network/base/.svn/text-base/DriverConfiguration.h.svn-base b/Src/Network/base/.svn/text-base/DriverConfiguration.h.svn-base new file mode 100644 index 0000000..b2fd44a --- /dev/null +++ b/Src/Network/base/.svn/text-base/DriverConfiguration.h.svn-base @@ -0,0 +1,239 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This component provides helper tools to deal with USB endpoints. + */ +/*----------------------------------------------------------*/ +#ifndef DRIVERCONFIGURATION_H +#define DRIVERCONFIGURATION_H + +#include +#include +#include "Types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given USB device instance exists. + * + * \param deviceInstance - The device instance of the USB INIC, starting at 0 for the first instance. + * + * \return true: Device exists. + * \return false: Device does not exist. + */ + /*----------------------------------------------------------*/ + bool ExistsUsbDeviceInstance( uint8_t deviceInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given MLB device instance exists. + * + * \param deviceInstance - The device instance of the MLB INIC, starting at 0 for the first instance. + * + * \return true: Device exists. + * \return false: Device does not exist. + */ + /*----------------------------------------------------------*/ + bool ExistsMlbDeviceInstance( uint8_t deviceInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given I2C device instance exists. + * + * \param deviceInstance - The device instance of the I2C INIC, starting at 0 for the first instance. + * + * \return true: Device exists. + * \return false: Device does not exist. + */ + /*----------------------------------------------------------*/ + bool ExistsI2CDeviceInstance( uint8_t deviceInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Retreives the Linux character device names for the given USB device instance and the endpoint address. + * + * \note If the given buffer is too small to hold the whole path, the string is truncated and not useable! + * + * \param deviceInstance - The device instance of the USB INIC, starting at 0 for the first instance. + * \param endpointAddress - The USB endpoint address, RX endpoints starting with upper bit set (0x8X). + * \param deviceName - [in,out] Given buffer will be filled with device name (starting with /dev/). + * \param deviceNameLength - The length of the given deviceName buffer in bytes. + * \param systemName - [in,out] Given buffer will be filled with system name (starting with /sys/). + * \param systemNameLength - The length of the given systemName buffer in bytes. + * \param linkName - [in,out] Given buffer will be filled with the string needed to register this device to an AIM + * \param linkNameLength - The length of the given linkName buffer in bytes. + * + * \return true: Operation was successful. + * \return false: Operation failed, device or endpoint does not exist. + */ + /*----------------------------------------------------------*/ + bool GetUsbDeviceNames( uint8_t deviceInstance, uint8_t endpointAddress, char *deviceName, + uint16_t deviceNameLength, char *systemName, uint16_t systemNameLength, char *linkName, + uint16_t linkNameLength ); + + + /*----------------------------------------------------------*/ + /*! \brief Retreives the Linux character device names for the given MLB device instance and the endpoint address. + * + * \note If the given buffer is too small to hold the whole path, the string is truncated and not useable! + * + * \param deviceInstance - The device instance of the USB INIC, starting at 0 for the first instance. + * \param mlbChannelAddress - The MLB channel address + * \param deviceName - [in,out] Given buffer will be filled with device name (starting with /dev/). + * \param deviceNameLength - The length of the given deviceName buffer in bytes. + * \param systemName - [in,out] Given buffer will be filled with system name (starting with /sys/). + * \param systemNameLength - The length of the given systemName buffer in bytes. + * \param linkName - [in,out] Given buffer will be filled with the string needed to register this device to an AIM + * \param linkNameLength - The length of the given linkName buffer in bytes. + * + * \return true: Operation was successful. + * \return false: Operation failed, device or endpoint does not exist. + */ + /*----------------------------------------------------------*/ + bool GetMlbDeviceNames( uint8_t deviceInstance, uint8_t mlbChannelAddress, char *deviceName, + uint16_t deviceNameLength, char *systemName, uint16_t systemNameLength, char *linkName, + uint16_t linkNameLength ); + + + /*----------------------------------------------------------*/ + /*! \brief Retreives the Linux character device names for the given I2C device instance and the endpoint address. + * + * \note If the given buffer is too small to hold the whole path, the string is truncated and not useable! + * + * \param deviceInstance - The device instance of the I2C INIC, starting at 0 for the first instance. + * \param isTx - true, if the names for transmission path should be retrieved, otherwise for the reception path. + * \param deviceName - [in,out] Given buffer will be filled with device name (starting with /dev/). + * \param deviceNameLength - The length of the given deviceName buffer in bytes. + * \param systemName - [in,out] Given buffer will be filled with system name (starting with /sys/). + * \param systemNameLength - The length of the given systemName buffer in bytes. + * \param linkName - [in,out] Given buffer will be filled with the string needed to register this device to an AIM + * \param linkNameLength - The length of the given linkName buffer in bytes. + * + * \return true: Operation was successful. + * \return false: Operation failed, device or endpoint does not exist. + */ + /*----------------------------------------------------------*/ + bool GetI2CDeviceNames( uint8_t deviceInstance, bool isTx, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ); + + /*----------------------------------------------------------*/ + /*! \brief Closes the MOST channel. + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool CloseMostChannel( const char *device ); + + /*----------------------------------------------------------*/ + /*! \brief Configures the MOST channel. + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * \param mostType - The MOST data type (control, asynchron, synchron, isochron). + * \param direction - The direction of the stream (directionRX, directionTX). + * \param numBuf - The amount of buffers. + * \param bufSize - The size of each buffer in bytes. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool ConfigureMostChannel( const char *device, EPDataType_t mostType, EPDirection_t direction, uint32_t numBuf, + uint32_t bufSize ); + + /*----------------------------------------------------------*/ + /*! \brief Configures the isochronous channel of a device. + * \note This function has to be called before ConfigureMostChannel! + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * \param subbufferSize - The amount of bytes for a single subbuffer (eg. 188 or 196 bytes). + * \param packetsPerTransaction - The amount of ischronous packets per transaction. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool ConfigureIsocChannel( const char *device, uint32_t subbufferSize, uint32_t packetsPerTransaction ); + + /*----------------------------------------------------------*/ + /*! \brief Configures the Synchronous channel of a device. + * \note This function has to be called before ConfigureMostChannel! + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * \param syncBlockWidth - The bandwidth of a single synchronous frame. Ignored if not sync channel. + * \param amounOfSyncFrames - The amount of synchronous frames per USB packet. Ignored if not sync channel. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool ConfigureSyncChannel( const char *device, uint32_t syncBlockWidth, uint32_t amounOfSyncFrames ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates a character device for the given device's link name. + * + * \param linkName - [in] Link name, which is needed to register this device to an AIM. + * \param deviceName - [in,out] Device name (starting with /dev/). + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool LinkToCharacterDevice( const char *linkName, const char *deviceName ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates a audio device (ALSA) for the given device's link name. + * + * \param linkName - [in] Link name, which is needed to register this device to an AIM. + * \param subBufferSize - [in] The amount of bytes for all channels of this connection on the MOST. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool LinkToAudioDevice( const char *linkName, uint16_t subBufferSize ); + + /*----------------------------------------------------------*/ + /*! \brief Creates a Video for Linux device (V4L) for the given device's link name. + * + * \param linkName - [in] Link name, which is needed to register this device to an AIM. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool LinkToVideoForLinuxDevice( const char *linkName ); + + + /*----------------------------------------------------------*/ + /*! \brief Parsed the global errno variable and generate a human readable error description out of it. + * + * \return Zero terminated error description string. + */ + /*----------------------------------------------------------*/ + const char *GetErrnoString(); + +#ifdef __cplusplus +} +#endif + +#endif //DRIVERCONFIGURATION_H diff --git a/Src/Network/base/Board.c b/Src/Network/base/Board.c new file mode 100644 index 0000000..50daf0c --- /dev/null +++ b/Src/Network/base/Board.c @@ -0,0 +1,51 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file */ +/*! \brief Base Board initialisation */ +/*----------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "Board.h" +#include "Console.h" + +uint32_t GetTickCount( void ) +{ + struct timespec currentTime; + if (clock_gettime(CLOCK_MONOTONIC_RAW, ¤tTime)) + { + ConsolePrintf( PRIO_ERROR, RED"GetTickCount failed!!"RESETCOLOR"\n" ); + return 0; + } + return ( currentTime.tv_sec * 1000 ) + ( currentTime.tv_nsec / 1000000 ); +} + +uint16_t GetTickCountWord() +{ + return ( uint16_t )( GetTickCount() & UINT16_MAX ); +} diff --git a/Src/Network/base/Board.h b/Src/Network/base/Board.h new file mode 100644 index 0000000..585a862 --- /dev/null +++ b/Src/Network/base/Board.h @@ -0,0 +1,63 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This component abstracts platform specific functionalities. + */ +/*----------------------------------------------------------*/ +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + /*----------------------------------------------------------*/ + /*! \brief reads the ms tick counter. + * + * \return value of the ms counter. + */ + /*----------------------------------------------------------*/ + uint32_t GetTickCount( void ); + + + + + /*----------------------------------------------------------*/ + /*! \brief reads the ms tick counter + * + * \return value of the ms counter as word + */ + /*----------------------------------------------------------*/ + uint16_t GetTickCountWord( void ); + + +#ifdef __cplusplus +} +#endif + +#endif // _BOARD_H_ diff --git a/Src/Network/base/DriverConfiguration.c b/Src/Network/base/DriverConfiguration.c new file mode 100644 index 0000000..7a98d25 --- /dev/null +++ b/Src/Network/base/DriverConfiguration.c @@ -0,0 +1,608 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include +#include +#include +#include +#include +#include "DriverConfiguration.h" +#include "Console.h" + +static bool WriteCharactersToFile( const char *pFileName, const char *pString ) +{ + bool success = false; + FILE *fh = fopen( pFileName, "a" ); + if( NULL != fh ) + { + int result = fputs( pString, fh ); + if( result >= 0 ) + fputc( '\n', fh ); + if( result >= 0 ) + success = true; + fclose( fh ); + } + if( success ) + ConsolePrintf( PRIO_MEDIUM, "*** configured device: '%s = %s', success:%d\n", pFileName, pString, success ); + else + ConsolePrintf( PRIO_ERROR, RED"DriverConfiguration.WriteCharactersToFile failed for file '%s', errno:'%s'"RESETCOLOR"\n", + pFileName, GetErrnoString() ); + return success; +} + +static bool WriteIntegerToFile( const char *pFileName, int intValue ) +{ + char tempBuffer[16]; + snprintf( tempBuffer, sizeof( tempBuffer ), "%d", intValue ); + return WriteCharactersToFile( pFileName, tempBuffer ); +} + +static bool ReadFromFile( const char *pFileName, char *pString, uint16_t bufferLen ) +{ + bool success = false; + if( NULL == pString || 0 == bufferLen ) + return success; + FILE *fh = fopen( pFileName, "r" ); + if( NULL != fh ) + { + success = ( NULL != fgets( pString, bufferLen, fh ) ); + fclose( fh ); + } + if( !success ) + ConsolePrintf( PRIO_ERROR, RED"DriverConfiguration.ReadFromFile failed for file '%s', errno:'%s'"RESETCOLOR"\n", + pFileName, GetErrnoString() ); + return success; +} + +static bool ExistsDevice( const char *pDeviceName ) +{ + struct stat buffer; + return ( stat( pDeviceName, &buffer ) == 0 ); +} + +static bool WaitForDevice( const char *pDeviceName ) +{ + int timeout; + bool deviceExists = false; + for( timeout = 0; timeout < 40; timeout++ ) + { + deviceExists = ExistsDevice( pDeviceName ); + if( deviceExists ) + { + break; + } + else + { + usleep( 2500 ); + } + } + if( !deviceExists ) + ConsolePrintf( PRIO_ERROR, RED"Waiting for device '%s' to appear, timed out"RESETCOLOR"\n", + pDeviceName ); + return deviceExists; +} + +static bool GetDeviceDescription( uint8_t deviceInstance, char *deviceString, uint32_t stringLen ) +{ + bool descriptionFound = false; + char descriptionPath[64]; + char descriptionValue[32]; + + if( NULL == deviceString ) + return descriptionFound; + + snprintf( descriptionPath, sizeof( descriptionPath ), + "/sys/devices/virtual/most/mostcore/devices/mdev%d/description", deviceInstance ); + + if( !ExistsDevice( descriptionPath ) ) + return descriptionFound; + + descriptionFound = ReadFromFile( descriptionPath, descriptionValue, sizeof( descriptionValue ) ); + if( descriptionFound ) + { + strncpy( deviceString, descriptionValue, stringLen ); + } + return descriptionFound; +} + +static bool ExistsDeviceWithType( uint8_t deviceInstance, const char *deviceString, uint32_t stringLen ) +{ + bool deviceFound = false; + char interfacePath[64]; + char interfaceValue[32]; + + if( NULL == deviceString ) + return deviceFound; + + snprintf( interfacePath, sizeof( interfacePath ), "/sys/devices/virtual/most/mostcore/devices/mdev%d/interface", + deviceInstance ); + + if( !ExistsDevice( interfacePath ) ) + return deviceFound; + + deviceFound = ReadFromFile( interfacePath, interfaceValue, sizeof( interfaceValue ) ); + if( deviceFound ) + { + deviceFound = ( 0 == strncmp( interfaceValue, deviceString, stringLen ) ); + } + return deviceFound; +} + +static bool GetAlsaConfiguration( uint16_t subBufferSize, uint8_t *amountOfChannels, uint8_t *bitDepth ) +{ + if( NULL == amountOfChannels || NULL == bitDepth ) + { + ConsolePrintf( PRIO_ERROR, RED"GetAlsaConfiguration was called with invalid parameters"RESETCOLOR"\n" ); + return false; + } + switch( subBufferSize ) + { + case 2: + *amountOfChannels = 1; + *bitDepth = 16; + break; + case 4: + *amountOfChannels = 2; + *bitDepth = 16; + break; + case 6: + *amountOfChannels = 2; + *bitDepth = 24; + break; + case 8: + *amountOfChannels = 2; + *bitDepth = 32; + break; + case 12: + *amountOfChannels = 6; + *bitDepth = 16; + break; + case 18: + *amountOfChannels = 6; + *bitDepth = 24; + break; + case 16: + *amountOfChannels = 8; + *bitDepth = 16; + break; + case 24: + *amountOfChannels = 8; + *bitDepth = 24; + break; + default: + ConsolePrintf( PRIO_ERROR, RED"Configure ALSA device was called"\ + " with unknown sub-buffer size: %d"RESETCOLOR"\n", subBufferSize ); + return false; + } + return true; +} + +bool ExistsUsbDeviceInstance( uint8_t deviceInstance ) +{ + uint32_t i; + bool found = false; + uint8_t curDevInst = 0; + char lastDescr[64]; + lastDescr[0] = '\0'; + for( i = 0; i < 12; i++ ) + { + uint32_t curDescrLen; + char curDescr[64]; + if( !ExistsDeviceWithType( i, "usb", 3 ) ) + continue; + + if( !GetDeviceDescription( i, curDescr, sizeof( curDescr ) ) ) + continue; + + curDescrLen = strnlen( curDescr, sizeof( curDescr ) ); + if( curDescrLen <= 2 ) + continue; + + //Cut away the last two characters, as they are different for the 3 interfaces of INIC + curDescrLen -= 2; + + if( 0 == strncmp( curDescr, lastDescr, curDescrLen ) ) + continue; + + strncpy( lastDescr, curDescr, curDescrLen ); + lastDescr[curDescrLen] = '\0'; + + if( curDevInst++ != deviceInstance ) + continue; + + found = true; + break; + } + return found; +} + +bool ExistsMlbDeviceInstance( uint8_t deviceInstance ) +{ + return ExistsDeviceWithType( deviceInstance, "mlb", 3 ); +} + +bool ExistsI2CDeviceInstance( uint8_t deviceInstance ) +{ + return ExistsDeviceWithType( deviceInstance, "i2c", 3 ); +} + +bool GetUsbDeviceNames( uint8_t deviceInstance, uint8_t endpointAddress, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ) +{ + uint32_t i; + bool systemFound = false; + uint8_t curDevInst = 0; + char lastDescr[64]; + lastDescr[0] = '\0'; + char endpointBuffer[16]; + char systemSourceDir[64]; + DIR *d; + struct dirent *dir; + + if( NULL == deviceName || NULL == systemName || NULL == linkName ) + return false; + + deviceName[0] = '\0'; + systemName[0] = '\0'; + + for( i = 0; !systemFound && i < 12; i++ ) + { + uint32_t curDescrLen; + char curDescr[64]; + if( !ExistsDeviceWithType( i, "usb", 3 ) ) + continue; + + if( !GetDeviceDescription( i, curDescr, sizeof( curDescr ) ) ) + continue; + + curDescrLen = strnlen( curDescr, sizeof( curDescr ) ); + if( curDescrLen <= 2 ) + continue; + + //Cut away the last two characters, as they are different for the 3 interfaces of INIC + curDescrLen -= 2; + + if( ( '\0' != lastDescr[0] ) + && ( 0 != strncmp( curDescr, lastDescr, curDescrLen ) ) ) + { + ++curDevInst; + } + + strncpy( lastDescr, curDescr, curDescrLen ); + lastDescr[curDescrLen] = '\0'; + + if( curDevInst < deviceInstance ) + continue; + else if( curDevInst > deviceInstance ) + break; + + snprintf( endpointBuffer, sizeof( endpointBuffer ), "ep%02x", endpointAddress ); + snprintf( systemSourceDir, sizeof( systemSourceDir ), "/sys/devices/virtual/most/mostcore/devices/mdev%d", i ); + d = opendir( systemSourceDir ); + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + if( strstr( dir->d_name, endpointBuffer ) ) + { + snprintf( systemName, systemNameLength, "%s/%s", systemSourceDir, dir->d_name ); + snprintf( deviceName, deviceNameLength, "/dev/mdev%d-%s", deviceInstance, dir->d_name ); + snprintf( linkName, linkNameLength, "mdev%d:%s:mdev%d-%s", i, dir->d_name, deviceInstance, + dir->d_name ); + systemFound = true; + break; + } + } + closedir( d ); + } + } + return systemFound; +} + +bool GetMlbDeviceNames( uint8_t deviceInstance, uint8_t mlbChannelAddress, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ) +{ + bool systemFound = false; + char channelBuffer[16]; + char systemSourceDir[64]; + DIR *d; + struct dirent *dir; + + if( NULL == deviceName || NULL == systemName || NULL == linkName ) + return false; + + deviceName[0] = '\0'; + systemName[0] = '\0'; + + snprintf( channelBuffer, sizeof( channelBuffer ), "ca%d", mlbChannelAddress ); + snprintf( systemSourceDir, sizeof( systemSourceDir ), "/sys/devices/virtual/most/mostcore/devices/mdev%d", + deviceInstance ); + d = opendir( systemSourceDir ); + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + if( strstr( dir->d_name, channelBuffer ) ) + { + snprintf( systemName, systemNameLength, "%s/%s", systemSourceDir, dir->d_name ); + snprintf( deviceName, deviceNameLength, "/dev/mdev%d-%s", deviceInstance, dir->d_name ); + snprintf( linkName, linkNameLength, "mdev%d:%s:mdev%d-%s", deviceInstance, dir->d_name, deviceInstance, + dir->d_name ); + systemFound = true; + break; + } + } + closedir( d ); + } + return systemFound; +} + +bool GetI2CDeviceNames( uint8_t deviceInstance, bool isTx, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ) +{ + bool systemFound = false; + char systemSourceDir[64]; + DIR *d; + struct dirent *dir; + + if( NULL == deviceName || NULL == systemName || NULL == linkName ) + return false; + + deviceName[0] = '\0'; + systemName[0] = '\0'; + + snprintf( systemSourceDir, sizeof( systemSourceDir ), "/sys/devices/virtual/most/mostcore/devices/mdev%d", + deviceInstance ); + d = opendir( systemSourceDir ); + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + if( strstr( dir->d_name, ( isTx ? "tx" : "rx" ) ) ) + { + snprintf( systemName, systemNameLength, "%s/%s", systemSourceDir, dir->d_name ); + snprintf( deviceName, deviceNameLength, "/dev/mdev%d-%s", deviceInstance, dir->d_name ); + snprintf( linkName, linkNameLength, "mdev%d:%s:mdev%d-%s", deviceInstance, dir->d_name, deviceInstance, + dir->d_name ); + systemFound = true; + break; + } + } + closedir( d ); + } + return systemFound; +} + +bool CloseMostChannel( const char *device ) +{ + return true; +} + +bool ConfigureMostChannel( const char *device, EPDataType_t mostType, EPDirection_t direction, uint32_t numBuf, + uint32_t bufSize ) +{ + static const char *controlType = "control"; + static const char *asyncType = "async"; + static const char *syncType = "sync"; + static const char *isocType = "isoc_avp"; + static const char *rxDirection = "dir_rx"; + static const char *txDirection = "dir_tx"; + bool success = true; + char tempBuffer[128]; + const char *typeString = NULL; + const char *directionString = NULL; + + switch( mostType ) + { + case EP_Control: + typeString = controlType; + break; + case EP_Asynchron: + typeString = asyncType; + break; + case EP_Synchron: + typeString = syncType; + break; + case EP_Isochron: + typeString = isocType; + break; + default: + return false; + } + + switch( direction ) + { + case EPDIR_IN: + directionString = txDirection; + break; + case EPDIR_OUT: + directionString = rxDirection; + break; + default: + return false; + } + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_datatype", device ); /// Setting data type + success = WriteCharactersToFile( tempBuffer, typeString ); + if( success ) + { + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_direction", device ); /// Setting direction + success = WriteCharactersToFile( tempBuffer, directionString ); + } + if( success ) + { + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_number_of_buffers", device ); /// Setting amount of buffers + success = WriteIntegerToFile( tempBuffer, numBuf ); + } + if( success ) + { + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_buffer_size", device ); /// Setting amount of buffers + success = WriteIntegerToFile( tempBuffer, bufSize ); + } + return success; +} + +bool ConfigureIsocChannel( const char *device, uint32_t subbufferSize, uint32_t packetsPerTransaction ) +{ + char tempBuffer[128]; + bool success; + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_subbuffer_size", device ); /// Setting the subbuffer size in bytes + success = WriteIntegerToFile( tempBuffer, subbufferSize ); + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_packets_per_xact", device ); /// Setting the packets per transaction + success = WriteIntegerToFile( tempBuffer, ( packetsPerTransaction & 0xFF ) ); + return success; +} + +bool ConfigureSyncChannel( const char *device, uint32_t syncBlockWidth, uint32_t amounOfSyncFrames ) +{ + char tempBuffer[128]; + bool success; + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_packets_per_xact", device ); /// Setting the amount of frames in one USB frame + success = WriteIntegerToFile( tempBuffer, amounOfSyncFrames ); + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s/set_subbuffer_size", device ); /// Setting the blockwidth of a single frame + success = WriteIntegerToFile( tempBuffer, syncBlockWidth ); + return success; +} + +bool LinkToCharacterDevice( const char *linkName, const char *deviceName ) +{ + bool success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/cdev/add_link", linkName ); + if( success ) + { + success = WaitForDevice( deviceName ); + } + if( success ) + { + chmod( deviceName, ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ) ); + } + return success; +} + +bool LinkToAudioDevice( const char *linkName, uint16_t subBufferSize ) +{ + char tempBuffer[128]; + uint8_t amountOfChannels; + uint8_t bitDepth; + if( !GetAlsaConfiguration( subBufferSize, &amountOfChannels, &bitDepth ) ) + return false; + + snprintf( tempBuffer, sizeof( tempBuffer ), "%s.%dx%d", linkName, amountOfChannels, bitDepth ); /// Add the channel information behind the Link Name + + //Be compatible to all versions of MOST Linux Driver. The name was changed of Sound AIM. + bool success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/sound/add_link", tempBuffer ); + if( !success ) + success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/audio/add_link", tempBuffer ); + return success; +} + +bool LinkToVideoForLinuxDevice( const char *linkName ) +{ + bool success = + WriteCharactersToFile( "/sys/devices/virtual/most/mostcore/aims/v4l/add_link", linkName ); + return success; +} + +const char *GetErrnoString() +{ + switch( errno ) + { + case 0: + return "Nothing stored in errno"; + case 1: + return "Operation not permitted"; + case 2: + return "No such file or directory"; + case 3: + return "No such process"; + case 4: + return "Interrupted system call"; + case 5: + return "I/O error"; + case 6: + return "No such device or address"; + case 7: + return "Argument list too long"; + case 8: + return "Exec format error"; + case 9: + return "Bad file number"; + case 10: + return "No child processes"; + case 11: + return "Try again"; + case 12: + return "Out of memory"; + case 13: + return "Permission denied"; + case 14: + return "Bad address"; + case 15: + return "Block device required"; + case 16: + return "Device or resource busy"; + case 17: + return "File exists"; + case 18: + return "Cross-device link"; + case 19: + return "No such device"; + case 20: + return "Not a directory"; + case 21: + return "Is a directory"; + case 22: + return "Invalid argument"; + case 23: + return "File table overflow"; + case 24: + return "Too many open files"; + case 25: + return "Not a typewriter"; + case 26: + return "Text file busy"; + case 27: + return "File too large"; + case 28: + return "No space left on device"; + case 29: + return "Illegal seek"; + case 30: + return "Read-only file system"; + case 31: + return "Too many links"; + case 32: + return "Broken pipe"; + case 33: + return "Math argument out of domain of func"; + case 34: + return "Math result not representable"; + default: + break; + } + return "Unknown"; +} diff --git a/Src/Network/base/DriverConfiguration.h b/Src/Network/base/DriverConfiguration.h new file mode 100644 index 0000000..b2fd44a --- /dev/null +++ b/Src/Network/base/DriverConfiguration.h @@ -0,0 +1,239 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This component provides helper tools to deal with USB endpoints. + */ +/*----------------------------------------------------------*/ +#ifndef DRIVERCONFIGURATION_H +#define DRIVERCONFIGURATION_H + +#include +#include +#include "Types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given USB device instance exists. + * + * \param deviceInstance - The device instance of the USB INIC, starting at 0 for the first instance. + * + * \return true: Device exists. + * \return false: Device does not exist. + */ + /*----------------------------------------------------------*/ + bool ExistsUsbDeviceInstance( uint8_t deviceInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given MLB device instance exists. + * + * \param deviceInstance - The device instance of the MLB INIC, starting at 0 for the first instance. + * + * \return true: Device exists. + * \return false: Device does not exist. + */ + /*----------------------------------------------------------*/ + bool ExistsMlbDeviceInstance( uint8_t deviceInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given I2C device instance exists. + * + * \param deviceInstance - The device instance of the I2C INIC, starting at 0 for the first instance. + * + * \return true: Device exists. + * \return false: Device does not exist. + */ + /*----------------------------------------------------------*/ + bool ExistsI2CDeviceInstance( uint8_t deviceInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Retreives the Linux character device names for the given USB device instance and the endpoint address. + * + * \note If the given buffer is too small to hold the whole path, the string is truncated and not useable! + * + * \param deviceInstance - The device instance of the USB INIC, starting at 0 for the first instance. + * \param endpointAddress - The USB endpoint address, RX endpoints starting with upper bit set (0x8X). + * \param deviceName - [in,out] Given buffer will be filled with device name (starting with /dev/). + * \param deviceNameLength - The length of the given deviceName buffer in bytes. + * \param systemName - [in,out] Given buffer will be filled with system name (starting with /sys/). + * \param systemNameLength - The length of the given systemName buffer in bytes. + * \param linkName - [in,out] Given buffer will be filled with the string needed to register this device to an AIM + * \param linkNameLength - The length of the given linkName buffer in bytes. + * + * \return true: Operation was successful. + * \return false: Operation failed, device or endpoint does not exist. + */ + /*----------------------------------------------------------*/ + bool GetUsbDeviceNames( uint8_t deviceInstance, uint8_t endpointAddress, char *deviceName, + uint16_t deviceNameLength, char *systemName, uint16_t systemNameLength, char *linkName, + uint16_t linkNameLength ); + + + /*----------------------------------------------------------*/ + /*! \brief Retreives the Linux character device names for the given MLB device instance and the endpoint address. + * + * \note If the given buffer is too small to hold the whole path, the string is truncated and not useable! + * + * \param deviceInstance - The device instance of the USB INIC, starting at 0 for the first instance. + * \param mlbChannelAddress - The MLB channel address + * \param deviceName - [in,out] Given buffer will be filled with device name (starting with /dev/). + * \param deviceNameLength - The length of the given deviceName buffer in bytes. + * \param systemName - [in,out] Given buffer will be filled with system name (starting with /sys/). + * \param systemNameLength - The length of the given systemName buffer in bytes. + * \param linkName - [in,out] Given buffer will be filled with the string needed to register this device to an AIM + * \param linkNameLength - The length of the given linkName buffer in bytes. + * + * \return true: Operation was successful. + * \return false: Operation failed, device or endpoint does not exist. + */ + /*----------------------------------------------------------*/ + bool GetMlbDeviceNames( uint8_t deviceInstance, uint8_t mlbChannelAddress, char *deviceName, + uint16_t deviceNameLength, char *systemName, uint16_t systemNameLength, char *linkName, + uint16_t linkNameLength ); + + + /*----------------------------------------------------------*/ + /*! \brief Retreives the Linux character device names for the given I2C device instance and the endpoint address. + * + * \note If the given buffer is too small to hold the whole path, the string is truncated and not useable! + * + * \param deviceInstance - The device instance of the I2C INIC, starting at 0 for the first instance. + * \param isTx - true, if the names for transmission path should be retrieved, otherwise for the reception path. + * \param deviceName - [in,out] Given buffer will be filled with device name (starting with /dev/). + * \param deviceNameLength - The length of the given deviceName buffer in bytes. + * \param systemName - [in,out] Given buffer will be filled with system name (starting with /sys/). + * \param systemNameLength - The length of the given systemName buffer in bytes. + * \param linkName - [in,out] Given buffer will be filled with the string needed to register this device to an AIM + * \param linkNameLength - The length of the given linkName buffer in bytes. + * + * \return true: Operation was successful. + * \return false: Operation failed, device or endpoint does not exist. + */ + /*----------------------------------------------------------*/ + bool GetI2CDeviceNames( uint8_t deviceInstance, bool isTx, char *deviceName, uint16_t deviceNameLength, + char *systemName, uint16_t systemNameLength, char *linkName, uint16_t linkNameLength ); + + /*----------------------------------------------------------*/ + /*! \brief Closes the MOST channel. + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool CloseMostChannel( const char *device ); + + /*----------------------------------------------------------*/ + /*! \brief Configures the MOST channel. + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * \param mostType - The MOST data type (control, asynchron, synchron, isochron). + * \param direction - The direction of the stream (directionRX, directionTX). + * \param numBuf - The amount of buffers. + * \param bufSize - The size of each buffer in bytes. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool ConfigureMostChannel( const char *device, EPDataType_t mostType, EPDirection_t direction, uint32_t numBuf, + uint32_t bufSize ); + + /*----------------------------------------------------------*/ + /*! \brief Configures the isochronous channel of a device. + * \note This function has to be called before ConfigureMostChannel! + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * \param subbufferSize - The amount of bytes for a single subbuffer (eg. 188 or 196 bytes). + * \param packetsPerTransaction - The amount of ischronous packets per transaction. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool ConfigureIsocChannel( const char *device, uint32_t subbufferSize, uint32_t packetsPerTransaction ); + + /*----------------------------------------------------------*/ + /*! \brief Configures the Synchronous channel of a device. + * \note This function has to be called before ConfigureMostChannel! + * + * \param device - The system path to configure the MOST device (starting with "/sys/"). + * \param syncBlockWidth - The bandwidth of a single synchronous frame. Ignored if not sync channel. + * \param amounOfSyncFrames - The amount of synchronous frames per USB packet. Ignored if not sync channel. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool ConfigureSyncChannel( const char *device, uint32_t syncBlockWidth, uint32_t amounOfSyncFrames ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates a character device for the given device's link name. + * + * \param linkName - [in] Link name, which is needed to register this device to an AIM. + * \param deviceName - [in,out] Device name (starting with /dev/). + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool LinkToCharacterDevice( const char *linkName, const char *deviceName ); + + + /*----------------------------------------------------------*/ + /*! \brief Creates a audio device (ALSA) for the given device's link name. + * + * \param linkName - [in] Link name, which is needed to register this device to an AIM. + * \param subBufferSize - [in] The amount of bytes for all channels of this connection on the MOST. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool LinkToAudioDevice( const char *linkName, uint16_t subBufferSize ); + + /*----------------------------------------------------------*/ + /*! \brief Creates a Video for Linux device (V4L) for the given device's link name. + * + * \param linkName - [in] Link name, which is needed to register this device to an AIM. + * + * \return True if no error + */ + /*----------------------------------------------------------*/ + bool LinkToVideoForLinuxDevice( const char *linkName ); + + + /*----------------------------------------------------------*/ + /*! \brief Parsed the global errno variable and generate a human readable error description out of it. + * + * \return Zero terminated error description string. + */ + /*----------------------------------------------------------*/ + const char *GetErrnoString(); + +#ifdef __cplusplus +} +#endif + +#endif //DRIVERCONFIGURATION_H diff --git a/Src/SafeVector.h b/Src/SafeVector.h new file mode 100644 index 0000000..97a4882 --- /dev/null +++ b/Src/SafeVector.h @@ -0,0 +1,192 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CSafeVector class. + */ +/*----------------------------------------------------------*/ + +#ifndef SAFE_VECTOR_H +#define SAFE_VECTOR_H + +#include +#include +#include +#include +#include +#include "SafeVector.h" + +using namespace std; + +/*----------------------------------------------------------*/ +/*! \brief Thread safe vector class. + */ +/*----------------------------------------------------------*/ +template +class CSafeVector +{ +private: + bool initialized; + std::vector vec; + sem_t sem; +public: + /*----------------------------------------------------------*/ + /*! \brief Default constructor of CSafeVector. + */ + /*----------------------------------------------------------*/ + CSafeVector() : initialized(false) + { + sem_init( &sem, 0, 1 ); //Mutex, initialized to 1 => sem_wait will block at 2nd call + } + + /*----------------------------------------------------------*/ + /*! \brief Default destructor of CSafeVector. + */ + /*----------------------------------------------------------*/ + ~CSafeVector() + { + vec.empty(); + sem_destroy( &sem); + } + + /*----------------------------------------------------------*/ + /*! \brief Stores the given object into the last postion of the vector. + */ + /*----------------------------------------------------------*/ + void PushBack(T obj) + { + if( NULL == obj ) + return; + sem_wait( &sem ); + vec.push_back( obj ); + initialized = true; + sem_post( &sem ); + } + + /*----------------------------------------------------------*/ + /*! \brief Gets the first element from the vector and dequeue it. + * \return The first object if exists, otherwise NULL, + */ + /*----------------------------------------------------------*/ + T PopFront() + { + T obj = NULL; + sem_wait( &sem ); + if (vec.size() >= 1) + { + obj = vec[0]; + vec.erase( vec.begin() ); + } + sem_post( &sem ); + return obj; + } + + /*----------------------------------------------------------*/ + /*! \brief Gets the first element from the vector and dequeue it. + * \return The first object if exists, otherwise NULL, + */ + /*----------------------------------------------------------*/ + T operator[](uint32_t p) + { + if( !initialized ) + return NULL; + T obj = NULL; + sem_wait( &sem ); + uint32_t s = vec.size(); + if (0 != s && p < s) + { + obj = vec[p]; + } + sem_post( &sem ); + return obj; + } + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of objects stored in the vector. + * \return The amount of objects available. + */ + /*----------------------------------------------------------*/ + uint32_t Size() + { + uint32_t s; + if( !initialized ) + return 0; + + sem_wait( &sem ); + s = vec.size(); + sem_post( &sem ); + return s; + } + + /*----------------------------------------------------------*/ + /*! \brief Safley removes the given object from the vector. + * \note If not found, it will be silently ignored. + * \note The memory of the object will NOT be freed! + */ + /*----------------------------------------------------------*/ + void Remove(T obj) + { + int32_t deleteIndex = -1; + if( !initialized || NULL == obj ) + return; + + sem_wait( &sem ); + for( uint32_t i = 0; i < vec.size(); i++ ) + { + if( obj == vec[i] ) + { + deleteIndex = i; + break; + } + } + if( -1 != deleteIndex ) + { + vec.erase( vec.begin() + deleteIndex ); + } + sem_post( &sem ); + } + + + /*----------------------------------------------------------*/ + /*! \brief Safley removes the given object from the vector. + * \note If not found, it will be silently ignored. + * \param destroy - if set to true, the memory will be freed for each deleted object. + */ + /*----------------------------------------------------------*/ + void RemoveAll(bool destroy) + { + if( !initialized ) + return; + sem_wait( &sem ); + for( uint32_t i = 0; destroy && i < vec.size(); i++ ) + { + T obj = vec[i]; + delete obj; + } + vec.clear(); + sem_post( &sem ); + } +}; + +#endif //SAFE_VECTOR_H \ No newline at end of file diff --git a/Src/ScriptXml.cpp b/Src/ScriptXml.cpp new file mode 100644 index 0000000..94c7ae3 --- /dev/null +++ b/Src/ScriptXml.cpp @@ -0,0 +1,200 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "SafeVector.h" +#include "Console.h" +#include "ScriptXml.h" + +#define TAG_SCRIPT ((xmlChar *)"script") +#define TAG_ACTION ((xmlChar *)"action") +#define TAG_NAME ((xmlChar *)"name") +#define TAG_TYPE ((xmlChar *)"type") +#define TAG_FBLOCK_ID_HEX ((xmlChar *)"fblock_id_hex") +#define TAG_FUNCTION_ID_HEX ((xmlChar *)"function_id_hex") +#define TAG_OP_TYPE_REQUEST_HEX ((xmlChar *)"op_type_request_hex") +#define TAG_OP_TYPE_RESPONSE_HEX ((xmlChar *)"op_type_response_hex") +#define TAG_PAYLOAD_HEX ((xmlChar *)"payload_hex") +#define TAG_PAUSE_MS ((xmlChar *)"pause_ms") + +#define VAL_SEND_MCM ((char*)"SEND_MCM") +#define VAL_PAUSE ((char*)"PAUSE") + +#define XML_STRCMP(x,y) (strcmp(reinterpret_cast< const char* >(x), reinterpret_cast< const char* >(y)) == 0) + +CSciptXml::CSciptXml( const char *szFile ) : CXml( szFile ) +{ + +} + +CSciptXml::CSciptXml( const char *szBuffer, uint32_t nBufferLen ) : CXml( szBuffer, nBufferLen ) +{ + +} + +bool CSciptXml::GetScripts( CSafeVector &allChannels ) +{ + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + bool success = false; + CXml Xml( *( ( CXml * )this ) ); + success = Xml.SetToTopNode(); + + while( success && Xml.FindNode( TAG_ACTION ) ) + { + ConsolePrintf( PRIO_LOW, BLUE"--NEW ACTION--"RESETCOLOR"\n" ); + CScript *script = new CScript(); + if( NULL == script ) + break; + + szTmp[0] = '\0'; + success &= Xml.GetChildValue( TAG_NAME, szTmp, XML_DEFAULT_VALUE_LEN ); + if( success ) + { + ConsolePrintf( PRIO_LOW, BLUE"Name: '%s'"RESETCOLOR"\n", szTmp ); + int nameLen = sizeof( xmlChar ) * ( xmlStrlen( szTmp ) + 1 ); + if( nameLen >= MAX_SCRIPT_NAME_LENGTH ) + nameLen = MAX_SCRIPT_NAME_LENGTH - 1; + memcpy( script->scriptName, szTmp, nameLen ); + script->scriptName[nameLen] = '\0'; //Terminate string in any case + } + + szTmp[0] = '\0'; + success &= Xml.GetChildValue( TAG_TYPE, szTmp ); + if( success ) + { + ConsolePrintf( PRIO_LOW, BLUE"Type: %s"RESETCOLOR"\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_SEND_MCM) ) + { + script->scriptType = SCRIPT_MCM_SEND; + } + else if( XML_STRCMP(szTmp, VAL_PAUSE) ) + { + script->scriptType = SCRIPT_PAUSE; + } + else + { + //Be tolerant to new types + ConsolePrintf( PRIO_ERROR, YELLOW"CSciptXml: Ignoring unknown TYPE:'%s'"RESETCOLOR"\n", szTmp ); + delete script; + Xml.FindNextNode(); + continue; + } + } + + if( SCRIPT_MCM_SEND == script->scriptType ) + { + szTmp[0] = '\0'; + success &= Xml.GetChildValue( TAG_FBLOCK_ID_HEX, szTmp, XML_DEFAULT_VALUE_LEN ); + if( success ) + { + script->fblockId = ConvertToInt( szTmp ); + ConsolePrintf( PRIO_LOW, BLUE"FBlock-ID: '%s=%d'"RESETCOLOR"\n", szTmp, script->fblockId ); + } + + szTmp[0] = '\0'; + success &= Xml.GetChildValue( TAG_FUNCTION_ID_HEX, szTmp, XML_DEFAULT_VALUE_LEN ); + if( success ) + { + script->functionId = ConvertToInt( szTmp ); + ConsolePrintf( PRIO_LOW, BLUE"Function-ID: '%s=%d'"RESETCOLOR"\n", szTmp, script->functionId ); + } + + szTmp[0] = '\0'; + success &= Xml.GetChildValue( TAG_OP_TYPE_REQUEST_HEX, szTmp, + XML_DEFAULT_VALUE_LEN ); + if( success ) + { + script->opTypeRequest = ConvertToInt( szTmp ); + ConsolePrintf( PRIO_LOW, BLUE"OP-Type Request: '%s=%d'"RESETCOLOR"\n", szTmp, script->opTypeRequest ); + } + + szTmp[0] = '\0'; + if( Xml.GetChildValue( TAG_OP_TYPE_RESPONSE_HEX, szTmp, + XML_DEFAULT_VALUE_LEN ) ) + { + script->opTypeResponse = ConvertToInt( szTmp ); + ConsolePrintf( PRIO_LOW, BLUE"OP-Type Response: '%s=%d'"RESETCOLOR"\n", szTmp, + script->opTypeResponse ); + } + else + { + script->opTypeResponse = 0xFF; + ConsolePrintf( PRIO_LOW, BLUE"OP-Type Response was not defined"RESETCOLOR"\n" ); + } + + szTmp[0] = '\0'; + //Payload is optional + if( Xml.GetChildValue( TAG_PAYLOAD_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + { + ConsolePrintf( PRIO_LOW, BLUE"Payload: '%s'"RESETCOLOR"\n", szTmp ); + StorePayload( szTmp, script ); + } + } + else if( SCRIPT_PAUSE == script->scriptType ) + { + szTmp[0] = '\0'; + success &= Xml.GetChildValue( TAG_PAUSE_MS, szTmp, XML_DEFAULT_VALUE_LEN ); + if( success ) + { + script->pauseInMillis = strtol( ( char * )szTmp, NULL, 10 ); + ConsolePrintf( PRIO_LOW, BLUE"FBlock-ID: '%s=%d'"RESETCOLOR"\n", szTmp, script->fblockId ); + } + } + if( success ) + { + allChannels.PushBack( script ); + } + else + { + delete script; + } + Xml.FindNextNode(); + } + return success; +} + +void CSciptXml::StorePayload( xmlChar *xString, CScript *script ) +{ + char *cString = ( char * )xString; //Convertion may be needed.. + script->payloadLength = 0; + for( uint32_t i = 0; i < strlen( cString ); i++ ) + { + if( cString[i] >= 65 && cString[i] <= 90 ) + { + //convert upper case to lower case + cString[i] += 32; + } + } + char *tkPtr; + char *token = strtok_r( cString, " ,.-", &tkPtr ); + while( NULL != token ) + { + if( script->payloadLength >= MAX_PAYLOAD_LENGTH ) + { + ConsolePrintf( PRIO_ERROR, + "CSciptXml::ConvertToByteArray, MAX_PAYLOAD_LENGTH is defined too low. Please increase value!" ); + break; + } + script->payload[script->payloadLength++] = strtol( token, NULL, 16 ); + token = strtok_r( NULL, " ,.-", &tkPtr ); + } +} diff --git a/Src/ScriptXml.h b/Src/ScriptXml.h new file mode 100644 index 0000000..093c4ae --- /dev/null +++ b/Src/ScriptXml.h @@ -0,0 +1,88 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CSciptXml class. + */ +/*----------------------------------------------------------*/ +#ifndef _SCRIPTXML_H_ +#define _SCRIPTXML_H_ + +#include +#include +#include +#include "Xml.h" + +#define MAX_SCRIPT_NAME_LENGTH 64 +#define MAX_PAYLOAD_LENGTH 64 + +typedef enum ScriptType_tag +{ + SCRIPT_UNKNOWN, + SCRIPT_MCM_SEND, + SCRIPT_PAUSE +} ScriptType_t; + +/*----------------------------------------------------------*/ +/*! + * \brief Storage class to store the scripting informations. This class will be returned by + * the CSciptXml class. + */ +/*----------------------------------------------------------*/ +class CScript +{ +public: + ScriptType_t scriptType; + char scriptName[MAX_SCRIPT_NAME_LENGTH]; + uint8_t fblockId; + uint16_t functionId; + uint8_t opTypeRequest; + uint8_t opTypeResponse; + uint8_t payload[MAX_PAYLOAD_LENGTH]; + uint32_t payloadLength; + uint32_t pauseInMillis; + + CScript() : scriptType( SCRIPT_UNKNOWN ), fblockId( 0xFF ), functionId( 0xFFFF ), opTypeRequest( 0xFF ), + opTypeResponse( 0xFF ), payloadLength( 0 ), pauseInMillis( 0 ) + { + scriptName[0] = '\0'; + } +}; + +/*----------------------------------------------------------*/ +/*! + * \brief Class to read XML for the NetworkManager scripting use case. + */ +/*----------------------------------------------------------*/ +class CSciptXml : public CXml +{ +private: + void StorePayload( xmlChar *xString, CScript *script ); +public: + CSciptXml( const char *szFile ); + CSciptXml( const char *szBuffer, uint32_t nBufferLen ); + bool GetScripts( CSafeVector &allChannels ); +}; + +#endif //_SCRIPTXML_H_ diff --git a/Src/Thread.cpp b/Src/Thread.cpp new file mode 100644 index 0000000..b6bb01e --- /dev/null +++ b/Src/Thread.cpp @@ -0,0 +1,129 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "Thread.h" +#include +#include "Console.h" + +CThread::CThread( const char *threadName, bool maxPrio ) : m_hThread( THREAD_STATE_DONE ) +{ + strncpy( m_threadName, threadName, sizeof( m_threadName ) ); + m_maxPrio = maxPrio; +#ifdef THREAD_DEBUG + ConsolePrintf(PRIO_LOW, "===== Thread '%s' has been created =====\n", m_threadName); +#endif +} + +CThread::~CThread() +{ + Stop(); + usleep( 10000 ); + while( IsThreadRunning() ) + { + ConsolePrintf( PRIO_LOW, "===== Thread '%s' is still running =====\n", m_threadName ); + usleep( 1000000 ); + } +} + +void *CThread::RunInst( void *pInst ) +{ + CThread *thread = ( CThread * )pInst; + if( NULL == thread ) + return 0; + + /* set thread priority: */ + if( thread->m_maxPrio ) + { + int ret; + pthread_t this_thread = pthread_self(); + struct sched_param params; + params.sched_priority = sched_get_priority_max( SCHED_FIFO ); + ret = pthread_setschedparam( this_thread, SCHED_FIFO, ¶ms ); + if( ret != 0 ) + { + ConsolePrintf( PRIO_ERROR, "Thread %s: pthread_setschedparam(SCHED_FIFO) failed\n", thread->m_threadName ); + } + // Now verify the change in thread priority + int policy = 0; + ret = pthread_getschedparam( this_thread, &policy, ¶ms ); + if( ret != 0 ) + { + ConsolePrintf( PRIO_ERROR, "Thread %s: pthread_getschedparam() failed\n", thread->m_threadName ); + } + if( policy != SCHED_FIFO ) + { + ConsolePrintf( PRIO_ERROR, "Thread %s: Scheduling is NOT SCHED_FIFO!\n", thread->m_threadName ); + } + else + { + ConsolePrintf( PRIO_LOW, "Thread %s: SCHED_FIFO OK\n", thread->m_threadName ); + } + ConsolePrintf( PRIO_LOW, "Thread %s: Thread priority is %d\n", thread->m_threadName, params.sched_priority ); + } + + while( THREAD_STATE_STOPPING != + thread->m_hThread ) + { // until thread is marked for termination +#ifdef THREAD_DEBUG + ConsolePrintf(PRIO_LOW, "+++++ Thread '%s' run before Run +++++\n", thread->m_threadName); +#endif + ( ( CThread * )pInst )->Run(); +#ifdef THREAD_DEBUG + ConsolePrintf(PRIO_LOW, "----- Thread '%s' run after Run -----\n", thread->m_threadName); +#endif + } + + ( ( CThread * )pInst )->m_hThread = THREAD_STATE_DONE; // mark thread as done + + return 0; +} + +void CThread::Start() +{ + if( THREAD_STATE_DONE != m_hThread ) + return; + + struct sched_param param; + pthread_attr_t tattr; + pthread_attr_init( &tattr ); + pthread_attr_getschedparam( &tattr, ¶m ); + pthread_attr_setschedpolicy( &tattr, SCHED_RR ); + if( m_maxPrio ) + { + param.sched_priority = sched_get_priority_max( SCHED_RR ); + pthread_attr_setschedparam( &tattr, ¶m ); + // note: this doesn't seem to work, so we're setting prio in RunInst() now + } + pthread_create( &m_hThread, &tattr, CThread::RunInst, ( void * )this ); +} + +void CThread::Stop() +{ + if( m_hThread ) + m_hThread = THREAD_STATE_STOPPING; +} + +bool CThread::IsThreadRunning() +{ + return ( THREAD_STATE_DONE != m_hThread ); +} diff --git a/Src/Thread.h b/Src/Thread.h new file mode 100644 index 0000000..01f6500 --- /dev/null +++ b/Src/Thread.h @@ -0,0 +1,88 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CThread class. + */ +/*----------------------------------------------------------*/ +#ifndef THREAD_H +#define THREAD_H + +#include +#include +#include +#include + +//#define THREAD_DEBUG +#define THREAD_STATE_DONE 0 +#define THREAD_STATE_STOPPING (pthread_t)-1 + +/*----------------------------------------------------------*/ +/*! \brief Helper class to deal with Threads. + */ +/*----------------------------------------------------------*/ +class CThread +{ + pthread_t m_hThread; + char m_threadName[32]; + bool m_maxPrio; + +public: + CThread( const char *threadName, bool maxPrio ); + virtual ~CThread(); + + /*----------------------------------------------------------*/ + /*! \brief Starts the thread execution. + */ + /*----------------------------------------------------------*/ + void Start(); + + /*----------------------------------------------------------*/ + /*! \brief Stops the thread execution. + */ + /*----------------------------------------------------------*/ + void Stop(); + + /*----------------------------------------------------------*/ + /*! \brief Determines if the thread is running. + * \return true, if the thread is running. false, if the thread is stopped. + */ + /*----------------------------------------------------------*/ + bool IsThreadRunning(); + + /*----------------------------------------------------------*/ + /*! \brief This method must be overridden the deriving class. + * This method will then run in an own thread. + * This method will be infinitely called, as long as the CThread object is marked to run. + */ + /*----------------------------------------------------------*/ + virtual void Run() = 0; + +private: + static void *RunInst( void *pInst ); + +}; + +#endif + diff --git a/Src/Types.h b/Src/Types.h new file mode 100644 index 0000000..13ab01b --- /dev/null +++ b/Src/Types.h @@ -0,0 +1,230 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the EPDataType_t, EPDirection_t, MlbPortSpeed_t, V1I2SPortClkDriveMode_t, V1I2SStreamingDataFormat_t, + * V1I2SPin_t, V3I2SPortOption_t, V3I2SClockMode_t, V3I2SDelayMode_t, V3I2SPortSpeed_t, V3I2SAlignment_t, V3I2SPin_t type definition. + */ +/*----------------------------------------------------------*/ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Console.h" + +#define LOG(x) ConsolePrintf(PRIO_ERROR, RED"! %s, %u, %s"RESETCOLOR"\n",x,__LINE__,__FILE__) + +#define SCM_IN 0x00 +#define SCM_OUT 0x01 + +typedef uint32_t TChannelId; +typedef uint16_t TDeviceId; +typedef uint8_t TNodePos; +typedef uint16_t TNodeAddr; +typedef uint32_t TBlockwidth; +typedef uint8_t TMostInstace; +typedef uint16_t TPos; ///Value is in per mile. (0..1000) + +typedef enum AimType_tag +{ + AIM_UNKNOWN, + AIM_CDEV, + AIM_AUDIO, + AIM_V4L +} AimType_t; + +///Data type enum values are matched to INIC API V1 & V2 +typedef enum +{ + EP_Synchron = 0x00, + EP_Asynchron = 0x01, + EP_Control = 0x02, + EP_Isochron = 0x03, + EP_Unused = 0xFE, + EP_Unknown = 0xFF +} EPDataType_t; + +///Direction type enum values are matched to INIC API V1 & V2 +typedef enum +{ + EPDIR_IN = 0x00, + EPDIR_OUT = 0x01, + EPDIR_Unknown = 0xFF +} EPDirection_t; + +///INIC API V1 and V2: MLB Port speed in mulitple of MOST base clock (FS) +typedef enum +{ + MlbSpeed256FS = 0, + MlbSpeed512FS = 1, + MlbSpeed1024FS = 2, + MlbSpeed2048FS = 3, + MlbSpeed3072FS = 4, + MlbSpeed4096FS = 5, + MlbSpeed6144FS = 6, + MlbSpeed8192FS = 7, + MlbSpeedNotSet = 0xFF +} MlbPortSpeed_t; + +///TSI Port ID +typedef enum +{ + V1TsiPortInstance0 = 0x5, + V1TsiPortInstance1 = 0x25, + V1TsiPortInstanceNotSet = 0xFF +} V1TsiPortInstance_t; + +///TSI Port Mode +typedef enum +{ + V1TsiPortModeSlave = 0x0, + V1TsiPortModeMaster = 0x1, + V1TsiPortModeNotSet = 0xFF +} V1TsiPortMode; + +///I2S Port drive mode +typedef enum +{ + V1I2sClockModeOutput = 0, + V1I2sClockModeInput = 1, + V1I2sClockModeIsocOutput = 2, + V1I2sClockModeIsocInput = 3, + V1I2sClockModeUnknown = 0xFE, +} V1I2SPortClkDriveMode_t; + +///I2S Streaming Port mode +typedef enum +{ + V1I2sStreamingPortInOut = 0, + V1I2sStreamingPortDualIn = 1, + V1I2sStreamingPortDualOut = 2, + V1I2sStreamingPortFullStream = 3, + V1I2sStreamingPortUnknown = 0xFE, + V1I2sStreamingPortWildcard = 0xFF ///Only valid for OS81110 +} V1I2SStreamingPortMode_t; + +///I2S Port streaming data format +typedef enum +{ + V1I2sDelay64Fs16Bit = 0x00, + V1I2sDelay64Fs24Bit = 0x01, + V1I2sDelay64FsSeq = 0x04, + V1I2sDelay128FsSeq = 0x05, + V1I2sDelay256FsSeq = 0x06, + V1I2sRight64Fs16Bit = 0x07, + V1I2sRight64Fs24Bit = 0x08, + V1I2sSeq64Fs = 0x0B, + V1I2sSeq128Fs = 0x0C, + V1I2sSeq256Fs = 0x0D, + V1I2sLeft64Fs16Bit = 0x0E, + V1I2sLeft64Fs24Bit = 0x0F, + V1I2sDelay512FsSeq = 0x17, + V1I2sSeq512Fs = 0x18, + V1I2sDataFormatUnknown = 0xFE, +} V1I2SStreamingDataFormat_t; + +///I2S Stream Pin ID +typedef enum +{ + V1I2sSR0Pin = 0x00, + V1I2sSX0Pin = 0x01, + V1I2sSR1Pin = 0x02, + V1I2sSX1Pin = 0x03, + V1I2sSR2Pin = 0x04, + V1I2sSX2Pin = 0x05, + V1I2sSR3Pin = 0x06, + V1I2sSX3Pin = 0x07, + V1I2sFSYSCKPins = 0x10, + V1I2sInvalidPin = 0xFE, +} V1I2SPin_t; + +///I2S Port option +typedef enum +{ + V3I2SOptionInOut = 0, + V3I2SOptionDualIn = 1, + V3I2SOptionDualOut = 2, + V3I2SOptionUnknown = 0xFE +} V3I2SPortOption_t; + +///I2S Clock mode +typedef enum +{ + V3I2SClockModeOutput = 0, + V3I2SClockModeInput = 1, + V3I2SClockModeWildcard = 0xFF, + V3I2SClockModeUnknown = 0xFE +} V3I2SClockMode_t; + +///I2S Delay option +typedef enum +{ + V3I2SDelayOff = 0, + V3I2SDelayEnabled = 1, + V3I2SDelayWildcard = 0xFF, + V3I2SDelayUnknown = 0xFE +} V3I2SDelayMode_t; + +///I2S Port speed in mulitple of MOST base clock (FS) +typedef enum +{ + V3I2SSpeed64FS = 3, + V3I2SSpeed128FS = 4, + V3I2SSpeed256FS = 5, + V3I2SSpeed512FS = 6, + V3I2SSpeedWildcard = 0xFF, + V3I2SSpeedUnknown = 0xFE +} V3I2SPortSpeed_t; + +///I2S Data Alignment +typedef enum +{ + V3I2SAlignLeft16 = 0, + V3I2SAlignLeft24 = 1, + V3I2SAlignRight16 = 2, + V3I2SAlignRight24 = 3, + V3I2SAlignSeq = 4, + V3I2SAlignUnknown = 0xFE +} V3I2SAlignment_t; + +///I2S Stream Pin ID +typedef enum +{ + V3I2SSRXA0 = 0x0, + V3I2SSRXA1 = 0x1, + V3I2SSRXB0 = 0x10, + V3I2SSRXB1 = 0x11, + V3I2SInvalidPin = 0xFE +} V3I2SPin_t; + +#endif diff --git a/Src/VodXml.cpp b/Src/VodXml.cpp new file mode 100644 index 0000000..edf27c5 --- /dev/null +++ b/Src/VodXml.cpp @@ -0,0 +1,1112 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "SafeVector.h" +#include "Console.h" +#include "VodXml.h" + +#define TAG_ADDRESS ((xmlChar *)"channel_addr") +#define TAG_ADDRESS_HEX ((xmlChar *)"channel_addr_hex") +#define TAG_ASYNC_BANDWIDTH ((xmlChar *)"async_bandwidth") +#define TAG_BLOCKWIDTH ((xmlChar *)"blockwidth") +#define TAG_OFFSET ((xmlChar *)"offset") +#define TAG_BUFFER_SIZE ((xmlChar *)"buffer_size") +#define TAG_CHANNEL ((xmlChar *)"channel") +#define TAG_CHANNEL_ID ((xmlChar *)"channel_id") +#define TAG_DATA_TYPE ((xmlChar *)"data_type") +#define TAG_AIM_TYPE ((xmlChar *)"aim_type") +#define TAG_DEVICE ((xmlChar *)"device") +#define TAG_DEVICE_API_VERSION ((xmlChar *)"device_api_ver") +#define TAG_DEVICE_MLB_SPEED ((xmlChar *)"open_mlb_port_speed") +#define TAG_DEVICE_TYPE ((xmlChar *)"device_type") +#define TAG_DEVICE_TYPE_HEX ((xmlChar *)"device_type_hex") +#define TAG_DIR ((xmlChar *)"dir") +#define TAG_ENDPOINT ((xmlChar *)"endpoint") +#define TAG_TSI_PORT_ID ((xmlChar *)"tsi_port_id") +#define TAG_TSI_PORT_MODE ((xmlChar *)"tsi_port_mode") +#define TAG_I2S_V1_CLKDRIVEMODE ((xmlChar *)"i2s_v1_clock_drive_mode") +#define TAG_I2S_V1_OPTION ((xmlChar *)"i2s_v1_option") +#define TAG_I2S_V1_DATAFORMAT ((xmlChar *)"i2s_v1_data_format") +#define TAG_I2S_V1_PIN ((xmlChar *)"i2s_v1_pin") +#define TAG_I2S_OPTION ((xmlChar *)"i2s_option") +#define TAG_I2S_CLOCK_DIR ((xmlChar *)"i2s_clock_dir") +#define TAG_I2S_CLOCK_DELAY ((xmlChar *)"i2s_clock_delay") +#define TAG_I2S_FS_SPEED ((xmlChar *)"i2s_fs_speed") +#define TAG_I2S_ALIGN ((xmlChar *)"i2s_align") +#define TAG_I2S_PIN ((xmlChar *)"i2s_pin") +#define TAG_INSTANCE ((xmlChar *)"instance") +#define TAG_LOCAL_CONNECTION ((xmlChar *)"local_connection") +#define TAG_NETWORK_CONFIG ((xmlChar *)"network_config") +#define TAG_NETWORK_MANAGER ((xmlChar *)"network_manager") +#define TAG_NUMBER_OF_BUFFERS ((xmlChar *)"number_of_buffers") +#define TAG_PACKETS_XACT ((xmlChar *)"packets_per_xact") +#define TAG_PACKETS_XACT_HEX ((xmlChar *)"packets_per_xact_hex") +#define TAG_PORT_ID ((xmlChar *)"port_id") +#define TAG_ROUTE ((xmlChar *)"route") +#define TAG_SCRIPT ((xmlChar *)"script") +#define TAG_SOCKET ((xmlChar *)"socket") +#define TAG_SUBBUFFER_SIZE ((xmlChar *)"subbuffer_size") +#define TAG_TERMINAL ((xmlChar *)"terminal") +#define TAG_TIMING_MASTER ((xmlChar *)"timing_master") + +#define VAL_AIM_TYPE_CDEV ((char*)"CDEV") +#define VAL_AIM_TYPE_AUDIO ((char*)"AUDIO") +#define VAL_AIM_TYPE_V4L ((char*)"V4L") + +#define VAL_PORT_MLB ((char*)"MLB") +#define VAL_PORT_TSI ((char*)"TSI") +#define VAL_PORT_I2S ((char*)"I2S") +#define VAL_PORT_I2C ((char*)"I2C") +#define VAL_PORT_USB ((char*)"USB") +#define VAL_PORT_PCIE ((char*)"PCIE") +#define VAL_PORT_MOST ((char*)"MOST") + +#define VAL_DATATYPE_SYNC ((char*)"SYNC") +#define VAL_DATATYPE_ISOC ((char*)"ISOC") +#define VAL_DATATYPE_ASYNC ((char*)"ASYNC") +#define VAL_DATATYPE_CTRL ((char*)"CTRL") + +#define VAL_DIR_IN ((char*)"IN") +#define VAL_DIR_OUT ((char*)"OUT") + +#define VAL_TSI_V1_PORT_0 ((char*)"TSI_PORT0") +#define VAL_TSI_V1_PORT_1 ((char*)"TSI_PORT1") + +#define VAL_TSI_V1_SLAVE ((char*)"TSI_SLAVE") +#define VAL_TSI_V1_MASTER ((char*)"TSI_MASTER") + +#define VAL_I2S_V1_CLK_OUTPUT ((char*)"Output") +#define VAL_I2S_V1_CLK_INPUT ((char*)"Input") +#define VAL_I2S_V1_CLK_ISOCOUT ((char*)"IsocOutput") +#define VAL_I2S_V1_CLK_ISOCIN ((char*)"IsocInput") + +#define VAL_I2S_V1_OPTION_INOUT ((char*)"InOut") +#define VAL_I2S_V1_OPTION_DUALIN ((char*)"DualIn") +#define VAL_I2S_V1_OPTION_DUALOUT ((char*)"DualOut") +#define VAL_I2S_V1_OPTION_FULL ((char*)"FullStream") +#define VAL_I2S_V1_OPTION_WILDCARD ((char*)"Wildcard") + +#define VAL_I2S_V1_FMT_D64Fs16 ((char*)"Delay64Fs16Bit") +#define VAL_I2S_V1_FMT_D64Fs24 ((char*)"Delay64Fs24Bit") +#define VAL_I2S_V1_FMT_D64FsSeq ((char*)"Delay64FsSeq") +#define VAL_I2S_V1_FMT_D128FsSeq ((char*)"Delay128FsSeq") +#define VAL_I2S_V1_FMT_D256FsSeq ((char*)"Delay256FsSeq") +#define VAL_I2S_V1_FMT_R64Fs16 ((char*)"Right64Fs16Bit") +#define VAL_I2S_V1_FMT_R64Fs24 ((char*)"Right64Fs24Bit") +#define VAL_I2S_V1_FMT_Seq64Fs ((char*)"Seq64Fs") +#define VAL_I2S_V1_FMT_Seq128Fs ((char*)"Seq128Fs") +#define VAL_I2S_V1_FMT_Seq256Fs ((char*)"Seq256Fs") +#define VAL_I2S_V1_FMT_L64Fs16 ((char*)"Left64Fs16Bit") +#define VAL_I2S_V1_FMT_L64Fs24 ((char*)"Left64Fs24Bit") +#define VAL_I2S_V1_FMT_D512FsSeq ((char*)"Delay512FsSeq") +#define VAL_I2S_V1_FMT_Seq512Fs ((char*)"Seq512Fs") + +#define VAL_I2S_V1_PIN_SR0 ((char*)"SR0Pin") +#define VAL_I2S_V1_PIN_SX0 ((char*)"SX0Pin") +#define VAL_I2S_V1_PIN_SR1 ((char*)"SR1Pin") +#define VAL_I2S_V1_PIN_SX1 ((char*)"SX1Pin") +#define VAL_I2S_V1_PIN_SR2 ((char*)"SR2Pin") +#define VAL_I2S_V1_PIN_SX2 ((char*)"SX2Pin") +#define VAL_I2S_V1_PIN_SR3 ((char*)"SR3Pin") +#define VAL_I2S_V1_PIN_SX3 ((char*)"SX3Pin") +#define VAL_I2S_V1_PIN_FSY_SCK ((char*)"FSY_SCK_Pins") + +#define VAL_I2S_OPTION_INOUT ((char*)"InOut") +#define VAL_I2S_OPTION_DUALIN ((char*)"DualIn") +#define VAL_I2S_OPTION_DUALOUT ((char*)"DualOut") +#define VAL_I2S_OPTION_WILDCARD ((char*)"Wildcard") + +#define VAL_I2S_CLOCK_OUTPUT ((char*)"Output") +#define VAL_I2S_CLOCK_INPUT ((char*)"Input") +#define VAL_I2S_CLOCK_WILDCARD ((char*)"Wildcard") + +#define VAL_I2S_ALIGNMENT_L16 ((char*)"Left16") +#define VAL_I2S_ALIGNMENT_L24 ((char*)"Left24") +#define VAL_I2S_ALIGNMENT_R16 ((char*)"Right16") +#define VAL_I2S_ALIGNMENT_R24 ((char*)"Right24") +#define VAL_I2S_SEQUENTIAL ((char*)"Seq") + +#define VAL_I2S_PIN_SRXA0 ((char*)"SRXA0") +#define VAL_I2S_PIN_SRXA1 ((char*)"SRXA1") +#define VAL_I2S_PIN_SRXB0 ((char*)"SRXB0") +#define VAL_I2S_PIN_SRXB1 ((char*)"SRXB1") + +#define UNKNOWN_ENDPOINT_ADDR 0xFF + +#define XML_STRCMP(x,y) (strcmp(reinterpret_cast< const char* >(x), reinterpret_cast< const char* >(y)) == 0) + +CVodXml::CVodXml( const char *szFile ) : CXml( szFile ) +{ + +} + +bool CVodXml::GetLocalInicConfigurations( uint8_t *rxEndpoint, uint8_t *txEndpoint ) +{ + if( NULL == rxEndpoint || NULL == txEndpoint ) + return false; + *rxEndpoint = UNKNOWN_ENDPOINT_ADDR; + *txEndpoint = UNKNOWN_ENDPOINT_ADDR; + CXml rootXml( *( ( CXml * )this ) ); + bool success = rootXml.SetToTopNode(); + if( success && rootXml.FindNode( TAG_NETWORK_MANAGER ) ) + { + CXml xmlNWM( rootXml ); + success = xmlNWM.FindFirstChildNode(); + while( success && xmlNWM.FindNode( TAG_ENDPOINT ) ) + { + ConsolePrintf( PRIO_LOW, "NETWORK_MANAGER\r\n" ); + xmlChar interface[XML_DEFAULT_VALUE_LEN]; + if( xmlNWM.GetChildValue( TAG_PORT_ID, interface ) ) + { + if( XML_STRCMP(interface, VAL_PORT_I2C) ) + { + ConsolePrintf( PRIO_LOW, + "GetLocalInicConfigurations found I2C, returning without channel addresses\r\n" ); + return true; + } + } + xmlChar dataType[XML_DEFAULT_VALUE_LEN]; + xmlChar direction[XML_DEFAULT_VALUE_LEN]; + dataType[0] = '\0'; + direction[0] = '\0'; + int epAddress; + success &= xmlNWM.GetChildValue( TAG_DATA_TYPE, dataType ); + ConsolePrintf( PRIO_LOW, " Type:%s\r\n", dataType ); + success &= xmlNWM.GetChildValue( TAG_DIR, direction ); + ConsolePrintf( PRIO_LOW, " Direction:%s\r\n", direction ); + if( !xmlNWM.GetChildValueInt( TAG_ADDRESS, epAddress ) ) + { + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( xmlNWM.GetChildValue( TAG_ADDRESS_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + epAddress = ConvertToInt( szTmp ); + } + ConsolePrintf( PRIO_LOW, " Address:%d\r\n", epAddress ); + + if( success ) + { + if( XML_STRCMP(dataType, VAL_DATATYPE_CTRL) ) + { + if( XML_STRCMP(direction, VAL_DIR_IN) ) + { + *rxEndpoint = epAddress; + } + else if( XML_STRCMP(direction, VAL_DIR_OUT) ) + { + *txEndpoint = epAddress; + } + } + } + else + { + ConsolePrintf( PRIO_ERROR, "Failed to retrieve Endpoint config\n" ); + } + xmlNWM.FindNextNode(); + } + } + return success && ( 0xFF != *rxEndpoint ) && ( 0xFF != *txEndpoint ); +} + +bool CVodXml::GetMostParameters(bool *isTimingMaster, uint32_t *asyncBandwidth) +{ + if( NULL == isTimingMaster || NULL == asyncBandwidth ) + { + ConsolePrintf( PRIO_ERROR, RED"GetMostParameters was"\ + " called with wrong parameters"RESETCOLOR"\n" ); + return false; + } + uint8_t serverNum = 0; + CXml xmlNWM( *( ( CXml * )this ) ); + bool success = xmlNWM.SetToTopNode(); + if( success && xmlNWM.FindNode( TAG_NETWORK_MANAGER ) ) + { + success = xmlNWM.FindFirstChildNode(); + while( success && xmlNWM.FindNode( TAG_LOCAL_CONNECTION ) ) + { + serverNum += 1; + xmlNWM.FindNextNode(); + } + } + //Be compatible to old XML API + if( !success || 0 == serverNum ) + { + success = GetNetworkConfiguration( isTimingMaster, asyncBandwidth ); + return success; + } + + if( success ) + success = xmlNWM.SetToTopNode(); + if( success ) + success = xmlNWM.FindNode( TAG_NETWORK_MANAGER ); + if( success ) + success = xmlNWM.FindFirstChildNode(); + + if( success && xmlNWM.FindNode( TAG_LOCAL_CONNECTION ) ) + { + CXml xmlLC( xmlNWM ); + int bw; + int tm = true; + success &= xmlNWM.GetChildValueInt( TAG_TIMING_MASTER, tm ); + if (success) + { + ConsolePrintf( PRIO_LOW, " Timing Master:%d\r\n", tm ); + success &= xmlNWM.GetChildValueInt( TAG_ASYNC_BANDWIDTH, bw ); + if (success) + { + *isTimingMaster = tm; + *asyncBandwidth = bw; + ConsolePrintf( PRIO_LOW, "Asynchronous Bandwidth:%d\r\n", bw ); + } + } + } + return success; +} + +bool CVodXml::GetLocalInicConfigurations( uint8_t *rxEndpoint[], uint8_t *txEndpoint[], uint32_t *apiVersion[], + SocketPort_t *hwPort[], bool *isTimingMaster[], uint32_t *asyncBandwidth[], uint8_t *serverNum ) +{ + if( NULL == rxEndpoint || NULL == txEndpoint || NULL == serverNum || + NULL == apiVersion || NULL == hwPort || NULL == isTimingMaster || + NULL == asyncBandwidth ) + { + ConsolePrintf( PRIO_ERROR, RED"GetLocalInicConfigurations was"\ + " called with wrong parameters"RESETCOLOR"\n" ); + return false; + } + *rxEndpoint = NULL; + *txEndpoint = NULL; + *apiVersion = NULL; + *hwPort = NULL; + *isTimingMaster = NULL; + *asyncBandwidth = NULL; + *serverNum = 0; + CXml xmlNWM( *( ( CXml * )this ) ); + bool success = xmlNWM.SetToTopNode(); + if( success && xmlNWM.FindNode( TAG_NETWORK_MANAGER ) ) + { + success = xmlNWM.FindFirstChildNode(); + while( success && xmlNWM.FindNode( TAG_LOCAL_CONNECTION ) ) + { + *serverNum = *serverNum + 1; + xmlNWM.FindNextNode(); + } + } + //Be compatible to old XML API + if( !success || 0 == *serverNum ) + { + uint8_t rxEP; + uint8_t txEP; + bool tm; + uint32_t bw; + success = GetLocalInicConfigurations( &rxEP, &txEP ); + if( success ) + success = GetNetworkConfiguration( &tm, &bw ); + + if( success ) + { + *rxEndpoint = ( uint8_t * )calloc( 1, sizeof( uint8_t ) ); + *txEndpoint = ( uint8_t * )calloc( 1, sizeof( uint8_t ) ); + *apiVersion = ( uint32_t * )calloc( 1, sizeof( uint32_t ) ); + *hwPort = ( SocketPort_t * )calloc( 1, sizeof( SocketPort_t ) ); + *isTimingMaster = ( bool * )calloc( 1, sizeof( bool ) ); + *asyncBandwidth = ( uint32_t * )calloc( 1, sizeof( uint32_t ) ); + ( *rxEndpoint )[0] = rxEP; + ( *txEndpoint )[0] = txEP; + ( *apiVersion )[0] = GetServerDeviceApi(); + ( *hwPort )[0] = GetServerHardwareInterface(); + ( *isTimingMaster )[0] = tm; + ( *asyncBandwidth )[0] = bw; + *serverNum = 1; + } + return success; + } + //New method to get multiple instances of local configurations + *rxEndpoint = ( uint8_t * )calloc( *serverNum, sizeof( uint8_t ) ); + *txEndpoint = ( uint8_t * )calloc( *serverNum, sizeof( uint8_t ) ); + *apiVersion = ( uint32_t * )calloc( *serverNum, sizeof( uint32_t ) ); + *hwPort = ( SocketPort_t * )calloc( *serverNum, sizeof( SocketPort_t ) ); + *isTimingMaster = ( bool * )calloc( *serverNum, sizeof( bool ) ); + *asyncBandwidth = ( uint32_t * )calloc( *serverNum, sizeof( uint32_t ) ); + + if( success ) + success = xmlNWM.SetToTopNode(); + if( success ) + success = xmlNWM.FindNode( TAG_NETWORK_MANAGER ); + if( success ) + success = xmlNWM.FindFirstChildNode(); + + uint8_t currentCon = 0; + while( success && xmlNWM.FindNode( TAG_LOCAL_CONNECTION ) && currentCon < *serverNum ) + { + int apiVer = -1; + CXml xmlLC( xmlNWM ); + if( xmlLC.GetChildValueInt( TAG_DEVICE_API_VERSION, apiVer ) ) + { + ( *apiVersion )[currentCon] = ( uint32_t )apiVer; + ConsolePrintf( PRIO_LOW, "[Con:%d] API Version: %d\r\n", currentCon, apiVer ); + } + ( *hwPort )[currentCon] = PORT_UNKNOWN; + xmlChar interface[XML_DEFAULT_VALUE_LEN]; + if( xmlLC.GetChildValue( TAG_PORT_ID, interface ) ) + { + ConsolePrintf( PRIO_LOW, "[Con:%d] HW Type: %s\r\n", currentCon, interface ); + + if( XML_STRCMP(interface, VAL_PORT_MLB) ) + ( *hwPort )[currentCon] = PORT_MLB; + else if( XML_STRCMP(interface, VAL_PORT_TSI) ) + ( *hwPort )[currentCon] = PORT_TSI; + else if( XML_STRCMP(interface, VAL_PORT_USB) ) + ( *hwPort )[currentCon] = PORT_USB; + else if( XML_STRCMP(interface, VAL_PORT_I2C) ) + ( *hwPort )[currentCon] = PORT_I2C; + } + int isMaster = true; + int bw = 40; + success &= xmlNWM.GetChildValueInt( TAG_TIMING_MASTER, isMaster ); + ConsolePrintf( PRIO_LOW, "[Con:%d] Timing Master:%d\r\n", currentCon, isMaster ); + success &= xmlNWM.GetChildValueInt( TAG_ASYNC_BANDWIDTH, bw ); + ConsolePrintf( PRIO_LOW, "[Con:%d] Asynchronous Bandwidth:%d\r\n", currentCon, bw ); + ( *isTimingMaster )[currentCon] = isMaster; + ( *asyncBandwidth )[currentCon] = bw; + success = xmlLC.FindFirstChildNode(); + while( success && xmlLC.FindNode( TAG_ENDPOINT ) ) + { + xmlChar dataType[XML_DEFAULT_VALUE_LEN]; + xmlChar direction[XML_DEFAULT_VALUE_LEN]; + dataType[0] = '\0'; + direction[0] = '\0'; + int epAddress; + success &= xmlLC.GetChildValue( TAG_DATA_TYPE, dataType ); + ConsolePrintf( PRIO_LOW, "[Con:%d] Type:%s\r\n", currentCon, dataType ); + success &= xmlLC.GetChildValue( TAG_DIR, direction ); + ConsolePrintf( PRIO_LOW, "[Con:%d] Direction:%s\r\n", currentCon, direction ); + if( !xmlLC.GetChildValueInt( TAG_ADDRESS, epAddress ) ) + { + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( xmlLC.GetChildValue( TAG_ADDRESS_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + epAddress = ConvertToInt( szTmp ); + } + ConsolePrintf( PRIO_LOW, "[Con:%d] Address:%d\r\n", currentCon, epAddress ); + + if( success ) + { + if( XML_STRCMP(dataType, VAL_DATATYPE_CTRL) ) + { + if( XML_STRCMP(direction, VAL_DIR_IN) ) + { + ( *rxEndpoint )[currentCon] = epAddress; + } + else if( XML_STRCMP(direction, VAL_DIR_OUT) ) + { + ( *txEndpoint )[currentCon] = epAddress; + } + } + } + else + { + ConsolePrintf( PRIO_ERROR, "Failed to retrieve Endpoint config\n" ); + } + xmlLC.FindNextNode(); + } + currentCon++; + xmlNWM.FindNextNode(); + } + return success; +} + +SocketPort_t CVodXml::GetServerHardwareInterface() +{ + //Be compatible to old configurations, which had no interface declaration + SocketPort_t hwInterface = PORT_USB; + CXml rootXml( *( ( CXml * )this ) ); + bool success = rootXml.SetToTopNode(); + if( success && rootXml.FindNode( TAG_NETWORK_MANAGER ) ) + { + CXml xmlNWM( rootXml ); + success = xmlNWM.FindFirstChildNode(); + if( success && xmlNWM.FindNode( TAG_ENDPOINT ) ) + { + xmlChar interface[XML_DEFAULT_VALUE_LEN]; + + if( xmlNWM.GetChildValue( TAG_PORT_ID, interface ) ) + { + ConsolePrintf( PRIO_LOW, " - Port ID : %s\r\n", interface ); + if( XML_STRCMP(interface, VAL_PORT_MLB) ) + hwInterface = PORT_MLB; + else if( XML_STRCMP(interface, VAL_PORT_USB) ) + hwInterface = PORT_USB; + else if( XML_STRCMP(interface, VAL_PORT_I2C) ) + hwInterface = PORT_I2C; + } + } + } + return hwInterface; +} + +uint32_t CVodXml::GetServerDeviceApi() +{ + //Be compatible to old configurations, which had no API declaration + uint32_t deviceApi = 2; + CXml rootXml( *( ( CXml * )this ) ); + bool success = rootXml.SetToTopNode(); + if( success && rootXml.FindNode( TAG_NETWORK_MANAGER ) ) + { + CXml xmlNWM( rootXml ); + success = xmlNWM.FindFirstChildNode(); + if( success && xmlNWM.FindNode( TAG_ENDPOINT ) ) + { + ConsolePrintf( PRIO_LOW, "NETWORK_MANAGER\r\n" ); + int apiVer; + if( xmlNWM.GetChildValueInt( TAG_DEVICE_API_VERSION, apiVer ) ) + { + deviceApi = apiVer; + ConsolePrintf( PRIO_LOW, " - Device API Version: %d\r\n", apiVer ); + } + } + } + return deviceApi; +} + +uint32_t CVodXml::GetDeviceApi( uint32_t deviceType ) +{ + uint32_t deviceApi = 0xFFFFFFFF; + bool success = true; + CXml Xml( *( ( CXml * )this ) ); + success = Xml.SetToTopNode(); + success &= Xml.FindNode( TAG_NETWORK_MANAGER ); + success &= Xml.FindFirstChildNode(); + + while( success && Xml.FindNode( TAG_DEVICE ) ) + { + int xmlDeviceType = -1; + int xmlDeviceApiVer = -1; + if( !Xml.GetChildValueInt( TAG_DEVICE_TYPE, xmlDeviceType ) ) + { + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( Xml.GetChildValue( TAG_DEVICE_TYPE_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + xmlDeviceType = ConvertToInt( szTmp ); + } + if( ( uint32_t )xmlDeviceType == deviceType ) + { + success &= Xml.GetChildValueInt( TAG_DEVICE_API_VERSION, xmlDeviceApiVer ); + if( success ) + { + ConsolePrintf( PRIO_LOW, "Device API Version %d for device type: 0x%X\r\n", xmlDeviceApiVer, + xmlDeviceType ); + deviceApi = xmlDeviceApiVer; + break; + } + } + Xml.FindNextNode(); + } + return deviceApi; +} + +MlbPortSpeed_t CVodXml::GetDeviceMlbPortSpeed( uint32_t deviceType ) +{ + MlbPortSpeed_t mlbSpeed = MlbSpeedNotSet; + bool success = true; + CXml Xml( *( ( CXml * )this ) ); + success = Xml.SetToTopNode(); + success &= Xml.FindNode( TAG_NETWORK_MANAGER ); + success &= Xml.FindFirstChildNode(); + + while( success && Xml.FindNode( TAG_DEVICE ) ) + { + int xmlDeviceType; + int xmlDeviceMlbPortSpeed = -1; + if( !Xml.GetChildValueInt( TAG_DEVICE_TYPE, xmlDeviceType ) ) + { + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( Xml.GetChildValue( TAG_DEVICE_TYPE_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + xmlDeviceType = ConvertToInt( szTmp ); + } + ConsolePrintf( PRIO_LOW, "DeviceType: %d\r\n", xmlDeviceType ); + if( ( uint32_t )xmlDeviceType == deviceType ) + { + success &= Xml.GetChildValueInt( TAG_DEVICE_MLB_SPEED, xmlDeviceMlbPortSpeed ); + if( success ) + { + ConsolePrintf( PRIO_LOW, "Device MLB speed %d for device type: 0x%X\r\n", xmlDeviceMlbPortSpeed, + xmlDeviceType ); + switch( xmlDeviceMlbPortSpeed ) + { + case 256: + mlbSpeed = MlbSpeed256FS; + break; + case 512: + mlbSpeed = MlbSpeed512FS; + break; + case 1024: + mlbSpeed = MlbSpeed1024FS; + break; + case 2048: + mlbSpeed = MlbSpeed2048FS; + break; + case 3072: + mlbSpeed = MlbSpeed3072FS; + break; + case 4096: + mlbSpeed = MlbSpeed4096FS; + break; + case 6144: + mlbSpeed = MlbSpeed6144FS; + break; + case 8192: + mlbSpeed = MlbSpeed8192FS; + break; + default: + mlbSpeed = MlbSpeedNotSet; + break; + } + break; + } + } + Xml.FindNextNode(); + } + return mlbSpeed; +} + +bool CVodXml::GetNetworkConfiguration( bool *isTimingMaster, uint32_t *asyncBandwidth ) +{ + if( NULL == isTimingMaster || NULL == asyncBandwidth ) + return false; + *isTimingMaster = true; + *asyncBandwidth = 40; + CXml rootXml( *( ( CXml * )this ) ); + bool success = rootXml.SetToTopNode(); + if( success && rootXml.FindNode( TAG_NETWORK_MANAGER ) ) + { + CXml xmlNWM( rootXml ); + success = xmlNWM.FindFirstChildNode(); + while( success && xmlNWM.FindNode( TAG_NETWORK_CONFIG ) ) + { + ConsolePrintf( PRIO_LOW, "NETWORK_MANAGER\r\n" ); + int isMaster; + int bw; + success &= xmlNWM.GetChildValueInt( TAG_TIMING_MASTER, isMaster ); + ConsolePrintf( PRIO_LOW, " Is Timing Master:%d\r\n", isMaster ); + success &= xmlNWM.GetChildValueInt( TAG_ASYNC_BANDWIDTH, bw ); + ConsolePrintf( PRIO_LOW, " Asynchronous Bandwidth:%d\r\n", bw ); + if( success ) + { + *isTimingMaster = isMaster; + *asyncBandwidth = bw; + break; + } + else + { + ConsolePrintf( PRIO_ERROR, "Failed to retrieve Network config\n" ); + } + xmlNWM.FindNextNode(); + } + } + return success; +} + + +bool CVodXml::GetChannelConfigurations( uint32_t deviceType, CSafeVector &allChannels ) +{ + bool success = false; + bool found = false; + CXml Xml( *( ( CXml * )this ) ); + success = Xml.SetToTopNode(); + success &= Xml.FindNode( TAG_NETWORK_MANAGER ); + success &= Xml.FindFirstChildNode(); + + while( !found && Xml.FindNode( TAG_DEVICE ) ) + { + int xmlDeviceType = -1; + char *externalScript = NULL; + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( !Xml.GetChildValueInt( TAG_DEVICE_TYPE, xmlDeviceType ) ) + { + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( Xml.GetChildValue( TAG_DEVICE_TYPE_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + xmlDeviceType = ConvertToInt( szTmp ); + } + if( ( uint32_t )xmlDeviceType != deviceType ) + { + Xml.FindNextNode(); + continue; + } + ConsolePrintf( PRIO_LOW, "DeviceType: %d (0x%X)\r\n", xmlDeviceType, xmlDeviceType ); + szTmp[0] = '\0'; + if( Xml.GetChildValue( TAG_SCRIPT, szTmp ) ) + { + int scriptLen = xmlStrlen( szTmp ); + externalScript = ( char * )malloc( scriptLen + 1 ); + if( NULL != externalScript ) + { + memcpy( externalScript, szTmp, scriptLen ); + externalScript[scriptLen] = '\0'; + } + ConsolePrintf( PRIO_LOW, "External script:'%s'\r\n", externalScript ); + } + found = true; + CXml Channel( Xml ); + success = Channel.FindFirstChildNode(); + + while( success && Channel.FindNode( TAG_CHANNEL ) ) + { + int channelId; + success &= Channel.GetChildValueInt( TAG_CHANNEL_ID, channelId ); + if( success ) + { + ConsolePrintf( PRIO_LOW, " Channel: %u\r\n", channelId ); + CChannelConfiguration *channelConfig = new CChannelConfiguration(); + allChannels.PushBack( channelConfig ); + channelConfig->deviceType = xmlDeviceType; + channelConfig->channelId = channelId; + channelConfig->externalScipt = externalScript; + CXml Socket( Channel ); + success = Socket.FindFirstChildNode(); + while( Socket.FindNode( TAG_SOCKET ) ) + { + ConsolePrintf( PRIO_LOW, " Socket:\r\n" ); + int nTmp; + CSocketConfiguration *sockConfig = NULL; + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_DIR, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Direction : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_DIR_IN) ) + sockConfig = &channelConfig->inSocket; + else if( XML_STRCMP(szTmp, VAL_DIR_OUT) ) + sockConfig = &channelConfig->outSocket; + } + if( NULL == sockConfig ) + { + ConsolePrintf( PRIO_ERROR, RED"VodXml can not detect socket direction"RESETCOLOR"\n" ); + return false; //unknown target socket + } + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_PORT_ID, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Port ID : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_PORT_MLB) ) + sockConfig->port = PORT_MLB; + else if( XML_STRCMP(szTmp, VAL_PORT_TSI) ) + sockConfig->port = PORT_TSI; + else if( XML_STRCMP(szTmp, VAL_PORT_I2S) ) + sockConfig->port = PORT_I2S; + else if( XML_STRCMP(szTmp, VAL_PORT_USB) ) + sockConfig->port = PORT_USB; + else if( XML_STRCMP(szTmp, VAL_PORT_PCIE) ) + sockConfig->port = PORT_PCIE; + else if( XML_STRCMP(szTmp, VAL_PORT_MOST) ) + sockConfig->port = PORT_MOST; + else + sockConfig->port = PORT_UNKNOWN; + } + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_DATA_TYPE, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Data Type : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_DATATYPE_SYNC) ) + sockConfig->type = EP_Synchron; + else if( XML_STRCMP(szTmp, VAL_DATATYPE_ISOC) ) + sockConfig->type = EP_Isochron; + else if( XML_STRCMP(szTmp, VAL_DATATYPE_ASYNC) ) + sockConfig->type = EP_Asynchron; + else if( XML_STRCMP(szTmp, VAL_DATATYPE_CTRL) ) + sockConfig->type = EP_Control; + else + sockConfig->type = EP_Unknown; + } + + if( Socket.GetChildValueInt( TAG_OFFSET, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Splitted Offset: %d\r\n", nTmp ); + sockConfig->splittedOffset = nTmp; + } + + if( Socket.GetChildValueInt( TAG_BLOCKWIDTH, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Blockwidth: %d\r\n", nTmp ); + sockConfig->blockWidth = nTmp; + } + + if( Socket.GetChildValueInt( TAG_ADDRESS, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Channel Address: 0x%X\r\n", nTmp ); + sockConfig->address = nTmp; + } + else if( Socket.GetChildValue( TAG_ADDRESS_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + { + ConsolePrintf( PRIO_LOW, " - Channel Address (hex): 0x%X\r\n", nTmp ); + sockConfig->address = ConvertToInt( szTmp ); + } + + if( Socket.GetChildValueInt( TAG_NUMBER_OF_BUFFERS, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Number of Buffers: %d\r\n", nTmp ); + sockConfig->amountOfBuffers = nTmp; + } + + if( Socket.GetChildValueInt( TAG_BUFFER_SIZE, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Buffer Size: %d\r\n", nTmp ); + sockConfig->bufferSize = nTmp; + } + + if( Socket.GetChildValueInt( TAG_PACKETS_XACT, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Packets per Transaction: %d\r\n", nTmp ); + sockConfig->packetsPerTransaction = nTmp; + } + else + { + if( Socket.GetChildValue( TAG_PACKETS_XACT_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + { + sockConfig->packetsPerTransaction = ConvertToInt( szTmp ); + ConsolePrintf( PRIO_LOW, " - Packets per Transaction: 0x%X\r\n", sockConfig->packetsPerTransaction ); + } + } + + if( Socket.GetChildValueInt( TAG_SUBBUFFER_SIZE, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - Subbuffer Size: %d\r\n", nTmp ); + sockConfig->subbufferSize = nTmp; + } + + if( Socket.GetChildValue( TAG_AIM_TYPE, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - TAG_AIM_TYPE : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_AIM_TYPE_CDEV) ) + sockConfig->aimType = AIM_CDEV; + else if( XML_STRCMP(szTmp, VAL_AIM_TYPE_AUDIO) ) + sockConfig->aimType = AIM_AUDIO; + else if( XML_STRCMP(szTmp, VAL_AIM_TYPE_V4L) ) + sockConfig->aimType = AIM_V4L; + else + sockConfig->aimType = AIM_UNKNOWN; + } + + if( PORT_TSI == sockConfig->port ) + { + if( Socket.GetChildValue( TAG_TSI_PORT_ID, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - TSI Port ID : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_TSI_V1_PORT_0) ) + sockConfig->tsiConfig.tsiPortInstance = V1TsiPortInstance0; + else if( XML_STRCMP(szTmp, VAL_TSI_V1_PORT_1) ) + sockConfig->tsiConfig.tsiPortInstance = V1TsiPortInstance1; + } + if( Socket.GetChildValue( TAG_TSI_PORT_MODE, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - TSI Port Mode : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_TSI_V1_SLAVE) ) + sockConfig->tsiConfig.tsiPortMode = V1TsiPortModeSlave; + else if( XML_STRCMP(szTmp, VAL_TSI_V1_MASTER) ) + sockConfig->tsiConfig.tsiPortMode = V1TsiPortModeMaster; + } + } + else if( PORT_I2S == sockConfig->port ) + { + sockConfig->i2sConfig.portClkDriveModeV1 = V1I2sClockModeUnknown; + sockConfig->i2sConfig.portOptionV1 = V1I2sStreamingPortWildcard; //Be backward compatible + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sDataFormatUnknown; + sockConfig->i2sConfig.pinV1 = V1I2sInvalidPin; + sockConfig->i2sConfig.portOption = V3I2SOptionUnknown; + sockConfig->i2sConfig.clockMode = V3I2SClockModeUnknown; + sockConfig->i2sConfig.delayMode = V3I2SDelayUnknown; + sockConfig->i2sConfig.portSpeed = V3I2SSpeedUnknown; + sockConfig->i2sConfig.alignment = V3I2SAlignUnknown; + sockConfig->i2sConfig.pin = V3I2SInvalidPin; + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_I2S_V1_CLKDRIVEMODE, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S V1 clock drive mode : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_V1_CLK_OUTPUT) ) + sockConfig->i2sConfig.portClkDriveModeV1 = V1I2sClockModeOutput; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_CLK_INPUT) ) + sockConfig->i2sConfig.portClkDriveModeV1 = V1I2sClockModeInput; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_CLK_ISOCOUT) ) + sockConfig->i2sConfig.portClkDriveModeV1 = V1I2sClockModeIsocOutput; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_CLK_ISOCIN) ) + sockConfig->i2sConfig.portClkDriveModeV1 = V1I2sClockModeIsocInput; + } + + if( Socket.GetChildValue( TAG_I2S_V1_OPTION, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S V1 port option : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_V1_OPTION_INOUT ) ) + sockConfig->i2sConfig.portOptionV1 = V1I2sStreamingPortInOut; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_OPTION_DUALIN ) ) + sockConfig->i2sConfig.portOptionV1 = V1I2sStreamingPortDualIn; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_OPTION_DUALOUT ) ) + sockConfig->i2sConfig.portOptionV1 = V1I2sStreamingPortDualOut; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_OPTION_FULL ) ) + sockConfig->i2sConfig.portOptionV1 = V1I2sStreamingPortFullStream; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_OPTION_WILDCARD ) ) + sockConfig->i2sConfig.portOptionV1 = V1I2sStreamingPortWildcard; + } + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_I2S_V1_DATAFORMAT, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S V1 data format : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_D64Fs16) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sDelay64Fs16Bit; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_D64Fs24) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sDelay64Fs24Bit; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_D64FsSeq) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sDelay64FsSeq; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_D128FsSeq) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sDelay128FsSeq; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_D256FsSeq) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sDelay256FsSeq; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_R64Fs16) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sRight64Fs16Bit; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_R64Fs24) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sRight64Fs24Bit; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_Seq64Fs) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sSeq64Fs; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_Seq128Fs) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sSeq128Fs; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_Seq256Fs) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sSeq256Fs; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_L64Fs16) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sLeft64Fs16Bit; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_L64Fs24) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sLeft64Fs24Bit; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_D512FsSeq) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sDelay512FsSeq; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_FMT_Seq512Fs) ) + sockConfig->i2sConfig.streamingDataFormatV1 = V1I2sSeq512Fs; + } + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_I2S_V1_PIN, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S V1 pin : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SR0) ) + sockConfig->i2sConfig.pinV1 = V1I2sSR0Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SX0) ) + sockConfig->i2sConfig.pinV1 = V1I2sSX0Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SR1) ) + sockConfig->i2sConfig.pinV1 = V1I2sSR1Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SX1) ) + sockConfig->i2sConfig.pinV1 = V1I2sSX1Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SR2) ) + sockConfig->i2sConfig.pinV1 = V1I2sSR2Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SX2) ) + sockConfig->i2sConfig.pinV1 = V1I2sSX2Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SR3) ) + sockConfig->i2sConfig.pinV1 = V1I2sSR3Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_SX3) ) + sockConfig->i2sConfig.pinV1 = V1I2sSX3Pin; + else if( XML_STRCMP(szTmp, VAL_I2S_V1_PIN_FSY_SCK) ) + sockConfig->i2sConfig.pinV1 = V1I2sFSYSCKPins; + } + + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_I2S_OPTION, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S OPTION : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_OPTION_INOUT) ) + sockConfig->i2sConfig.portOption = V3I2SOptionInOut; + else if( XML_STRCMP(szTmp, VAL_I2S_OPTION_DUALIN) ) + sockConfig->i2sConfig.portOption = V3I2SOptionDualIn; + else if( XML_STRCMP(szTmp, VAL_I2S_OPTION_DUALOUT) ) + sockConfig->i2sConfig.portOption = V3I2SOptionDualOut; + } + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_I2S_CLOCK_DIR, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S clock direction : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_CLOCK_OUTPUT) ) + sockConfig->i2sConfig.clockMode = V3I2SClockModeOutput; + else if( XML_STRCMP(szTmp, VAL_I2S_CLOCK_INPUT) ) + sockConfig->i2sConfig.clockMode = V3I2SClockModeInput; + else if( XML_STRCMP(szTmp, VAL_I2S_OPTION_WILDCARD) ) + sockConfig->i2sConfig.clockMode = V3I2SClockModeWildcard; + } + + if( Socket.GetChildValueInt( TAG_I2S_CLOCK_DELAY, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S clock delay: %d\r\n", nTmp ); + if( 0 == nTmp ) + sockConfig->i2sConfig.delayMode = V3I2SDelayOff; + else if( 1 == nTmp ) + sockConfig->i2sConfig.delayMode = V3I2SDelayEnabled; + else if( 2 == nTmp ) + sockConfig->i2sConfig.delayMode = V3I2SDelayWildcard; + } + + if( Socket.GetChildValueInt( TAG_I2S_FS_SPEED, nTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S FS speed: %d\r\n", nTmp ); + switch( nTmp ) + { + case 64: + sockConfig->i2sConfig.portSpeed = V3I2SSpeed64FS; + break; + case 128: + sockConfig->i2sConfig.portSpeed = V3I2SSpeed128FS; + break; + case 256: + sockConfig->i2sConfig.portSpeed = V3I2SSpeed256FS; + break; + case 512: + sockConfig->i2sConfig.portSpeed = V3I2SSpeed512FS; + break; + case 0: + sockConfig->i2sConfig.portSpeed = V3I2SSpeedWildcard; + break; + } + } + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_I2S_ALIGN, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S alignment : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_ALIGNMENT_L16) ) + sockConfig->i2sConfig.alignment = V3I2SAlignLeft16; + else if( XML_STRCMP(szTmp, VAL_I2S_ALIGNMENT_L24) ) + sockConfig->i2sConfig.alignment = V3I2SAlignLeft24; + else if( XML_STRCMP(szTmp, VAL_I2S_ALIGNMENT_R16) ) + sockConfig->i2sConfig.alignment = V3I2SAlignRight16; + else if( XML_STRCMP(szTmp, VAL_I2S_ALIGNMENT_R24) ) + sockConfig->i2sConfig.alignment = V3I2SAlignRight24; + else if( XML_STRCMP(szTmp, VAL_I2S_SEQUENTIAL) ) + sockConfig->i2sConfig.alignment = V3I2SAlignSeq; + } + + szTmp[0] = '\0'; + if( Socket.GetChildValue( TAG_I2S_PIN, szTmp ) ) + { + ConsolePrintf( PRIO_LOW, " - I2S Pin : %s\r\n", szTmp ); + if( XML_STRCMP(szTmp, VAL_I2S_PIN_SRXA0) ) + sockConfig->i2sConfig.pin = V3I2SSRXA0; + else if( XML_STRCMP(szTmp, VAL_I2S_PIN_SRXA1) ) + sockConfig->i2sConfig.pin = V3I2SSRXA1; + else if( XML_STRCMP(szTmp, VAL_I2S_PIN_SRXB0) ) + sockConfig->i2sConfig.pin = V3I2SSRXB0; + else if( XML_STRCMP(szTmp, VAL_I2S_PIN_SRXB1) ) + sockConfig->i2sConfig.pin = V3I2SSRXB1; + } + } + + Socket.FindNextNode(); + } + } + Channel.FindNextNode(); + } + Xml.FindNextNode(); + } + return success && found; +} + +bool CVodXml::GetRouteTerminals( CRouteTerminal *routeTerminal, CSafeVector &allRouteTerminals ) +{ + bool success = false; + bool found = false; + CXml Xml( *( ( CXml * )this ) ); + success = Xml.SetToTopNode(); + success &= Xml.FindNode( TAG_NETWORK_MANAGER ); + success &= Xml.FindFirstChildNode(); + + while( success && Xml.FindNode( TAG_ROUTE ) ) + { + CXml t1( Xml ); + t1.FindFirstChildNode(); + + int deviceType = -1; + int instance = -1; + int channelId = -1; + + while( t1.FindNode( TAG_TERMINAL ) && + t1.GetChildValueInt( TAG_CHANNEL_ID, channelId ) && + t1.GetChildValueInt( TAG_INSTANCE, instance ) ) + { + if( !t1.GetChildValueInt( TAG_DEVICE_TYPE, deviceType ) ) + { + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( t1.GetChildValue( TAG_DEVICE_TYPE_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + deviceType = ConvertToInt( szTmp ); + } + if( routeTerminal->deviceType != ( uint32_t )deviceType || + routeTerminal->channelId != ( uint32_t )channelId || + routeTerminal->instance != ( uint32_t )instance ) + { + t1.FindNextNode(); + continue; + } + + CXml t2( Xml ); + t2.FindFirstChildNode(); + + while( t2.FindNode( TAG_TERMINAL ) ) + { + deviceType = -1; + if( !t2.GetChildValueInt( TAG_DEVICE_TYPE, deviceType ) ) + { + xmlChar szTmp[XML_DEFAULT_VALUE_LEN]; + if( t2.GetChildValue( TAG_DEVICE_TYPE_HEX, szTmp, XML_DEFAULT_VALUE_LEN ) ) + deviceType = ConvertToInt( szTmp ); + } + if( !t2.GetChildValueInt( TAG_CHANNEL_ID, channelId ) || + !t2.GetChildValueInt( TAG_INSTANCE, instance ) || + ( routeTerminal->deviceType == ( uint32_t )deviceType && + routeTerminal->channelId == ( uint32_t )channelId && + routeTerminal->instance == ( uint32_t )instance ) ) + { + t2.FindNextNode(); + continue; + } + bool alreadyStored = false; + for( uint32_t i = 0; found && i < allRouteTerminals.Size(); i++ ) + { + if( allRouteTerminals[i]->deviceType == ( uint32_t )deviceType && + allRouteTerminals[i]->channelId == ( uint32_t )channelId && + allRouteTerminals[i]->instance == ( uint32_t )instance ) + { + alreadyStored = true; + break; + } + } + if( !alreadyStored ) + { + CRouteTerminal *terminal = new CRouteTerminal(); + if( NULL != terminal ) + { + terminal->deviceType = deviceType; + terminal->channelId = channelId; + terminal->instance = instance; + allRouteTerminals.PushBack( terminal ); + found = true; + } + } + t2.FindNextNode(); + } + t1.FindNextNode(); + } + Xml.FindNextNode(); + } + return success && found; +} diff --git a/Src/VodXml.h b/Src/VodXml.h new file mode 100644 index 0000000..e02fe66 --- /dev/null +++ b/Src/VodXml.h @@ -0,0 +1,361 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CVodXml, CChannelConfiguration and the CSocketConfiguration classes. + */ +/*----------------------------------------------------------*/ +#ifndef _VODXML_H_ +#define _VODXML_H_ + +#include +#include +#include +#include "Xml.h" + +typedef enum SocketPort_tag +{ + PORT_UNKNOWN, + PORT_I2C, + PORT_I2S, + PORT_PCIE, + PORT_MLB, + PORT_MOST, + PORT_TSI, + PORT_USB +} SocketPort_t; + +/*----------------------------------------------------------*/ +/*! + * \brief Storage class to store I2S related informations + */ + +/*----------------------------------------------------------*/ +class CI2SConfiguration +{ +public: + ///I2S Clock drive mode + V1I2SPortClkDriveMode_t portClkDriveModeV1; + + ///I2S Streaming port mode + V1I2SStreamingPortMode_t portOptionV1; + + ///I2S Streaming data format + V1I2SStreamingDataFormat_t streamingDataFormatV1; + + ///I2S Stream Pin ID + V1I2SPin_t pinV1; + + ///I2S Port option + V3I2SPortOption_t portOption; + + ///I2S Clock mode + V3I2SClockMode_t clockMode; + + ///I2S Delay option + V3I2SDelayMode_t delayMode; + + ///I2S Port speed in mulitple of MOST base clock (FS) + V3I2SPortSpeed_t portSpeed; + + ///I2S Data Alignment + V3I2SAlignment_t alignment; + + ///I2S Stream Pin ID + V3I2SPin_t pin; + + CI2SConfiguration() : portClkDriveModeV1( V1I2sClockModeUnknown ), + streamingDataFormatV1( V1I2sDataFormatUnknown ), pinV1( V1I2sInvalidPin ), + portOption( V3I2SOptionUnknown ), clockMode( V3I2SClockModeUnknown ), + delayMode( V3I2SDelayUnknown ), portSpeed( V3I2SSpeedUnknown ), + alignment( V3I2SAlignUnknown ), pin( V3I2SInvalidPin ) + { + } +}; + +/*----------------------------------------------------------*/ +/*! + * \brief Storage class to store TSI related informations (only available on OS81110) + */ + +/*----------------------------------------------------------*/ +class CTsiConfigurationV1 +{ +public: + ///TSI port instance + V1TsiPortInstance_t tsiPortInstance; + + ///I2S Streaming port mode + V1TsiPortMode tsiPortMode; + + CTsiConfigurationV1() : tsiPortInstance(V1TsiPortInstanceNotSet), tsiPortMode(V1TsiPortModeNotSet) + { + } +}; + +/*----------------------------------------------------------*/ +/*! + * \brief Storage class to store all socket related informations. This class will be used inside + * the CChannelConfiguration class. + */ + +/*----------------------------------------------------------*/ +class CSocketConfiguration +{ +public: + ///Determines the type of used Application Interface Module (Linux Driver API) + AimType_t aimType; + + /// Determines the used INIC port + SocketPort_t port; + + /// Determines the used MOST data type + EPDataType_t type; + + /// Determines the offset of current socket is splitted in case of usage of Splitter or Combiner + /// The value in case Splitter or Combiner are not used, this value is 0xFFFFFFFF. + uint32_t splittedOffset; + + /// Determines the used amount of bytes used for this configuration + uint32_t blockWidth; + + /// Determines the used address. This value is generic and has to be interpreted for the used INIC port. + uint32_t address; + + /// (optional) Determines the amount of buffers used in the driver. + uint32_t amountOfBuffers; + + /// (optional) Determines the size of a single buffer used in the driver. + uint32_t bufferSize; + + /// (optional) Determines the amount of transmitted packets inside of one USB frame. + uint32_t packetsPerTransaction; + + /// (optional) Determines the width of a subbuffer. + uint32_t subbufferSize; + + /// (optional) In case of I2C port, this class holds the corresponding informations + CI2SConfiguration i2sConfig; + + /// (optional) In case of TSI port, this class holds the corresponding informations + CTsiConfigurationV1 tsiConfig; + + CSocketConfiguration() : aimType( AIM_UNKNOWN ), port( PORT_UNKNOWN ), type( EP_Unknown ), + splittedOffset( 0xFFFFFFFF ), blockWidth( 0xFFFFFFFF ), address( 0xFFFFFFFF ), + amountOfBuffers( 32 ), bufferSize( 8192 ), packetsPerTransaction( 2 ), + subbufferSize( 188 ) + { + } +}; + +/*----------------------------------------------------------*/ +/*! + * \brief Storage class to store all channel related informations. This class will be returned by + * the CVodXml class. + */ + +/*----------------------------------------------------------*/ +class CChannelConfiguration +{ +public: + /// Determines the device type as specified in the configuration XML file and found in the group address configuration. + uint32_t deviceType; + + /// Determines the used channel id, as specified in the configuration XML file. + uint32_t channelId; + + /// Points to the input socket configuration, which will be represented by the CSocketConfiguration class. + CSocketConfiguration inSocket; + + /// Points to the output socket configuration, which will be represented by the CSocketConfiguration class. + CSocketConfiguration outSocket; + + /// Points to an external scripting file, to perform additional configuration actions. May be NULL. + char *externalScipt; + + CChannelConfiguration() : deviceType( 0xFFFFFFFF ), channelId( 0xFFFFFFFF ), externalScipt( NULL ) + { + } + + ~CChannelConfiguration() + { + if( NULL != externalScipt ) + { + free( externalScipt ); + externalScipt = NULL; + } + } +}; + + +/*----------------------------------------------------------*/ +/*! + * \brief Storage class for channel related informations. This class will be returned by + * the CVodXml class. + */ + +/*----------------------------------------------------------*/ +class CRouteTerminal +{ +public: + /// Determines the device type as specified in the configuration XML file and found in the group address configuration. + uint32_t deviceType; + + /// instance number of the device found in the network + uint32_t instance; + + /// Determines the used channel id, as specified in the configuration XML file. + uint32_t channelId; +}; + + + + +/*----------------------------------------------------------*/ +/*! + * \brief Class to read XML for the Video On Demand use case. + */ + +/*----------------------------------------------------------*/ +class CVodXml : public CXml +{ +private: + /*----------------------------------------------------------*/ + /*! \brief Retrieves the control channel addresses used by the sever + * + * \param rxEndpoint RX address will be written to this address (1 Byte) + * \param txEndpoint TX address will be written to this address (1 Byte) + * \return true, if successful + */ + /*----------------------------------------------------------*/ + bool GetLocalInicConfigurations( uint8_t *rxEndpoint, uint8_t *txEndpoint ); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the used physical interface used by the server for the MOST control channel. + * + * \return Enumeration holding the physical interface. + */ + /*----------------------------------------------------------*/ + SocketPort_t GetServerHardwareInterface(); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the Port Message Protocol Version used by the server INIC. + * + * \return 1 for INIC API V1, 2 for INIC API V2. + */ + /*----------------------------------------------------------*/ + uint32_t GetServerDeviceApi(); + + + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the MOST parameters for the server attached INIC + * + * \param isTimingMaster true will be written to this address, when INIC shall act as timing master. false, otherwise. + * \param asyncBandwidth The amount of bytes usable for asynchronous data will be written to this address. + * \return true, if successful + */ + /*----------------------------------------------------------*/ + bool GetNetworkConfiguration( bool *isTimingMaster, uint32_t *asyncBandwidth ); + +public: + /*----------------------------------------------------------*/ + /*! \brief Constructor of CVodXml. + * + * \param szFile Path to configuration XML file. + */ + /*----------------------------------------------------------*/ + CVodXml( const char *szFile ); + + /*----------------------------------------------------------*/ + /*! \brief Retrieves all instances of the control channel addresses used by the sever + * + * \param rxEndpoint RX address array will be written to this address (1 Byte) + * \param txEndpoint TX address array will be written to this address (1 Byte) + * \param serverNum The amount of found configurations will be written to this address. + * \note The user has to free the arrays rxEndpoint and txEndpoint after usage. + * \return true, if successful + */ + /*----------------------------------------------------------*/ + bool GetLocalInicConfigurations( uint8_t *rxEndpoint[], uint8_t *txEndpoint[], uint32_t *apiVersion[], + SocketPort_t *hwPort[], bool *isTimingMaster[], uint32_t *asyncBandwidth[], uint8_t *serverNum ); + + + bool GetMostParameters(bool *isTimingMaster, uint32_t *asyncBandwidth); + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the Port Message Protocol Version used by the given device type. + * + * \return 1 for INIC API V1, 2 for INIC API V2. + */ + /*----------------------------------------------------------*/ + uint32_t GetDeviceApi( uint32_t deviceType ); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the used MLB Port speed, if used. + * + * \return The configured MLB port speed. + */ + /*----------------------------------------------------------*/ + MlbPortSpeed_t GetDeviceMlbPortSpeed( uint32_t deviceType ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the MOST channel configurations for the given device type. + * + * \param deviceType The device type to search for. + * \param allChannels A list with all channel configurations will be written into the given vector. + * \return true, if successful + */ + /*----------------------------------------------------------*/ + bool GetChannelConfigurations( uint32_t deviceType, CSafeVector &allChannels ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the counter route terminals found in routes + * + * \param routeTerminal - Route terminal, a counter terminal is searched for + * \return true, if successful + */ + /*----------------------------------------------------------*/ + bool GetRouteTerminals( CRouteTerminal *routeTerminal, CSafeVector &allRouteTerminals ); + + + +}; + +#endif //_VODXML_H_ diff --git a/Src/Xml.cpp b/Src/Xml.cpp new file mode 100644 index 0000000..44a814e --- /dev/null +++ b/Src/Xml.cpp @@ -0,0 +1,251 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "Types.h" +#include "Xml.h" + +#define CHILD(x) ( (x) ? (x)->xmlChildrenNode : NULL ) + + + + +CXml::CXml( const char *szFile ) +{ + m_Doc = xmlReadFile( szFile, NULL, 0 ); + + if( NULL == m_Doc ) + LOG("unable to open XML file\n"); + else + m_bDocOpened = true; +} + + + +CXml::CXml( const char *szBuffer, uint32_t nBufferLen ) +{ + if( NULL == szBuffer || strlen( szBuffer ) >= nBufferLen ) + { + LOG("Parameter error in constructor of memory driven CXml\n"); + } + m_Doc = xmlReadMemory( szBuffer, strlen( szBuffer ), "config.xml", NULL, 0 ); + + if( NULL == m_Doc ) + LOG("unable to open XML buffer\n"); + else + m_bDocOpened = true; +} + + + + +CXml::CXml( CXml &Node ) : m_Doc( Node.m_Doc ), m_Node( Node.m_Node ), m_bDocOpened( false ) +{ +} + + + + + +CXml::~CXml() +{ + if( m_bDocOpened ) + xmlFreeDoc( m_Doc ); +} + + + + + +bool CXml::SetToTopNode() +{ + m_Node = xmlDocGetRootElement( m_Doc ); + m_Node = CHILD(m_Node); + + return ( NULL != m_Node ); +} + + + + + +bool CXml::FindFirstChildNode() +{ + m_Node = CHILD(m_Node); + return ( NULL != m_Node ); +} + + + + + +bool CXml::FindNextNode() +{ + if( NULL == m_Node ) + return false; + + m_Node = m_Node->next; + + return NULL != m_Node; +} + + + + + +bool CXml::FindNode( const xmlChar *szTag ) +{ + if( NULL == m_Node || NULL == szTag ) + { + m_Node = NULL; + return false; + } + + while( m_Node && xmlStrcmp( m_Node->name, szTag ) ) // search in all nodes + m_Node = m_Node->next; + + return ( NULL != m_Node ); +} + + + + + +bool CXml::GetChildValue( const xmlChar *szTag, xmlChar *szValue, int nMaxLen ) +{ + xmlNode *nodeMem = m_Node; + FindFirstChildNode(); + FindNode( szTag ); + + if( NULL == m_Node ) + { + m_Node = nodeMem; + return false; + } + + xmlChar *szTmp = xmlNodeListGetString( m_Doc, m_Node->xmlChildrenNode, 1 ); + m_Node = nodeMem; + + if( NULL == szTmp ) + return false; + + if( xmlStrlen( szTmp ) >= nMaxLen ) + { + xmlFree( szTmp ); + return false; + } + + memcpy( szValue, szTmp, sizeof( xmlChar ) * ( xmlStrlen( szTmp ) + 1 ) ); + xmlFree( szTmp ); + + return true; +} + + + + + +bool CXml::GetChildValueInt( const xmlChar *szTag, int &nValue ) +{ + xmlNode *nodeMem = m_Node; + FindFirstChildNode(); + FindNode( szTag ); + xmlNode *node = m_Node; + m_Node = nodeMem; + + if( NULL == node ) + { + return false; + } + + xmlChar *szVal = xmlNodeListGetString( m_Doc, node->xmlChildrenNode, 1 ); + + if( NULL == szVal ) + return false; + + nValue = (szVal[0] == '0' && (szVal[1] =='x' || szVal[1] =='X')) ? + strtol( ( char * )szVal + 2, NULL, 16 ) : + atoi( ( char * )szVal ); + + xmlFree( szVal ); + + return true; +} + + + + + +bool CXml::FindNodeWithValue( const xmlChar *szNodeTag, const xmlChar *szValueTag, const xmlChar *szValue ) +{ + while( FindNode( szNodeTag ) ) + { // search node fitting to NodeTag + xmlChar *szCmp = NULL; + + if( GetChildValue( szValueTag, szCmp ) ) + { // get nodes value fitting to ValueTag + int nCmp = xmlStrcmp( szValue, szCmp ); // value fitting to compare value + xmlFree( szCmp ); + + if( 0 == nCmp ) // search completed? + break; + } + + m_Node = m_Node->next; // continue with next node + } + + return ( NULL != m_Node ); +} + + + + + +bool CXml::FindNodeWithValueInt( const xmlChar *szNodeTag, const xmlChar *szValueTag, int nValue ) +{ + int nCmp; + + while( FindNode( szNodeTag ) && + GetChildValueInt( szValueTag, nCmp ) && + nValue != nCmp ) + { + FindNextNode(); + } + + return NULL != + m_Node; // return found node or NULL; +} + + +int CXml::ConvertToInt( xmlChar *xString ) +{ + char *cString = ( char * )xString; //Convertion may be needed.. + for( uint32_t i = 0; i < strlen( cString ); i++ ) + { + if( cString[i] >= 65 && cString[i] <= 90 ) + { + //convert upper case to lower case + cString[i] += 32; + } + } + return strtol( cString, NULL, 16 ); +} diff --git a/Src/Xml.h b/Src/Xml.h new file mode 100644 index 0000000..477a086 --- /dev/null +++ b/Src/Xml.h @@ -0,0 +1,179 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CXml class. + */ +/*----------------------------------------------------------*/ +#ifndef _XML_H_ +#define _XML_H_ + +#include "Types.h" +#include +#include + + +#define XML_DEFAULT_VALUE_LEN 255 + +/*----------------------------------------------------------*/ +/*! \brief Generic class to read XML files. It does not implement a concrete use case. + */ +/*----------------------------------------------------------*/ +class CXml +{ + xmlDoc *m_Doc; + xmlNode *m_Node; + bool m_bDocOpened; +protected: + static int ConvertToInt( xmlChar *xString ); + +public: + /*----------------------------------------------------------*/ + /*! \brief construct a XML document from a file name + */ + /*----------------------------------------------------------*/ + CXml( const char *szFile ); + + + /*----------------------------------------------------------*/ + /*! \brief construct a XML document from a zero terminated string in the memory. + */ + /*----------------------------------------------------------*/ + CXml( const char *szBuffer, uint32_t nBufferLen ); + + + /*----------------------------------------------------------*/ + /*! \brief construct a XML document from a given node + */ + /*----------------------------------------------------------*/ + CXml( CXml &Node ); + + + + /*----------------------------------------------------------*/ + /*! \brief construct a XML document from a file name + */ + /*----------------------------------------------------------*/ + ~CXml(); + + + + /*----------------------------------------------------------*/ + /*! \brief gets top node inside root of XML document + * + * \return true, if successful + */ + /*----------------------------------------------------------*/ + bool SetToTopNode(); + + + + /*----------------------------------------------------------*/ + /*! \brief find first child node + * + * \return true, if child node was found + */ + /*----------------------------------------------------------*/ + bool FindFirstChildNode(); + + + + + /*----------------------------------------------------------*/ + /*! \brief find next node on same level + * + * \return true, if node was found + */ + /*----------------------------------------------------------*/ + bool FindNextNode(); + + + + + /*----------------------------------------------------------*/ + /*! \brief find a XML node with a given tag + * + * \param szTag - tag looking for + * + * \return XML node + */ + /*----------------------------------------------------------*/ + bool FindNode( const xmlChar *szTag ); + + + + + + /*----------------------------------------------------------*/ + /*! \brief searches a node, which has a child node with a + * given value + * + * \param szNodeTag - tag of nodes which are included in search + * \param szValueTag - tag of value inside node + * \param szValue - value looked for + * + * \return XML true if found + */ + /*----------------------------------------------------------*/ + bool FindNodeWithValue( const xmlChar *szNodeTag, const xmlChar *szValueTag, const xmlChar *szValue ); + + + + /*----------------------------------------------------------*/ + /*! \brief searches node, which has a child node with a + * given integer value + * + * \param szNodeTag - tag of nodes which are included in search + * \param szValueTag - tag of value inside node + * \param szValue - value looked for + * + * \return true if found + */ + /*----------------------------------------------------------*/ + bool FindNodeWithValueInt( const xmlChar *szNodeTag, const xmlChar *szValueTag, int nValue ); + + /*----------------------------------------------------------*/ + /*! \brief get node's child value as string + * + * \param szTag - tag of the value inside the node + * + * \return value as string if found, otherwise NULL + */ + /*----------------------------------------------------------*/ + bool GetChildValue( const xmlChar *szTag, xmlChar *szValue, int nMaxLen = XML_DEFAULT_VALUE_LEN ); + + + /*----------------------------------------------------------*/ + /*! \brief get node's child value as int + * + * \param szTag - tag of the value inside the node + * \param nErr - value returned in case of an error + * + * \return value as int + */ + /*----------------------------------------------------------*/ + bool GetChildValueInt( const xmlChar *szTag, int &nValue ); +}; + +#endif diff --git a/buildX86/Makefile-Release.mk b/buildX86/Makefile-Release.mk new file mode 100644 index 0000000..7419a76 --- /dev/null +++ b/buildX86/Makefile-Release.mk @@ -0,0 +1,368 @@ +# +# Generated Makefile - do not edit! +# +# Edit the Makefile in the project folder instead (../Makefile). Each target +# has a -pre and a -post target defined where you can add customized code. +# +# This makefile implements configuration specific macros and targets. + + +# Environment +MKDIR=mkdir +CP=cp +GREP=grep +NM=nm +CCADMIN=CCadmin +RANLIB=ranlib +CC=${CROSS_COMPILE}gcc +CCC=${CROSS_COMPILE}g++ +CXX=${CROSS_COMPILE}g++ +FC=${CROSS_COMPILE}gfortran +AS=${CROSS_COMPILE}as + +# Macros +CND_PLATFORM=GNU-Linux-x86 +CND_DLIB_EXT=so +CND_CONF=Release +CND_DISTDIR=dist +CND_BUILDDIR=build + +# Include project Makefile +include Makefile + +# Object Directory +OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM} + +# Object Files +OBJECTFILES= \ + ${OBJECTDIR}/Src/ConnectionInfo.o \ + ${OBJECTDIR}/Src/Console.o \ + ${OBJECTDIR}/Src/IP/MostIpc.o \ + ${OBJECTDIR}/Src/IP/MostMsg.o \ + ${OBJECTDIR}/Src/IP/MostMsgTx.o \ + ${OBJECTDIR}/Src/IP/MsgAddr.o \ + ${OBJECTDIR}/Src/IP/MsgFilter.o \ + ${OBJECTDIR}/Src/MacAddr.o \ + ${OBJECTDIR}/Src/Main.o \ + ${OBJECTDIR}/Src/Network/Network.o \ + ${OBJECTDIR}/Src/Network/Network_CB.o \ + ${OBJECTDIR}/Src/Network/Network_Private.o \ + ${OBJECTDIR}/Src/Network/NetworkDevice.o \ + ${OBJECTDIR}/Src/Network/NetworkDeviceListener.o \ + ${OBJECTDIR}/Src/Network/NodeDatabase.o \ + ${OBJECTDIR}/Src/Network/IndustrialStack.o \ + ${OBJECTDIR}/Src/Network/IndustrialStack_LLD.o \ + ${OBJECTDIR}/Src/Network/IndustrialStack_MNS.o \ + ${OBJECTDIR}/Src/Network/base/Board.o \ + ${OBJECTDIR}/Src/Network/base/DriverConfiguration.o \ + ${OBJECTDIR}/Src/Thread.o \ + ${OBJECTDIR}/Src/ScriptXml.o \ + ${OBJECTDIR}/Src/VodXml.o \ + ${OBJECTDIR}/Src/Xml.o \ + ${OBJECTDIR}/mnsl/mns_alm.o \ + ${OBJECTDIR}/mnsl/mns_ams.o \ + ${OBJECTDIR}/mnsl/mns_amsmessage.o \ + ${OBJECTDIR}/mnsl/mns_amspool.o \ + ${OBJECTDIR}/mnsl/mns_base.o \ + ${OBJECTDIR}/mnsl/mns_dl.o \ + ${OBJECTDIR}/mnsl/mns_eh.o \ + ${OBJECTDIR}/mnsl/mns_encoder.o \ + ${OBJECTDIR}/mnsl/mns_lldpool.o \ + ${OBJECTDIR}/mnsl/mns_message.o \ + ${OBJECTDIR}/mnsl/mns_misc.o \ + ${OBJECTDIR}/mnsl/mns_obs.o \ + ${OBJECTDIR}/mnsl/mns_pmchannel.o \ + ${OBJECTDIR}/mnsl/mns_pmfifo.o \ + ${OBJECTDIR}/mnsl/mns_pmp.o \ + ${OBJECTDIR}/mnsl/mns_pool.o \ + ${OBJECTDIR}/mnsl/mns_scheduler.o \ + ${OBJECTDIR}/mnsl/mns_segmentation.o \ + ${OBJECTDIR}/mnsl/mns_timer.o \ + ${OBJECTDIR}/mnsl/mns_telqueue.o \ + ${OBJECTDIR}/mnsl/mns_transceiver.o \ + ${OBJECTDIR}/mnsl/mns_pmcmd.o \ + ${OBJECTDIR}/mnsl/mns_pmfifos.o \ + ${OBJECTDIR}/mnsl/mnsl.o + + +# Include Path +C_INCLUDE=-Imnsl -ISrc -ISrc/IP -ISrc/Network -ISrc/Network/base -I/usr/include/libxml2 ${INCLUDE_PATH} + +# C Compiler Flags +CFLAGS=-c -Wall -O2 -MMD -MP -DNDEBUG ${PROJECT_C_FLAGS} + +# CC Compiler Flags +CCFLAGS=-Wall -O2 -MMD -MP -DNDEBUG ${PROJECT_C_FLAGS} +CXXFLAGS=${CCFLAGS} + +# Fortran Compiler Flags +FFLAGS= + +# Assembler Flags +ASFLAGS= + +# Link Libraries and Options +LDLIBSOPTIONS=-lpthread -lxml2 -lrt ${LIBRARIES_FLAGS} + +# Build Targets +.build-conf: ${BUILD_SUBPROJECTS} + "${MAKE}" -f buildX86/Makefile-${CND_CONF}.mk NetworkManager + +NetworkManager: ${OBJECTFILES} + ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM} + ${LINK.cc} -o NetworkManager ${OBJECTFILES} ${LDLIBSOPTIONS} + +${OBJECTDIR}/Src/ConnectionInfo.o: Src/ConnectionInfo.cpp + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/ConnectionInfo.o Src/ConnectionInfo.cpp + +${OBJECTDIR}/Src/Console.o: Src/Console.c + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Console.o Src/Console.c + +${OBJECTDIR}/Src/IP/MostIpc.o: Src/IP/MostIpc.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/IP + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MostIpc.o Src/IP/MostIpc.cpp + +${OBJECTDIR}/Src/IP/MostMsg.o: Src/IP/MostMsg.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/IP + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MostMsg.o Src/IP/MostMsg.cpp + +${OBJECTDIR}/Src/IP/MostMsgTx.o: Src/IP/MostMsgTx.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/IP + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MostMsgTx.o Src/IP/MostMsgTx.cpp + +${OBJECTDIR}/Src/IP/MsgAddr.o: Src/IP/MsgAddr.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/IP + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MsgAddr.o Src/IP/MsgAddr.cpp + +${OBJECTDIR}/Src/IP/MsgFilter.o: Src/IP/MsgFilter.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/IP + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MsgFilter.o Src/IP/MsgFilter.cpp + +${OBJECTDIR}/Src/MacAddr.o: Src/MacAddr.cpp + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/MacAddr.o Src/MacAddr.cpp + +${OBJECTDIR}/Src/Main.o: Src/Main.cpp + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Main.o Src/Main.cpp + +${OBJECTDIR}/Src/Network/Network.o: Src/Network/Network.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/Network.o Src/Network/Network.cpp + +${OBJECTDIR}/Src/Network/Network_CB.o: Src/Network/Network_CB.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/Network_CB.o Src/Network/Network_CB.cpp + +${OBJECTDIR}/Src/Network/Network_Private.o: Src/Network/Network_Private.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/Network_Private.o Src/Network/Network_Private.cpp + +${OBJECTDIR}/Src/Network/NetworkDevice.o: Src/Network/NetworkDevice.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/NetworkDevice.o Src/Network/NetworkDevice.cpp + +${OBJECTDIR}/Src/Network/NetworkDeviceListener.o: Src/Network/NetworkDeviceListener.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/NetworkDeviceListener.o Src/Network/NetworkDeviceListener.cpp + +${OBJECTDIR}/Src/Network/NodeDatabase.o: Src/Network/NodeDatabase.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/NodeDatabase.o Src/Network/NodeDatabase.cpp + +${OBJECTDIR}/Src/Network/IndustrialStack.o: Src/Network/IndustrialStack.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/IndustrialStack.o Src/Network/IndustrialStack.cpp + +${OBJECTDIR}/Src/Network/IndustrialStack_LLD.o: Src/Network/IndustrialStack_LLD.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/IndustrialStack_LLD.o Src/Network/IndustrialStack_LLD.cpp + +${OBJECTDIR}/Src/Network/IndustrialStack_MNS.o: Src/Network/IndustrialStack_MNS.cpp + ${MKDIR} -p ${OBJECTDIR}/Src/Network + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/IndustrialStack_MNS.o Src/Network/IndustrialStack_MNS.cpp + +${OBJECTDIR}/Src/Network/base/Board.o: Src/Network/base/Board.c + ${MKDIR} -p ${OBJECTDIR}/Src/Network/base + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/base/Board.o Src/Network/base/Board.c + +${OBJECTDIR}/Src/Network/base/DriverConfiguration.o: Src/Network/base/DriverConfiguration.c + ${MKDIR} -p ${OBJECTDIR}/Src/Network/base + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Network/base/DriverConfiguration.o Src/Network/base/DriverConfiguration.c + +${OBJECTDIR}/Src/Thread.o: Src/Thread.cpp + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Thread.o Src/Thread.cpp + +${OBJECTDIR}/Src/ScriptXml.o: Src/ScriptXml.cpp + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/ScriptXml.o Src/ScriptXml.cpp + +${OBJECTDIR}/Src/VodXml.o: Src/VodXml.cpp + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/VodXml.o Src/VodXml.cpp + +${OBJECTDIR}/Src/Xml.o: Src/Xml.cpp + ${MKDIR} -p ${OBJECTDIR}/Src + ${RM} "$@.d" + ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Xml.o Src/Xml.cpp + +${OBJECTDIR}/mnsl/mns_alm.o: mnsl/mns_alm.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_alm.o mnsl/mns_alm.c + +${OBJECTDIR}/mnsl/mns_ams.o: mnsl/mns_ams.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_ams.o mnsl/mns_ams.c + +${OBJECTDIR}/mnsl/mns_amsmessage.o: mnsl/mns_amsmessage.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_amsmessage.o mnsl/mns_amsmessage.c + +${OBJECTDIR}/mnsl/mns_amspool.o: mnsl/mns_amspool.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_amspool.o mnsl/mns_amspool.c + +${OBJECTDIR}/mnsl/mns_base.o: mnsl/mns_base.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_base.o mnsl/mns_base.c + +${OBJECTDIR}/mnsl/mns_dl.o: mnsl/mns_dl.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_dl.o mnsl/mns_dl.c + +${OBJECTDIR}/mnsl/mns_eh.o: mnsl/mns_eh.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_eh.o mnsl/mns_eh.c + +${OBJECTDIR}/mnsl/mns_encoder.o: mnsl/mns_encoder.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_encoder.o mnsl/mns_encoder.c + +${OBJECTDIR}/mnsl/mns_lldpool.o: mnsl/mns_lldpool.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_lldpool.o mnsl/mns_lldpool.c + +${OBJECTDIR}/mnsl/mns_message.o: mnsl/mns_message.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_message.o mnsl/mns_message.c + +${OBJECTDIR}/mnsl/mns_misc.o: mnsl/mns_misc.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_misc.o mnsl/mns_misc.c + +${OBJECTDIR}/mnsl/mns_obs.o: mnsl/mns_obs.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_obs.o mnsl/mns_obs.c + +${OBJECTDIR}/mnsl/mns_pmchannel.o: mnsl/mns_pmchannel.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_pmchannel.o mnsl/mns_pmchannel.c + +${OBJECTDIR}/mnsl/mns_pmfifo.o: mnsl/mns_pmfifo.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_pmfifo.o mnsl/mns_pmfifo.c + +${OBJECTDIR}/mnsl/mns_pmp.o: mnsl/mns_pmp.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_pmp.o mnsl/mns_pmp.c + +${OBJECTDIR}/mnsl/mns_pool.o: mnsl/mns_pool.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_pool.o mnsl/mns_pool.c + +${OBJECTDIR}/mnsl/mns_scheduler.o: mnsl/mns_scheduler.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_scheduler.o mnsl/mns_scheduler.c + +${OBJECTDIR}/mnsl/mns_segmentation.o: mnsl/mns_segmentation.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_segmentation.o mnsl/mns_segmentation.c + +${OBJECTDIR}/mnsl/mns_timer.o: mnsl/mns_timer.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_timer.o mnsl/mns_timer.c + +${OBJECTDIR}/mnsl/mns_telqueue.o: mnsl/mns_telqueue.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_telqueue.o mnsl/mns_telqueue.c +${OBJECTDIR}/mnsl/mns_transceiver.o: mnsl/mns_transceiver.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_transceiver.o mnsl/mns_transceiver.c + +${OBJECTDIR}/mnsl/mns_pmcmd.o: mnsl/mns_pmcmd.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_pmcmd.o mnsl/mns_pmcmd.c + +${OBJECTDIR}/mnsl/mns_pmfifos.o: mnsl/mns_pmfifos.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mns_pmfifos.o mnsl/mns_pmfifos.c + +${OBJECTDIR}/mnsl/mnsl.o: mnsl/mnsl.c + ${MKDIR} -p ${OBJECTDIR}/mnsl + ${RM} "$@.d" + ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/mnsl/mnsl.o mnsl/mnsl.c + +# Subprojects +.build-subprojects: + +# Clean Targets +.clean-conf: ${CLEAN_SUBPROJECTS} + ${RM} -r ${CND_BUILDDIR}/${CND_CONF} + ${RM} NetworkManager + +# Subprojects +.clean-subprojects: + +# Enable dependency checking +.dep.inc: .depcheck-impl + +include .dep.inc diff --git a/buildX86/Makefile-impl.mk b/buildX86/Makefile-impl.mk new file mode 100644 index 0000000..96feb83 --- /dev/null +++ b/buildX86/Makefile-impl.mk @@ -0,0 +1,133 @@ +# +# Generated Makefile - do not edit! +# +# Edit the Makefile in the project folder instead (../Makefile). Each target +# has a pre- and a post- target defined where you can add customization code. +# +# This makefile implements macros and targets common to all configurations. +# +# NOCDDL + + +# Building and Cleaning subprojects are done by default, but can be controlled with the SUB +# macro. If SUB=no, subprojects will not be built or cleaned. The following macro +# statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf +# and .clean-reqprojects-conf unless SUB has the value 'no' +SUB_no=NO +SUBPROJECTS=${SUB_${SUB}} +BUILD_SUBPROJECTS_=.build-subprojects +BUILD_SUBPROJECTS_NO= +BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}} +CLEAN_SUBPROJECTS_=.clean-subprojects +CLEAN_SUBPROJECTS_NO= +CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}} + + +# Project Name +PROJECTNAME=NetworkManager + +# Active Configuration +DEFAULTCONF=Release +CONF=${DEFAULTCONF} + +# All Configurations +ALLCONFS=Release + + +# build +.build-impl: .build-pre .validate-impl .depcheck-impl + @#echo "=> Running $@... Configuration=$(CONF)" + "${MAKE}" -f buildX86/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf + + +# clean +.clean-impl: .clean-pre .validate-impl .depcheck-impl + @#echo "=> Running $@... Configuration=$(CONF)" + "${MAKE}" -f buildX86/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf + + +# clobber +.clobber-impl: .clobber-pre .depcheck-impl + @#echo "=> Running $@..." + for CONF in ${ALLCONFS}; \ + do \ + "${MAKE}" -f buildX86/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf; \ + done + +# all +.all-impl: .all-pre .depcheck-impl + @#echo "=> Running $@..." + for CONF in ${ALLCONFS}; \ + do \ + "${MAKE}" -f buildX86/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf; \ + done + +# build tests +.build-tests-impl: .build-impl .build-tests-pre + @#echo "=> Running $@... Configuration=$(CONF)" + "${MAKE}" -f buildX86/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-tests-conf + +# run tests +.test-impl: .build-tests-impl .test-pre + @#echo "=> Running $@... Configuration=$(CONF)" + "${MAKE}" -f buildX86/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .test-conf + +# dependency checking support +.depcheck-impl: + @echo "# This code depends on make tool being used" >.dep.inc + @if [ -n "${MAKE_VERSION}" ]; then \ + echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES}))" >>.dep.inc; \ + echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \ + echo "include \$${DEPFILES}" >>.dep.inc; \ + echo "endif" >>.dep.inc; \ + else \ + echo ".KEEP_STATE:" >>.dep.inc; \ + echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \ + fi + +# configuration validation +.validate-impl: + @if [ ! -f buildX86/Makefile-${CONF}.mk ]; \ + then \ + echo ""; \ + echo "Error: can not find the makefile for configuration '${CONF}' in project ${PROJECTNAME}"; \ + echo "See 'make help' for details."; \ + echo "Current directory: " `pwd`; \ + echo ""; \ + fi + @if [ ! -f buildX86/Makefile-${CONF}.mk ]; \ + then \ + exit 1; \ + fi + + +# help +.help-impl: .help-pre + @echo "This makefile supports the following configurations:" + @echo " ${ALLCONFS}" + @echo "" + @echo "and the following targets:" + @echo " build (default target)" + @echo " clean" + @echo " clobber" + @echo " all" + @echo " help" + @echo "" + @echo "Makefile Usage:" + @echo " make [CONF=] [SUB=no] build" + @echo " make [CONF=] [SUB=no] clean" + @echo " make [SUB=no] clobber" + @echo " make [SUB=no] all" + @echo " make help" + @echo "" + @echo "Target 'build' will build a specific configuration and, unless 'SUB=no'," + @echo " also build subprojects." + @echo "Target 'clean' will clean a specific configuration and, unless 'SUB=no'," + @echo " also clean subprojects." + @echo "Target 'clobber' will remove all built files from all configurations and," + @echo " unless 'SUB=no', also from subprojects." + @echo "Target 'all' will will build all configurations and, unless 'SUB=no'," + @echo " also build subprojects." + @echo "Target 'help' prints this message." + @echo "" + diff --git a/buildX86/Makefile-variables.mk b/buildX86/Makefile-variables.mk new file mode 100644 index 0000000..92eaac7 --- /dev/null +++ b/buildX86/Makefile-variables.mk @@ -0,0 +1,27 @@ +# +# Generated - do not edit! +# +# NOCDDL +# +CND_BASEDIR=`pwd` +CND_BUILDDIR=build +CND_DISTDIR=dist +# Release configuration +CND_PLATFORM_Release=GNU-Linux-x86 +CND_ARTIFACT_DIR_Release=dist/Release/GNU-Linux-x86 +CND_ARTIFACT_NAME_Release=tmp +CND_ARTIFACT_PATH_Release=dist/Release/GNU-Linux-x86/tmp +CND_PACKAGE_DIR_Release=dist/Release/GNU-Linux-x86/package +CND_PACKAGE_NAME_Release=tmp.tar +CND_PACKAGE_PATH_Release=dist/Release/GNU-Linux-x86/package/tmp.tar +# +# include compiler specific variables +# +# dmake command +ROOT:sh = test -f nbproject/private/Makefile-variables.mk || \ + (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk) +# +# gmake command +.PHONY: $(shell test -f nbproject/private/Makefile-variables.mk || (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk)) +# +include nbproject/private/Makefile-variables.mk diff --git a/buildX86/Package-Release.bash b/buildX86/Package-Release.bash new file mode 100644 index 0000000..fb38dd2 --- /dev/null +++ b/buildX86/Package-Release.bash @@ -0,0 +1,76 @@ +#!/bin/bash -x + +# +# Generated - do not edit! +# + +# Macros +TOP=`pwd` +CND_PLATFORM=GNU-Linux-x86 +CND_CONF=Release +CND_DISTDIR=dist +CND_BUILDDIR=build +CND_DLIB_EXT=so +NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging +TMPDIRNAME=tmp-packaging +OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/tmp +OUTPUT_BASENAME=tmp +PACKAGE_TOP_DIR=tmp/ + +# Functions +function checkReturnCode +{ + rc=$? + if [ $rc != 0 ] + then + exit $rc + fi +} +function makeDirectory +# $1 directory path +# $2 permission (optional) +{ + mkdir -p "$1" + checkReturnCode + if [ "$2" != "" ] + then + chmod $2 "$1" + checkReturnCode + fi +} +function copyFileToTmpDir +# $1 from-file path +# $2 to-file path +# $3 permission +{ + cp "$1" "$2" + checkReturnCode + if [ "$3" != "" ] + then + chmod $3 "$2" + checkReturnCode + fi +} + +# Setup +cd "${TOP}" +mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package +rm -rf ${NBTMPDIR} +mkdir -p ${NBTMPDIR} + +# Copy files and create directories and links +cd "${TOP}" +makeDirectory "${NBTMPDIR}/tmp/bin" +copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 + + +# Generate tar file +cd "${TOP}" +rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/tmp.tar +cd ${NBTMPDIR} +tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/tmp.tar * +checkReturnCode + +# Cleanup +cd "${TOP}" +rm -rf ${NBTMPDIR} diff --git a/buildX86/project.xml b/buildX86/project.xml new file mode 100644 index 0000000..f4d9328 --- /dev/null +++ b/buildX86/project.xml @@ -0,0 +1,27 @@ + + + org.netbeans.modules.cnd.makeproject + + + app + c + cpp + h + UTF-8 + + + mnsl + Src + + + + Release + 1 + + + + false + + + + diff --git a/mnsl/mns_alm.c b/mnsl/mns_alm.c new file mode 100644 index 0000000..5c5e76d --- /dev/null +++ b/mnsl/mns_alm.c @@ -0,0 +1,277 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the API locking manager. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ALM + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_alm.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constant */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Interval for garbage collection */ +static const uint16_t ALM_GARBAGE_COLLECTOR_INTERVAL = 2600U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Alm_HandleInternalErrors(void *self, void *error_code_ptr); +static void Alm_GarbageCollector(void *self); +static bool Alm_CheckRegisteredApi(void *current_alm_ptr, void *alm_inst_ptr); +static void Alm_StartTimeout(CApiLockingManager *self); +static void Alm_ClearTimeout(CApiLockingManager *self); +static bool Alm_SearchLockedApi(void *current_alm_ptr, void *alm_inst_ptr); +static void Alm_ResetRegisteredApis(CApiLockingManager *self); +static bool Alm_ResetApi(void *current_alm_ptr, void *alm_inst_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CApiLockingManager */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the API locking manager class. + * \param self Instance pointer + * \param tm_ptr Reference to timer management instance + * \param eh_ptr Reference to event handler instance + * \param mns_inst_id MOST NetServices instance ID + */ +void Alm_Ctor(CApiLockingManager *self, + CTimerManagement *tm_ptr, + CEventHandler *eh_ptr, + uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + T_Ctor(&self->garbage_collector); + self->tm_ptr = tm_ptr; + self->eh_ptr = eh_ptr; + self->mns_inst_id = mns_inst_id; + + /* Observe internal errors and events */ + Mobs_Ctor(&self->internal_error_obs, self, EH_M_TERMINATION_EVENTS, &Alm_HandleInternalErrors); + Eh_AddObsrvInternalEvent(self->eh_ptr, &self->internal_error_obs); +} + +/*! \brief Handles internal errors and events + * \param self Instance pointer + * \param error_code_ptr Reference to reported error code + */ +static void Alm_HandleInternalErrors(void *self, void *error_code_ptr) +{ + CApiLockingManager *self_ = (CApiLockingManager *)self; + MISC_UNUSED(error_code_ptr); + + Tm_ClearTimer(self_->tm_ptr, &self_->garbage_collector); /* Clear timeout */ + Alm_ResetRegisteredApis(self_); /* Reset all registered APIs */ +} + +/*! \brief Checks for API locking timeouts. This method is the callback function of timer + * \c garbage_collector. + * \param self Instance pointer + */ +static void Alm_GarbageCollector(void *self) +{ + CApiLockingManager *self_ = (CApiLockingManager *)self; + (void)Dl_Foreach(&self_->api_list, &Alm_CheckRegisteredApi, self_); +} + +/*! \brief This method is used by Alm_GarbageCollector() to process each registered API. + * \param current_alm_ptr Reference to the current API + * \param alm_inst_ptr Instance of the API locking manager + * \return \c false to process all registered APIs + */ +static bool Alm_CheckRegisteredApi(void *current_alm_ptr, void *alm_inst_ptr) +{ + CApiLockingManager *self = (CApiLockingManager *)alm_inst_ptr; + CApiLocking *alm_ptr_ = (CApiLocking *)current_alm_ptr; + MISC_UNUSED(self); + + if(alm_ptr_->timeout_mask != 0U) + { + Alm_ModuleMask_t tmp_mask = 1U; + while(alm_ptr_->timeout_mask != 0U) + { + if(tmp_mask == (tmp_mask & alm_ptr_->timeout_mask)) + { + Ssub_Notify(&alm_ptr_->subject, &tmp_mask, false); + alm_ptr_->method_mask &= ~tmp_mask; + alm_ptr_->timeout_mask &= ~tmp_mask; + } + tmp_mask <<= 1; + } + Alm_ClearTimeout(self); + } + if(alm_ptr_->method_mask != 0U) + { + alm_ptr_->timeout_mask = alm_ptr_->method_mask; + } + return false; +} + +/*! \brief Registers a new API locking object. + * \param self Instance pointer + * \param al_ptr Reference to the API to register + */ +void Alm_RegisterApi(CApiLockingManager *self, CApiLocking *al_ptr) +{ + Dl_InsertTail(&self->api_list, &al_ptr->node); + Dln_SetData(&al_ptr->node, al_ptr); + al_ptr->alm_ptr = self; +} + +/*! \brief Starts the garbage collecting timer. + * \param self Instance pointer + */ +static void Alm_StartTimeout(CApiLockingManager *self) +{ + if(T_IsTimerInUse(&self->garbage_collector) == false) + { + Tm_SetTimer(self->tm_ptr, + &self->garbage_collector, + &Alm_GarbageCollector, + self, + ALM_GARBAGE_COLLECTOR_INTERVAL, + ALM_GARBAGE_COLLECTOR_INTERVAL); + } +} + +/*! \brief Clears the garbage collecting timer. The timer is clear if no API locking flag is + * currently pending. + * \param self Instance pointer + */ +static void Alm_ClearTimeout(CApiLockingManager *self) +{ + if(NULL == Dl_Foreach(&self->api_list, &Alm_SearchLockedApi, self)) + { + Tm_ClearTimer(self->tm_ptr, &self->garbage_collector); + } +} + +/*! \brief Used by Alm_ClearTimeout() to check if at least one registered API is locked. + * \param current_alm_ptr Reference to the current API locking object + * \param alm_inst_ptr Instance of the API locking manager + * \return \c true if a locked API was found, otherwise \c false + */ +static bool Alm_SearchLockedApi(void *current_alm_ptr, void *alm_inst_ptr) +{ + CApiLocking *alm_ptr_ = (CApiLocking *)current_alm_ptr; + bool ret_val = false; + MISC_UNUSED(alm_inst_ptr); + + if(alm_ptr_->method_mask != 0U) + { + ret_val = true; + } + return ret_val; +} + +/*! \brief Resets all registered APIs. Called if an internal error has been occurred. + * \param self Instance pointer + */ +static void Alm_ResetRegisteredApis(CApiLockingManager *self) +{ + (void)Dl_Foreach(&self->api_list, &Alm_ResetApi, self); +} + +/*! \brief Used by Alm_ResetRegisteredApis() to reset all registered APIs. + * \param current_alm_ptr Reference to the current API locking object + * \param alm_inst_ptr Instance of the API locking manager + * \return \c false (process all registered APIs) + */ +static bool Alm_ResetApi(void *current_alm_ptr, void *alm_inst_ptr) +{ + CApiLocking *alm_ptr_ = (CApiLocking *)current_alm_ptr; + MISC_UNUSED(alm_inst_ptr); + + alm_ptr_->method_mask = 0U; + alm_ptr_->timeout_mask = 0U; + + return false; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CApiLocking */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the API locking class. + * \param self Instance pointer + * \param obs_ptr Observer to signal locked API methods + * \param mns_inst_id MOST NetServices instance ID + */ +void Al_Ctor(CApiLocking *self, CSingleObserver *obs_ptr, uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = mns_inst_id; + Dln_Ctor(&self->node, NULL); + if(obs_ptr != NULL) + { + Ssub_Ctor(&self->subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->subject, obs_ptr); + } +} + +/*! \brief Locks the given API method. + * \param self Instance pointer + * \param method Bitmask of method to lock + * \return \c true if the API has been locked successfully + * \return \c false if the API was already locked + */ +bool Al_Lock(CApiLocking *self, Alm_ModuleMask_t method) +{ + bool ret_val = false; + if((self->method_mask & method) == 0U) + { + ret_val = true; + self->method_mask |= method; + self->timeout_mask &= ~method; + Alm_StartTimeout(self->alm_ptr); + } + return ret_val; +} + +/*! \brief Releases the lock of the given API method. + * \param self Instance pointer + * \param method Bitmask of method to lock + */ +void Al_Release(CApiLocking *self, Alm_ModuleMask_t method) +{ + self->method_mask &= ~method; + self->timeout_mask &= ~method; + Alm_ClearTimeout(self->alm_ptr); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_alm.h b/mnsl/mns_alm.h new file mode 100644 index 0000000..92e3e46 --- /dev/null +++ b/mnsl/mns_alm.h @@ -0,0 +1,113 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the API locking module. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ALM + * @{ + */ + +#ifndef MNS_ALM_H +#define MNS_ALM_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_eh.h" +#include "mns_timer.h" +#include "mns_obs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Bitmask used to store locked API methods */ +typedef uint32_t Alm_ModuleMask_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of the API locking manager */ +typedef struct CApiLockingManager_ +{ + CTimerManagement *tm_ptr; /*!< \brief Reference to timer management instance */ + CEventHandler *eh_ptr; /*!< \brief Reference to event handler instance */ + CTimer garbage_collector; /*!< \brief Timer for garbage collection */ + CDlList api_list; /*!< \brief List of registered APIs */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + CMaskedObserver internal_error_obs; /*!< \brief Error observer to handle internal errors and + events */ + +} CApiLockingManager; + +/*! \brief Class structure of the API locking */ +typedef struct CApiLocking_ +{ + CDlNode node; /*!< \brief Node of the doubly linked (API-) list */ + CApiLockingManager *alm_ptr; /*!< \brief Reference to CApiLockingManager instance */ + Alm_ModuleMask_t method_mask; /*!< \brief Bitmask which holds locked API methods */ + Alm_ModuleMask_t timeout_mask; /*!< \brief Bitmask to report timeouts */ + CSingleSubject subject; /*!< \brief Subject to update registered observer */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CApiLocking; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CApiLockingManager */ +/*------------------------------------------------------------------------------------------------*/ +extern void Alm_Ctor(CApiLockingManager *self, + CTimerManagement *tm_ptr, + CEventHandler *eh_ptr, + uint8_t mns_inst_id); +extern void Alm_RegisterApi(CApiLockingManager *self, CApiLocking *al_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CApiLocking */ +/*------------------------------------------------------------------------------------------------*/ +extern void Al_Ctor(CApiLocking *self, CSingleObserver *obs_ptr, uint8_t mns_inst_id); +extern bool Al_Lock(CApiLocking *self, Alm_ModuleMask_t method); +extern void Al_Release(CApiLocking *self, Alm_ModuleMask_t method); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_ALM_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ams.c b/mnsl/mns_ams.c new file mode 100644 index 0000000..338047b --- /dev/null +++ b/mnsl/mns_ams.c @@ -0,0 +1,665 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of Application Message Service + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSC + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams.h" +#include "mns_amsmessage.h" +#include "mns_dl.h" +#include "mns_misc.h" +#include "mns_pmp.h" +#include "mns_encoder.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Priority of the Application Message Service */ +static const uint8_t AMS_SRV_PRIO = 253U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */ +/*! \brief Event which triggers the Rx service */ +static const Srv_Event_t AMS_EV_RX_SERVICE = 1U; +/*! \brief Event which triggers the Tx service */ +static const Srv_Event_t AMS_EV_TX_SERVICE = 2U; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Ams_Cleanup(CAms *self); +static void Ams_Service(void *self); +static void Ams_OnEhEvent(void *self, void *error_code_ptr); + +static void Ams_TxService(CAms *self); +static void Ams_TxOnStatus(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status); +static uint8_t Ams_TxGetNextFollowerId(CAms *self); + +static void Ams_RxOnTelComplete(CAms *self, Msg_MostTel_t *tel_ptr); +static void Ams_RxReleaseTel(CAms *self, Msg_MostTel_t *tel_ptr); +static void Ams_RxProcessWaitingQ(CAms *self); +static void Ams_RxOnSegError(void *self, Msg_MostTel_t *tel_ptr, Segm_Error_t error); +static void Ams_RxOnFreedMsg(void *self, void *data_ptr); +static void Ams_RxFlush(CAms *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization Methods */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CAms + * \param self The instance + * \param base_ptr Reference to base services + * \param mcm_trcv_ptr Reference to the MCM transceiver + * \param rcm_trcv_ptr Reference to the RCM transceiver + * \param pool_ptr Reference to the pool for application message handles + * \param rx_def_payload_sz Default memory size that is allocated when receiving segmented messages + * without size prefix + */ +void Ams_Ctor(CAms *self, CBase *base_ptr, CTransceiver *mcm_trcv_ptr, CTransceiver *rcm_trcv_ptr, + CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz) +{ + MISC_UNUSED(rcm_trcv_ptr); + MISC_MEM_SET((void *)self, 0, sizeof(*self)); /* reset members to "0" */ + self->trcv_mcm_ptr = mcm_trcv_ptr; + self->trcv_rcm_ptr = rcm_trcv_ptr; + self->base_ptr = base_ptr; + self->pool_ptr = pool_ptr; + + self->tx.default_llrbc = AMS_LLRBC_DEFAULT; /* set initial retries */ + self->tx.next_follower_id = 1U; /* set initial follower id */ + /* init pools */ + Obs_Ctor(&self->rx.message_freed_observer, self, &Ams_RxOnFreedMsg); + Amsp_AssignRxFreedObs(self->pool_ptr, &self->rx.message_freed_observer); + Telq_Ctor(&self->rx.waiting_queue, self->base_ptr->mns_inst_id); /* init Rx waiting queue */ + + Dl_Ctor(&self->tx.queue, self->base_ptr->mns_inst_id); + + Srv_Ctor(&self->service, AMS_SRV_PRIO, self, &Ams_Service); /* register service */ + (void)Scd_AddService(&self->base_ptr->scd, &self->service); + + Segm_Ctor(&self->segmentation, self->base_ptr, self->pool_ptr, rx_def_payload_sz); + Segm_AssignRxErrorHandler(&self->segmentation, &Ams_RxOnSegError, self); + + if (self->trcv_mcm_ptr != NULL) + { + Trcv_RxAssignReceiver(self->trcv_mcm_ptr, &Ams_RxOnMcmTelComplete, self); + } + if (self->trcv_rcm_ptr != NULL) + { + Trcv_RxAssignReceiver(self->trcv_rcm_ptr, &Ams_RxOnRcmTelComplete, self); + } + + Mobs_Ctor(&self->unsync_result_observer, self, EH_M_TERMINATION_EVENTS, &Ams_OnEhEvent); /* register error observer */ + Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->unsync_result_observer); +} + +/*! \brief Sets the default retry values used for Application Messages + * \param self The instance + * \param llrbc The default low level retry block count + */ +void Ams_TxSetDefaultRetries(CAms* self, uint8_t llrbc) +{ + self->tx.default_llrbc = llrbc; +} + +/*! \brief Assigns a function of another class to receive application messages + * \param self The instance + * \param cb_fptr Callback function + * \param inst_ptr The instance of the receiver class + */ +void Ams_RxAssignReceiver(CAms *self, Amsg_RxCompleteCb_t cb_fptr, void *inst_ptr) +{ + self->rx.complete_fptr = cb_fptr; + self->rx.complete_inst_ptr = inst_ptr; +} + +/*! \brief Assigns an observer which is invoked if a Tx application message is freed. + * \details The observer is only notified a previous allocation of a Tx object has failed. + * The data_ptr of the update callback function is not used (always \c NULL). + * See \ref Obs_UpdateCb_t. + * \param self The instance + * \param observer_ptr The observer + */ +void Ams_TxAssignMsgFreedObs(CAms *self, CObserver *observer_ptr) +{ + Amsp_AssignTxFreedObs(self->pool_ptr, observer_ptr); +} + +/*! \brief Assigns a callback function that selects FIFO routing for a Tx message. + * \details If no callback function is assigned, then all Tx messages are routed to RCM FIFO. + * \param self The instance + * \param cb_fptr The callback function + */ +void Ams_TxAssignTrcvSelector(CAms *self, Ams_TxIsRcmMsgCb_t cb_fptr) +{ + self->tx.is_rcm_fptr = cb_fptr; +} + +/*! \brief Performs a cleanup of the Tx message queue and notifies + * the transmission error MNS_AMSTX_RES_NOT_AVAILABLE. + * \param self The instance + */ +static void Ams_Cleanup(CAms *self) +{ + Mns_AmsTx_Msg_t *tx_ptr = NULL; + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Starting AMS Cleanup", 0U)); + /* cleanup Tx queue */ + for (tx_ptr = Amsg_TxDequeue(&self->tx.queue); tx_ptr != NULL; tx_ptr = Amsg_TxDequeue(&self->tx.queue)) + { + /* just just notify completion, the object is automatically freed to the pool */ + Amsg_TxNotifyComplete(tx_ptr, MNS_AMSTX_RES_ERR_NOT_AVAILABLE, MNS_AMSTX_I_ERR_UNSYNCED); + } + + Segm_Cleanup(&self->segmentation); /* cleanup Rx */ + Ams_RxFlush(self); + Amsp_Cleanup(self->pool_ptr); /* final cleanup of pool */ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Finished AMS Cleanup", 0U)); +} + +/*! \brief Callback function which is invoked by the event handler + * on any termination event. + * \param self The instance + * \param error_code_ptr Reference to the error code + */ +static void Ams_OnEhEvent(void *self, void *error_code_ptr) +{ + CAms *self_ = (CAms*)self; + MISC_UNUSED(error_code_ptr); + Ams_Cleanup(self_); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Service */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The AMS service function + * \param self The instance + */ +static void Ams_Service(void *self) +{ + CAms *self_ = (CAms*)self; + Srv_Event_t event_mask; + Srv_GetEvent(&self_->service, &event_mask); + + if (AMS_EV_TX_SERVICE == (event_mask & AMS_EV_TX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, AMS_EV_TX_SERVICE); + Ams_TxService(self_); + } + + if (AMS_EV_RX_SERVICE == (event_mask & AMS_EV_RX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, AMS_EV_RX_SERVICE); + Ams_RxProcessWaitingQ(self_); + } +} + +/*! \brief Allocates and transmits MCMs for the dedicated Application Messages + * \param self The instance + */ +static void Ams_TxService(CAms *self) +{ + CDlNode *node1_ptr; + /* run as long as messages are available in Tx queue */ + for (node1_ptr = Dl_PeekHead(&self->tx.queue); node1_ptr != NULL; node1_ptr = Dl_PeekHead(&self->tx.queue)) + { + Msg_MostTel_t *tel_ptr = NULL; + CTransceiver *trcv_ptr = self->trcv_mcm_ptr; + Mns_AmsTx_Msg_t *tx_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node1_ptr); + + if (self->tx.is_rcm_fptr != NULL) + { + if (self->tx.is_rcm_fptr(tx_ptr) != false) + { + trcv_ptr = self->trcv_rcm_ptr; + } + } + /* allocate telegram object with 2 bytes for TelId 4 */ + tel_ptr = Trcv_TxAllocateMsg(trcv_ptr, 2U); /* remaining message payload is attached as external memory */ + + if (tel_ptr != NULL) /* transmit message if telegram object is available */ + { + bool done; + CDlNode *node2_ptr = Dl_PopHead(&self->tx.queue); /* get application message from queue */ + + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (node1_ptr == node2_ptr)); + tx_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node2_ptr); + done = Segm_TxBuildSegment(&self->segmentation, tx_ptr, tel_ptr); /* run segmentation */ + Trcv_TxSendMsgExt(trcv_ptr, tel_ptr, &Ams_TxOnStatus, self); /* transmit telegram */ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Ams_TxService(tel_ptr=0x%p)", 1U, tel_ptr)); + + if (done == false) + { + Dl_InsertHead(&self->tx.queue, node2_ptr); + } + } + else + { + break; + } + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* AMS Tx handles */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves a message Tx handle + * \details The payload provided is limited the supported size of the memory management. + * The application may also attach own payload to a message object. Therefore, + * Ams_TxGetMsg() shall be called with size "0". + * \param self The instance + * \param size Payload size in bytes or "0" to use application provided payload. + * The payload provided by MNS is limited to a size of 45 bytes. + * Valid values: 0..45. + * \return A Tx message object or \c NULL if no message object is available. + */ +Mns_AmsTx_Msg_t * Ams_TxGetMsg(CAms *self, uint16_t size) +{ + Mns_AmsTx_Msg_t * msg_ptr = Amsp_AllocTxObj(self->pool_ptr, size); + + if (msg_ptr != NULL) + { + msg_ptr->destination_address = AMS_ADDR_RSVD_RANGE; /* set invalid address to prevent internal transmission*/ + msg_ptr->llrbc = self->tx.default_llrbc; + } + + return msg_ptr; +} + +/*! \brief Frees an unused or completed Tx message to the pool + * \param self The instance + * \param msg_ptr Reference to the related message object + */ +void Ams_TxFreeUnusedMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr) +{ + MISC_UNUSED(self); + Amsg_TxFreeUnused(msg_ptr); /* the object is automatically freed to the pool */ +} + +/*------------------------------------------------------------------------------------------------*/ +/* AMS Transmission */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Transmits a MOST Application Message + * \details After the transmission completed the function will call one callback function. Therefore + * the caller is able to assign one of two different callback function. The difference between + * the callback function is that tx_complete_sia_fptr does no provide a self pointer whether + * tx_complete_fptr and tx_complete_inst_ptr allow to invoke a class method. + * \param self The instance + * \param msg_ptr Reference to the related message object + * \param tx_complete_sia_fptr Single instance API callback function which is invoked as soon as + * the transmission was finished. + * \param tx_complete_fptr Multi instance callback function which is invoked as soon as + * the transmission was finished. + * \param tx_complete_inst_ptr Instance pointer which is referred when tx_complete_fptr is invoked. + * \return Possible return values are + * - \c MNS_RET_SUCCESS if the transmission was started successfully + * - \c MNS_RET_ERR_PARAM if the transmission was refused due to an invalid parameter + */ +Mns_Return_t Ams_TxSendMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr, + Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr) +{ + Mns_Return_t ret_val = MNS_RET_ERR_PARAM; + + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Called Ams_TxSendMsg(0x%p)", 1U, msg_ptr)); + + if (Ams_TxIsValidMessage(msg_ptr)) /* prevent application messages to loc. INIC */ + { /* do not set both callback pointers */ + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (((tx_complete_sia_fptr != NULL) && (tx_complete_fptr != NULL)) == false)) + Amsg_TxSetCompleteCallback(msg_ptr, tx_complete_sia_fptr, tx_complete_fptr, tx_complete_inst_ptr); + Ams_TxSendMsgDirect(self, msg_ptr); + ret_val = MNS_RET_SUCCESS; + } + + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (ret_val == MNS_RET_SUCCESS)); + + return ret_val; +} + +/*! \brief Transmits a MOST Application Message without attributes check + * \details This method shall be only be used by AMD and AMS internally + * \param self The instance + * \param msg_ptr Reference to the related message object + */ +void Ams_TxSendMsgDirect(CAms *self, Mns_AmsTx_Msg_t *msg_ptr) +{ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Called Ams_TxSendMsg(0x%p)", 1U, msg_ptr)); + + if (msg_ptr->data_size > SEGM_MAX_SIZE_TEL) /* set follower id to be used for all segments */ + { + Amsg_TxSetFollowerId(msg_ptr, Ams_TxGetNextFollowerId(self)); + } + + Amsg_TxEnqueue(msg_ptr, &self->tx.queue); /* schedule transmission */ + Srv_SetEvent(&self->service, AMS_EV_TX_SERVICE); +} + +/*! \brief Callback function which is invoked as soon as MCM transmission + * was finished in PMS. + * \param self The instance + * \param tel_ptr Reference to the telegram + * \param status Transmission status + */ +static void Ams_TxOnStatus(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status) +{ + CAms *self_ = (CAms*)self; + Mns_AmsTx_Msg_t* msg_ptr = (Mns_AmsTx_Msg_t*)tel_ptr->info_ptr; + + TR_INFO((self_->base_ptr->mns_inst_id, "[AMS]", "Ams_TxOnStatus(tel_ptr=0x%p, %d)", 2U, tel_ptr, status)); + + if (msg_ptr != NULL) /* MOST Telegram has AMS parent? */ + { + Amsg_TxUpdateResult(msg_ptr, status); + + if ((0U == tel_ptr->tel.tel_id) || (3U == tel_ptr->tel.tel_id)) /* is finished? */ + { /* just just notify completion, the object is */ + Amsg_TxNotifyComplete(msg_ptr, Amsg_TxGetResultCode(msg_ptr), Amsg_TxGetResultInfo(msg_ptr)); /* automatically freed to the pool */ + } + else if (status != MNS_MSG_STAT_OK) /* check transmission needs termination before transmission end */ + { + TR_ASSERT(self_->base_ptr->mns_inst_id, "[AMS]", (Amsg_TxGetFollowerId(msg_ptr) != 0U)); + + if (((uint8_t)Amsg_TxGetNextSegmCnt(msg_ptr) == (uint8_t)(tel_ptr->tel.tel_cnt + 1U)) /* is last transmitted segment */ + || ((Amsg_TxGetNextSegmCnt(msg_ptr) == 0U) && (4U == tel_ptr->tel.tel_id))) /* or TelId 4 and the first segment is pending */ + { + Amsg_TxRemoveFromQueue(msg_ptr, &self_->tx.queue); + Amsg_TxNotifyComplete(msg_ptr, Amsg_TxGetResultCode(msg_ptr), Amsg_TxGetResultInfo(msg_ptr)); /* just just notify completion, the object is */ + } /* automatically freed to the pool */ + } + } + Trcv_TxReleaseMsg(tel_ptr); /* release message object to pool */ + + if ((Dl_GetSize(&self_->tx.queue) > 0U) && (status != MNS_MSG_STAT_ERROR_SYNC)) /* Application Messages are available for Tx */ + { + Srv_SetEvent(&self_->service, AMS_EV_TX_SERVICE); + } +} + +/*! \brief Checks if the destination address of the Tx message is valid and payload is consistent + * \param msg_ptr Reference to the Tx message object + * \return Returns \c true if the destination is correct, otherwise \c false. + */ +bool Ams_TxIsValidMessage(Mns_AmsTx_Msg_t *msg_ptr) +{ + bool ret = false; + + if (msg_ptr != NULL) + { + if ((msg_ptr->destination_address == MNS_ADDR_INTERNAL) + ||(msg_ptr->destination_address > AMS_ADDR_RSVD_RANGE)) /* is not reserved address? */ + { + if (((msg_ptr->destination_address & 0xFF00U) != 0x0300U)/* is single-cast? */ + || (msg_ptr->data_size <= SEGM_MAX_SIZE_TEL)) /* or not segmented */ + { + if (!((msg_ptr->data_size > 0U) && (msg_ptr->data_ptr == NULL))) + { + ret = true; + } + } + } + } + + return ret; +} + +/*! \brief Retrieves the next follower id to use for segmented transfer + * \param self The instance + * \return The follower id + */ +static uint8_t Ams_TxGetNextFollowerId(CAms *self) +{ + uint8_t ret; + ret = self->tx.next_follower_id; + self->tx.next_follower_id++; + + if (self->tx.next_follower_id == 0U) /* skip zero since it means */ + { /* "no follower" */ + self->tx.next_follower_id = 1U; + } + + return ret; +} + +/*! \brief Retrieves the number of messages that are queued for transmission + * \param self The instance + * \return The number of messages in the Tx queue + */ +uint16_t Ams_TxGetMsgCnt(CAms *self) +{ + return Dl_GetSize(&self->tx.queue); +} + +/*------------------------------------------------------------------------------------------------*/ +/* AMS Reception */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Rx callback function that can be assigned to the MCM transceiver + * \details The associated transceiver reference will be stored in the telegrams \c info_ptr. + * Later on the telegram must be released via Ams_RxReleaseTel(). + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +void Ams_RxOnMcmTelComplete(void *self, Msg_MostTel_t *tel_ptr) +{ + CAms *self_ = (CAms*)self; + TR_ASSERT(self_->base_ptr->mns_inst_id, "[AMS]", (tel_ptr->info_ptr == NULL)); + tel_ptr->info_ptr = self_->trcv_mcm_ptr; + Ams_RxOnTelComplete(self_, tel_ptr); +} + +/*! \brief Rx callback function that can be assigned to the RCM transceiver + * \details The associated transceiver reference will be stored in the telegrams \c info_ptr. + * Later on the telegram must be released via Ams_RxReleaseTel(). + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +void Ams_RxOnRcmTelComplete(void *self, Msg_MostTel_t *tel_ptr) +{ + CAms *self_ = (CAms*)self; + TR_ASSERT(self_->base_ptr->mns_inst_id, "[AMS]", (tel_ptr->info_ptr == NULL)); + tel_ptr->info_ptr = self_->trcv_rcm_ptr; + Ams_RxOnTelComplete(self_, tel_ptr); +} + +/*! \brief Releases an Rx telegram to the associated transceiver + * \details The associated transceiver reference is stored in the telegrams \c info_ptr + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +static void Ams_RxReleaseTel(CAms *self, Msg_MostTel_t *tel_ptr) +{ + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", ((tel_ptr != NULL) && (tel_ptr->info_ptr != NULL))); + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", ((tel_ptr->info_ptr == self->trcv_mcm_ptr)||(tel_ptr->info_ptr == self->trcv_rcm_ptr))); + + if (tel_ptr->info_ptr != NULL) + { + Trcv_RxReleaseMsg((CTransceiver*)tel_ptr->info_ptr, tel_ptr); + } + + MISC_UNUSED(self); +} + +/*! \brief Internal callback function which is invoked as soon as the transceiver + * reference is stored to the telegrams info_ptr. + * \param self The instance + * \param tel_ptr Reference to the Rx telegram object + */ +static void Ams_RxOnTelComplete(CAms *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Ams_RxOnComplete(0x%p)", 1U, tel_ptr)); + + if (self->rx.complete_fptr == NULL) + { + /* no processing required, tel_ptr shall be freed */ + msg_ptr = NULL; + } + else if (Telq_GetSize(&self->rx.waiting_queue) > 0U) /* asynchronous Rx is running */ + { /* queue Rx telegram for later processing */ + Telq_Enqueue(&self->rx.waiting_queue, tel_ptr); + tel_ptr = NULL; /* do not free Rx telegram */ + msg_ptr = NULL; + } + else + { + Segm_Result_t result; /* synchronous processing is possible now */ + msg_ptr = Segm_RxExecuteSegmentation(&self->segmentation, tel_ptr, &result); + + if (result == SEGM_RES_RETRY) + { + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (msg_ptr == NULL)); + Telq_Enqueue(&self->rx.waiting_queue, tel_ptr); + tel_ptr = NULL; /* do not free Rx telegram */ + } + } + + if (msg_ptr != NULL) + { + self->rx.complete_fptr(self->rx.complete_inst_ptr, (Mns_AmsRx_Msg_t*)(void*)msg_ptr); + } + + if (tel_ptr != NULL) + { + Ams_RxReleaseTel(self, tel_ptr); /* free Rx telegram */ + } +} + +/*! \brief Processes all telegrams in waiting queue + * \details Stops if allocation of Rx messages fails + * \param self The instance + */ +static void Ams_RxProcessWaitingQ(CAms *self) +{ + Msg_MostTel_t *tel_ptr; + Mns_AmsRx_Msg_t *msg_ptr = NULL; + + for (tel_ptr = Telq_Peek(&self->rx.waiting_queue); tel_ptr != NULL; tel_ptr = Telq_Peek(&self->rx.waiting_queue)) + { + Segm_Result_t result; + msg_ptr = Segm_RxExecuteSegmentation(&self->segmentation, tel_ptr, &result); + + if (result == SEGM_RES_OK) /* segmentation process succeeded */ + { + (void)Telq_Dequeue(&self->rx.waiting_queue); /* remove telegram from waitingQ */ + Ams_RxReleaseTel(self, tel_ptr); /* free telegram */ + tel_ptr = NULL; /* parasoft-suppress MISRA2004-13_6 "variable is not used as a counter" */ + + if (msg_ptr != NULL) + { + self->rx.complete_fptr(self->rx.complete_inst_ptr, (Mns_AmsRx_Msg_t*)(void*)msg_ptr); + } + } + else + { + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (msg_ptr == NULL)); + break; /* wait until further Rx messages can be allocated - abort loop */ + } + } +} + +/*! \brief Callback function which is invoked by segmentation process to notify a segmentation error + * \param self The instance + * \param tel_ptr The related Rx telegram which caused the segmentation error + * \param error The segmentation error number + */ +static void Ams_RxOnSegError(void *self, Msg_MostTel_t *tel_ptr, Segm_Error_t error) +{ + const uint8_t ERR_SZ = 2U; + CAms *self_ = (CAms*)self; + Msg_MostTel_t* error_tel_ptr = NULL; + + TR_ERROR((self_->base_ptr->mns_inst_id, "[AMS]", "Ams_RxOnComplete(0x%p, %d)", 2U, tel_ptr, error)); + + if (tel_ptr->source_addr != MSG_ADDR_INIC) + { /* only generate segmentation errors */ + error_tel_ptr = Trcv_TxAllocateMsg(self_->trcv_mcm_ptr, ERR_SZ); /* for messages which are NOT locally routed by the INIC */ + } + + if (error_tel_ptr != NULL) + { + error_tel_ptr->destination_addr = tel_ptr->source_addr; + error_tel_ptr->id = tel_ptr->id; + error_tel_ptr->id.op_type = MNS_OP_ERROR; + error_tel_ptr->tel.tel_data_ptr[0] = 0x0CU; + error_tel_ptr->tel.tel_data_ptr[1] = (uint8_t)error; + error_tel_ptr->opts.llrbc = 0U; + + Trcv_TxSendMsg(self_->trcv_mcm_ptr, error_tel_ptr); /* just fire the message */ + } +} + +/*! \brief Callback function that is invoked if application Rx messages are available again + * \param self The instance + * \param data_ptr Unused parameter of observer callback + */ +static void Ams_RxOnFreedMsg(void *self, void *data_ptr) +{ + CAms *self_ = (CAms*) self; + Srv_SetEvent(&self_->service, AMS_EV_RX_SERVICE); + MISC_UNUSED(data_ptr); +} + +/*! \brief Removes and frees a message from the Rx queue + * \details The application must not access the passed + * message any more. + * \param self The instance + * \param msg_ptr Reference to the message, or \c NULL for the front-most + * message in the Rx queue. + */ +void Ams_RxFreeMsg(CAms *self, Mns_AmsRx_Msg_t *msg_ptr) +{ + TR_INFO((self->base_ptr->mns_inst_id, "[AMS]", "Ams_RxFreeMsg(msg_ptr=0x%p)", 1U, msg_ptr)); + TR_ASSERT(self->base_ptr->mns_inst_id, "[AMS]", (msg_ptr != NULL)); + + if (msg_ptr != NULL) + { + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); /* free external payload */ + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); /* return message to Rx pool */ + } +} + +/*! \brief Removes all messages located in Rx queues + * \param self The instance + */ +static void Ams_RxFlush(CAms *self) +{ + Msg_MostTel_t *tel_ptr; + + for (tel_ptr = Telq_Dequeue(&self->rx.waiting_queue); tel_ptr != NULL; tel_ptr = Telq_Dequeue(&self->rx.waiting_queue)) + { + Ams_RxReleaseTel(self, tel_ptr); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ams.h b/mnsl/mns_ams.h new file mode 100644 index 0000000..fb291c1 --- /dev/null +++ b/mnsl/mns_ams.h @@ -0,0 +1,163 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of Application Message Service + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSC + * @{ + */ + +#ifndef MNS_AMS_H +#define MNS_AMS_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams_pb.h" +#include "mns_amsmessage.h" +#include "mns_amspool.h" +#include "mns_pool.h" +#include "mns_transceiver.h" +#include "mns_pmchannel.h" +#include "mns_ret.h" +#include "mns_segmentation.h" +#include "mns_message.h" +#include "mns_telqueue.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines the default LLR number for Application Messages */ +#define AMS_LLRBC_DEFAULT (MSG_LLRBC_DEFAULT) +/*! \brief Defines the maximum LLR number for Application Messages */ +#define AMS_LLRBC_MAX (MSG_LLRBC_MAX) +/*! \brief Default memory size that is allocated when receiving segmented messages + * without size prefix */ +#define AMS_RX_DEF_SIZE_PAYLOAD 400U +/*! \brief Maximum destination address which is reserved for internal transmission */ +#define AMS_ADDR_RSVD_RANGE 0x000FU + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback function type to request FIFO routing for a Tx message. + * \param msg_ptr Reference to a Tx message object + * \return Returns \c true if a Tx message shall be routed to RCM FIFO, otherwise returns \c false. + */ +typedef bool (*Ams_TxIsRcmMsgCb_t)(Mns_AmsTx_Msg_t *msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Class */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Application Message Service Class + * \details Allows transmission and reception of MOST Application Messages + */ +typedef struct CAms_ +{ + CService service; /*!< \brief Service object */ + CSegmentation segmentation; /*!< \brief Segmentation object */ + CMaskedObserver unsync_result_observer; /*!< \brief Observes un-sync result */ + + CBase *base_ptr; /*!< \brief Reference to basic services */ + CAmsMsgPool *pool_ptr; /*!< \brief Pool providing Rx and Tx objects/payload */ + CTransceiver *trcv_mcm_ptr; /*!< \brief Reference to MCM transceiver */ + CTransceiver *trcv_rcm_ptr; /*!< \brief Reference to RCM transceiver */ + + struct Ams_tx_ + { + CDlList queue; /*!< \brief Input queue of Tx Application Messages */ + uint8_t default_llrbc; /*!< \brief Default LowLevelRetryBlockCount. Valid values: 0..100 */ + Ams_TxIsRcmMsgCb_t is_rcm_fptr; /*!< \brief Assignable callback function to request the correct transceiver */ + uint8_t next_follower_id; /*!< \brief The follower id for the next segmented + * message + */ + } tx; + + struct Ams_rx_ + { + CObserver message_freed_observer; /*!< \brief Observes message freed event */ + + Amsg_RxCompleteCb_t complete_fptr; /*!< \brief Callback function which is invoked on + * message reception + */ + void *complete_inst_ptr; /*!< \brief Instance which is notified on + * message reception + */ + CTelQueue waiting_queue; /*!< \brief Queue of unprocessed single telegrams */ + + } rx; + +} CAms; + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ams_Ctor(CAms *self, CBase *base_ptr, CTransceiver *mcm_trcv_ptr, CTransceiver *rcm_trcv_ptr, + CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz); + +/*------------------------------------------------------------------------------------------------*/ +/* Public methods / Tx */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ams_TxSetDefaultRetries(CAms *self, uint8_t llrbc); +extern void Ams_TxAssignMsgFreedObs(CAms *self, CObserver *observer_ptr); +extern void Ams_TxAssignTrcvSelector(CAms *self, Ams_TxIsRcmMsgCb_t cb_fptr); +extern Mns_AmsTx_Msg_t* Ams_TxGetMsg(CAms *self, uint16_t size); +extern void Ams_TxFreeUnusedMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr); +extern uint16_t Ams_TxGetMsgCnt(CAms *self); +extern bool Ams_TxIsValidMessage(Mns_AmsTx_Msg_t *msg_ptr); +extern void Ams_TxSendMsgDirect(CAms *self, Mns_AmsTx_Msg_t *msg_ptr); +extern Mns_Return_t Ams_TxSendMsg(CAms *self, Mns_AmsTx_Msg_t *msg_ptr, Amsg_TxCompleteSiaCb_t tx_complete_sia_fptr, + Amsg_TxCompleteCb_t tx_complete_fptr, void* tx_complete_inst_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Public methods / Rx */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ams_RxAssignReceiver(CAms *self, Amsg_RxCompleteCb_t cb_fptr, void *inst_ptr); +extern void Ams_RxFreeMsg(CAms *self, Mns_AmsRx_Msg_t *msg_ptr); +extern void Ams_RxOnMcmTelComplete(void *self, Msg_MostTel_t *tel_ptr); +extern void Ams_RxOnRcmTelComplete(void *self, Msg_MostTel_t *tel_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_AMS_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ams_pb.h b/mnsl/mns_ams_pb.h new file mode 100644 index 0000000..a559c94 --- /dev/null +++ b/mnsl/mns_ams_pb.h @@ -0,0 +1,262 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Public header file of Application Message Service + */ +/*! + * \addtogroup G_MNS_AMS_TYPES + * @{ + */ + +#ifndef MNS_AMS_PB_H +#define MNS_AMS_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_message_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! \brief Defines which address type was used by the transmitter of a message. */ +typedef enum Mns_AmsRx_ReceiveType_ +{ + MNS_AMSRX_RCT_SINGLECAST = 0U, /*!< \brief Message was transmitted as singlecast */ + MNS_AMSRX_RCT_GROUPCAST = 1U, /*!< \brief Message was transmitted as groupcast */ + MNS_AMSRX_RCT_BROADCAST = 2U /*!< \brief Message was transmitted as broadcast */ + +} Mns_AmsRx_ReceiveType_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Application message Tx type */ +typedef struct Mns_AmsTx_Msg_ +{ + uint16_t destination_address; /*!< \brief Destination address. Find some predefined addresses \ref G_MNS_AMS "here". */ + uint8_t fblock_id; /*!< \brief Function block ID (MOST FBlockID). */ + uint8_t instance_id; /*!< \brief Instance ID (MOST InstID). */ + uint16_t function_id; /*!< \brief Function ID (MOST FktID). */ + Mns_OpType_t op_type; /*!< \brief Operation type (MOST OpType). */ + uint8_t llrbc; /*!< \brief Specifies the "Low-Level Retry Block Count" (LLRBC) + * \details Valid values: 0..100. Default value: configurable via \ref Mns_AmsTx_InitData_t "default_llrbc" + * of the initialization structure \ref Mns_AmsTx_InitData_t. + * \mns_ic_inic{ See also INIC API User's Guide, section \ref SEC_OS81118_19. } + */ + uint8_t *data_ptr; /*!< \brief Payload data */ + uint16_t data_size; /*!< \brief The size of payload data in bytes */ + void *custom_info_ptr; /*!< \brief Customer specific reference + * \details The application is allowed to use this attribute to assign an + * own reference to the message object. The reference is initialized + * by MOST NetServices with \c NULL and will not alter until the + * transmission has finished. + */ +} Mns_AmsTx_Msg_t; + +/*! \brief Application message Rx type */ +typedef struct Mns_AmsRx_Msg_ +{ + uint16_t source_address; /*!< \brief Source address */ + uint8_t fblock_id; /*!< \brief Function block ID (MOST FBlockID). */ + uint8_t instance_id; /*!< \brief Instance ID (MOST InstID). */ + uint16_t function_id; /*!< \brief Function ID (MOST FktID). */ + Mns_OpType_t op_type; /*!< \brief Operation type (MOST OpType). */ + uint8_t *data_ptr; /*!< \brief Reference to payload */ + uint16_t data_size; /*!< \brief Payload size in bytes */ + void *custom_info_ptr; /*!< \brief Customer specific reference */ + Mns_AmsRx_ReceiveType_t receive_type; /*!< \brief Defines which address type was used by the transmitter of this message */ + +} Mns_AmsRx_Msg_t; + +/*! \brief Transmission result of an application message */ +typedef enum Mns_AmsTx_Result_ +{ + MNS_AMSTX_RES_SUCCESS = 0x00U,/*!< \brief The transmission succeeded. */ + + MNS_AMSTX_RES_ERR_RETRIES_EXP = 0x01U,/*!< \brief The transmission including all retries have failed. + * \details The following issues may have caused the failure: + * - message corruption + * - transmission timeouts + * - full receive buffers of the destination device + * - full receive buffers of the local device if the + * destination was the own address, own group or broadcast + * address + * . + */ + MNS_AMSTX_RES_ERR_INVALID_TGT = 0x02U,/*!< \brief The transmission failed because the specified destination + * address is not found or not valid. + * \details The following issues may have caused the failure: + * - device with the given destination address is not found + * - destination address is reserved (for future use) + * - destination address is 0xFFFF (un-initialized logical + * node address is not supported) + * . + */ + MNS_AMSTX_RES_ERR_NOT_AVAILABLE = 0x03U,/*!< \brief The transmission failed since the network or the INIC + * is not available. + */ + MNS_AMSTX_RES_ERR_BUF_INTERNAL = 0xFEU,/*!< \brief The transmission failed because the allocation of an Rx message object failed. + * The Rx message object is required to receive the message via the own Rx message queue. + * \details This is possible in the following cases: + * - A message is transmitted to the own node address and the allocation + * of an Rx message object failed. + * - The network transmission to the own group address or broadcast address + * succeeded but the allocation of an Rx message object failed. The application + * has to decide whether to retransmit the message to the own address again. + */ + MNS_AMSTX_RES_ERR_UNEXPECTED = 0xFFU /*!< \brief The transmission failed due to an unexpected error. + * The cause of this failure may be an invalid INIC configuration, + * or an INIC to MOST NetServices incompatibility issue. + */ +} Mns_AmsTx_Result_t; + + +/*! \brief Detailed INIC transmission information which might be useful for debugging purposes. */ +typedef enum Mns_AmsTx_Info_ +{ + MNS_AMSTX_I_SUCCESS = 0x00U, /*!< \brief The transmission succeeded. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_SUCCESS. + */ + MNS_AMSTX_I_ERR_CFG_NORECEIVER = 0x01U, /*!< \brief The transmission failed because the MOST network is not accessible for + * MCM in the current attach state or for ICM in general. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_UNEXPECTED. + */ + MNS_AMSTX_I_ERR_BF = 0x08U, /*!< \brief The transmission failed because the receivers buffer is full. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_CRC = 0x09U, /*!< \brief The transmission failed because of a failed CRC. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_ID = 0x0AU, /*!< \brief The transmission failed because of corrupted identifiers. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_ACK = 0x0BU, /*!< \brief The transmission failed because of corrupted PACK or CACK. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_TIMEOUT = 0x0CU, /*!< \brief The transmission failed because of a transmission timeout. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_RETRIES_EXP. + */ + MNS_AMSTX_I_ERR_FATAL_WT = 0x10U, /*!< \brief The transmission failed because of destination is not available. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_INVALID_TGT. + */ + MNS_AMSTX_I_ERR_FATAL_OA = 0x11U, /*!< \brief The transmission failed because of the destination is the own node address. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_INVALID_TGT. + */ + MNS_AMSTX_I_ERR_UNAVAIL_TRANS = 0x18U, /*!< \brief The transmission canceled during the transition from network interface state + * "available" to "not available". + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_NOT_AVAILABLE. + */ + MNS_AMSTX_I_ERR_UNAVAIL_OFF = 0x19U, /*!< \brief The transmission failed because the network interface state is "not available". + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_NOT_AVAILABLE. + */ + MNS_AMSTX_I_ERR_UNKNOWN = 0xFEU, /*!< \brief The transmission failed because of an unknown INIC error code. + * \details The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_UNEXPECTED. + * Please check if the MNS version is compatible with the applied INIC firmware version. + */ + MNS_AMSTX_I_ERR_UNSYNCED = 0xFFU /*!< \brief The transmission failed because the communication between the EHC (MOST NetServices) + * and the INIC is lost. + * \details The reason can be a communication error between the EHC and the INIC or that + * the application has called Mns_Stop().\n + * The corresponding transmission result is \ref MNS_AMSTX_RES_ERR_NOT_AVAILABLE. + */ +} Mns_AmsTx_Info_t; + + +/*! \brief Defines the usage of a requested memory chunk */ +typedef enum Mns_Ams_MemUsage_ +{ + MNS_AMS_RX_OBJECT, /*!< \brief Memory is required to allocate an Rx message object */ + MNS_AMS_RX_PAYLOAD, /*!< \brief Memory is required to allocate Rx message payload */ + MNS_AMS_TX_OBJECT, /*!< \brief Memory is required to allocate a Tx message object */ + MNS_AMS_TX_PAYLOAD /*!< \brief Memory is required to allocate Tx message payload */ + +} Mns_Ams_MemUsage_t; + +/*! \brief Callback function type that is invoked to allocate external payload for a segmented Rx message + * \param inst_ptr Reference to the (external) memory management + * \param mem_size Reference to the required memory size in bytes. Valid values: 0..65535. + * \param type Declares how the memory is used by the MOST NetServices + * \param custom_info_pptr Reference which is related to the memory chunk and can be set by + * the application. + * \return Pointer to the provided memory chunk. The application has to guarantee that the memory size + * is equal or greater than \c mem_size. The application has to return \c NULL if it is not able + * to allocate the required memory at this moment. + */ +typedef void* (*Mns_Ams_AllocMemCb_t)(void *inst_ptr, uint16_t mem_size, Mns_Ams_MemUsage_t type, void** custom_info_pptr); + +/*! \brief Callback function type that is invoked to free external payload for a segmented Rx message + * \param inst_ptr Reference to the (external) memory management + * \param mem_ptr Reference to the external payload memory + * \param type Declares how the memory is used by the MOST NetServices + * \param custom_info_ptr Reference to memory related information which was set by the application + * during memory allocation + */ +typedef void (*Mns_Ams_FreeMemCb_t)(void *inst_ptr, void *mem_ptr, Mns_Ams_MemUsage_t type, void* custom_info_ptr); + +/*! \brief Type of a callback function that is invoked by the MOST NetServices as soon as a + * message transmission was finished + * \details The callback function notifies the result of a completed transmission. If + * the message has external payload, the application must decide whether + * to re-use or to free the external payload. + * \param msg_ptr Reference to the related Tx message object. When the callback function returns + * the reference is no longer valid. + * \param result The transmission result. + * \param info Detailed INIC transmission result, which might be helpful for debug purposes. + */ +typedef void (*Mns_AmsTx_CompleteCb_t)(Mns_AmsTx_Msg_t* msg_ptr, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); + +/*! + * @} + * \addtogroup G_MNS_AMS + * @{ + */ + +/*! \brief Type of a callback function that is invoked by MOST NetServices to notify that + * a Tx application message object is available again while a previous + * allocation using Mns_AmsTx_AllocMsg() has failed. + */ +typedef void (*Mns_AmsTx_MsgFreedCb_t)(void); + +/*! \brief Callback function type that is invoked if the MOST NetServices has received a message + * completely and appended to the Rx message queue. + */ +typedef void (*Mns_AmsRx_MsgReceivedCb_t)(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ifndef MNS_AMS_PB_H */ + +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amsmessage.c b/mnsl/mns_amsmessage.c new file mode 100644 index 0000000..11ba985 --- /dev/null +++ b/mnsl/mns_amsmessage.c @@ -0,0 +1,656 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of Application Message Classes + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSMSG + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_amsmessage.h" +#include "mns_dl.h" +#include "mns_misc.h" +#include "mns_trace.h" + +#define SELF_RX ((Amsg_IntMsgRx_t*)(void*)(self)) +#define SELF_TX ((Amsg_IntMsgTx_t*)(void*)(self)) + +#define AMSG_TX_BACKUP_ADDR_NONE 0U + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static Mns_AmsRx_ReceiveType_t Amsg_RxGetReceiveType(uint16_t destination_address); +static void Amsg_TxRestoreDestinationAddr(Mns_AmsTx_Msg_t *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Tx Message */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initializes aggregated objects + * \details Needs to be called once before first usage. Call Amsg_TxHandleSetup() before + * repeated usage. + * \param self Reference to an internal Application Message Tx handle + * \param info_ptr Memory information required to free the object + * \param free_fptr Callback function which is invoked when the object is freed + * \param free_inst_ptr The instance which is passed to free_fptr + */ +void Amsg_TxCtor(Mns_AmsTx_Msg_t *self, void *info_ptr, Amsg_TxFreedCb_t free_fptr, void *free_inst_ptr) +{ + /* cleanup complete object */ + MISC_MEM_SET((void*)self, 0, sizeof(Amsg_IntMsgTx_t)); + + /* reset default references + SELF_TX->memory_ptr = NULL; + SELF_TX->memory_sz = NULL; + SELF_TX->memory_info_ptr = NULL; + + SELF_TX->complete_fptr = NULL; + SELF_TX->complete_inst_ptr = NULL; + SELF_TX->complete_sia_fptr = NULL; + + SELF_TX->backup_dest_address = AMSG_TX_BACKUP_ADDR_NONE;*/ + + SELF_TX->info_ptr = info_ptr; + SELF_TX->free_fptr = free_fptr; + SELF_TX->free_inst_ptr = free_inst_ptr; + SELF_TX->next_segment_cnt = 0xFFFFU; /* start with TelId "4" */ + SELF_TX->temp_result = MNS_MSG_STAT_OK; + SELF_TX->internal_status = AMSG_TX_INTRES_NONE; + SELF_TX->ignore_wrong_target = false; + + Dln_Ctor(&SELF_TX->node, self); /* initialize node */ +} + +/*! \brief Sets payload memory provided by memory management and updates data pointer and size + * \param self The instance + * \param mem_ptr Reference to the provided memory chunk + * \param mem_size Size of the provided memory chunk + * \param mem_info_ptr Optional reference for memory management + */ +void Amsg_TxSetInternalPayload(Mns_AmsTx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *mem_info_ptr) +{ + SELF_TX->memory_ptr = mem_ptr; + SELF_TX->memory_sz = mem_size; + SELF_TX->memory_info_ptr = mem_info_ptr; + + SELF_TX->pb_msg.data_ptr = mem_ptr; + SELF_TX->pb_msg.data_size = mem_size; +} + +/*! \brief Prepares the message object for re-usage + * \details The public message structure is re-initialized. The internal payload + * is assigned to the public data reference. + * \param self The instance + */ +void Amsg_TxReuse(Mns_AmsTx_Msg_t *self) +{ + MISC_MEM_SET((void *)&SELF_TX->pb_msg, 0, sizeof(SELF_TX->pb_msg)); /* cleanup public object */ + /* SELF_TX->backup_dest_address = AMSG_TX_BACKUP_ADDR_NONE; */ + + SELF_TX->pb_msg.data_ptr = SELF_TX->memory_ptr; /* reset public payload */ + SELF_TX->pb_msg.data_size = SELF_TX->memory_sz; + + SELF_TX->next_segment_cnt = 0xFFFFU; /* start with TelId "4" */ + SELF_TX->temp_result = MNS_MSG_STAT_OK; + SELF_TX->internal_status = AMSG_TX_INTRES_NONE; +} + +/*! \brief Assigns a Tx complete callback function + * \details It is not possible to assign the single and multiple instance callback + * at the same time. This function shall be called before message transmission. + * \param self The instance + * \param compl_sia_fptr Reference to the single instance callback function + * \param compl_fptr Reference to a multiple instance callback function + * \param compl_inst_ptr Instance which is invoked by compl_fptr() + */ +void Amsg_TxSetCompleteCallback(Mns_AmsTx_Msg_t *self, Amsg_TxCompleteSiaCb_t compl_sia_fptr, + Amsg_TxCompleteCb_t compl_fptr, void* compl_inst_ptr) +{ + SELF_TX->complete_sia_fptr = compl_sia_fptr; + SELF_TX->complete_fptr = compl_fptr; + SELF_TX->complete_inst_ptr = compl_inst_ptr; +} + +/*! \brief Invokes the correct callback function to notify the transmission result + * and frees the memory + * \param self Reference to the related message object + * \param result The transmission result + * \param info The INIC transmission result + */ +void Amsg_TxNotifyComplete(Mns_AmsTx_Msg_t *self, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info) +{ + Amsg_TxRestoreDestinationAddr(self); + + if (SELF_TX->complete_sia_fptr != NULL) /* invoke single instance API callback */ + { + SELF_TX->complete_sia_fptr(self, result, info); + } + else if (SELF_TX->complete_fptr != NULL) + { + SELF_TX->complete_fptr(SELF_TX->complete_inst_ptr, self, result, info); + } + + TR_ASSERT(0xFU, "[AMSG_TX]", (SELF_TX->free_fptr != NULL)); + if (SELF_TX->free_fptr != NULL) + { + SELF_TX->free_fptr(SELF_TX->free_inst_ptr, self); + } +} + +/*! \brief Frees an unused message object to the owning pool + * \param self Reference to the message object + */ +void Amsg_TxFreeUnused(Mns_AmsTx_Msg_t *self) +{ + TR_ASSERT(0xFU, "[AMSG_TX]", (SELF_TX->free_fptr != NULL)); + if (SELF_TX->free_fptr != NULL) + { + SELF_TX->free_fptr(SELF_TX->free_inst_ptr, self); + } +} + +/*! \brief Updates the transmission result + * \param self Reference to the related message object + * \param result The latest MCM transmission result + * \details Since the transmission result of an application message may + * consist of multiple telegram transmission results, it is + * important to store the final transmission error. An error cannot + * be overwritten by a success. + */ +void Amsg_TxUpdateResult(Mns_AmsTx_Msg_t *self, Mns_MsgTxStatus_t result) +{ + if (result != MNS_MSG_STAT_OK) /* store the latest error and do not overwrite with success */ + { + SELF_TX->temp_result = result; + } +} + +/*! \brief Updates the internal transmission result + * \param self Reference to the related message object + * \param result The internal transmission result + * \details The internal transmission result must be updated if the + * the message is transmitted to the internal Rx queue. + */ +void Amsg_TxUpdateInternalResult(Mns_AmsTx_Msg_t *self, Amsg_TxIntStatus_t result) +{ + SELF_TX->internal_status = result; +} + +/*! \brief Returns the latest AMS transmission result code + * \param self Reference to the related message object + * \return Returns the transmission result which shall be notified to the application + */ +Mns_AmsTx_Result_t Amsg_TxGetResultCode(Mns_AmsTx_Msg_t *self) +{ + Mns_AmsTx_Result_t res = MNS_AMSTX_RES_SUCCESS; /* success is the expected result */ + + switch (SELF_TX->temp_result) + { + case MNS_MSG_STAT_OK: + if (SELF_TX->internal_status == AMSG_TX_INTRES_ERRBUF) + { + res = MNS_AMSTX_RES_ERR_BUF_INTERNAL; /* internal transmission error overrules network success */ + } + break; + case MNS_MSG_STAT_ERROR_BF: + case MNS_MSG_STAT_ERROR_CRC: + case MNS_MSG_STAT_ERROR_ID: + case MNS_MSG_STAT_ERROR_ACK: + case MNS_MSG_STAT_ERROR_TIMEOUT: + res = MNS_AMSTX_RES_ERR_RETRIES_EXP; /* transmission failed, retries are possible */ + break; + case MNS_MSG_STAT_ERROR_FATAL_WT: + case MNS_MSG_STAT_ERROR_FATAL_OA: + if (SELF_TX->internal_status == AMSG_TX_INTRES_ERRBUF) + { + res = MNS_AMSTX_RES_ERR_BUF_INTERNAL; /* internal transmission error and network node not found */ + } + else if (SELF_TX->internal_status == AMSG_TX_INTRES_NONE) + { + res = MNS_AMSTX_RES_ERR_INVALID_TGT; /* not transmitted internally and no network node found */ + } + /* else -> internal success -> target node was found locally */ + break; + case MNS_MSG_STAT_ERROR_NA_TRANS: + case MNS_MSG_STAT_ERROR_NA_OFF: + if (SELF_TX->internal_status != AMSG_TX_INTRES_SUCCESS) + { + res = MNS_AMSTX_RES_ERR_NOT_AVAILABLE; /* successful if internal transmission succeeded, otherwise "not available" */ + } + break; + case MNS_MSG_STAT_ERROR_SYNC: + res = MNS_AMSTX_RES_ERR_NOT_AVAILABLE; + break; + default: + res = MNS_AMSTX_RES_ERR_UNEXPECTED; /* unexpected network transmission state */ + break; + } + + return res; +} + +/*! \brief Returns the latest MCM transmission error + * \param self Reference to the related message object + * \return Returns the INIC transmission result which is provided as additional info + */ +Mns_AmsTx_Info_t Amsg_TxGetResultInfo(Mns_AmsTx_Msg_t *self) +{ + Mns_AmsTx_Info_t res = (Mns_AmsTx_Info_t)SELF_TX->temp_result; + + if ((SELF_TX->temp_result == MNS_MSG_STAT_ERROR_FATAL_WT) && (SELF_TX->ignore_wrong_target != false)) + { + res = MNS_AMSTX_I_SUCCESS; + } + + return res; +} + +/*! \brief Queues a Tx message at the tail of a list + * \param self The instance + * \param list_ptr Reference to the list + */ +void Amsg_TxEnqueue(Mns_AmsTx_Msg_t* self, CDlList* list_ptr) +{ + Dl_InsertTail(list_ptr, &SELF_TX->node); +} + +/*! \brief Retrieves the next segment count + * \param self The instance + * \return The next segment count as uint16_t + */ +uint16_t Amsg_TxGetNextSegmCnt(Mns_AmsTx_Msg_t *self) +{ + return SELF_TX->next_segment_cnt; +} + +/*! \brief Increments the next segment count + * \param self The instance + */ +void Amsg_TxIncrementNextSegmCnt(Mns_AmsTx_Msg_t *self) +{ + SELF_TX->next_segment_cnt++; +} + +/*! \brief Retrieves the follower id which labels all telegrams of a segmented message + * \param self The instance + * \return The follower id + */ +uint8_t Amsg_TxGetFollowerId(Mns_AmsTx_Msg_t *self) +{ + return SELF_TX->follower_id; +} + +/*! \brief Sets the follower id which labels all telegrams of a segmented message + * \param self The instance + * \param id The follower id + */ +void Amsg_TxSetFollowerId(Mns_AmsTx_Msg_t *self, uint8_t id) +{ + SELF_TX->follower_id = id; +} + +/*! \brief Replaces the current destination address by a new one. + * \details The current destination address can be restore by Amsg_TxRestoreDestinationAddr(). + * \param self The instance + * \param new_destination The new destination address + */ +void Amsg_TxReplaceDestinationAddr(Mns_AmsTx_Msg_t *self, uint16_t new_destination) +{ + SELF_TX->backup_dest_address = self->destination_address; /* internal backup of current destination address */ + self->destination_address = new_destination; /* replace public destination address */ +} + +/*! \brief Restores the destination address which was saved by calling Amsg_TxReplaceDestinationAddr(). + * \param self The instance + */ +static void Amsg_TxRestoreDestinationAddr(Mns_AmsTx_Msg_t *self) +{ + if (SELF_TX->backup_dest_address != AMSG_TX_BACKUP_ADDR_NONE) + { + self->destination_address = SELF_TX->backup_dest_address;/* restore public destination address */ + } +} + +/*! \brief Removes a message from a given queue + * \param self The instance + * \param list_ptr The queue that contains the message + */ +void Amsg_TxRemoveFromQueue(Mns_AmsTx_Msg_t *self, CDlList *list_ptr) +{ + (void)Dl_Remove(list_ptr, &SELF_TX->node); +} + +/*! \brief Peeks a Tx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Tx message + */ +Mns_AmsTx_Msg_t* Amsg_TxPeek(CDlList* list_ptr) +{ + Mns_AmsTx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PeekHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*! \brief Removes a Tx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Tx message + */ +Mns_AmsTx_Msg_t* Amsg_TxDequeue(CDlList* list_ptr) +{ + Mns_AmsTx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PopHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsTx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx Message */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initializes aggregated objects + * \details Needs to be called once before first usage. Call Amsg_RxHandleSetup() before + * repeated usage. + * \param self Reference to an internal Application Message Rx handle + * \param info_ptr Memory information required to free the object + */ +void Amsg_RxCtor(Mns_AmsRx_Msg_t *self, void *info_ptr) +{ + Dln_Ctor(&SELF_RX->node, SELF_RX); + SELF_RX->info_ptr = info_ptr; /* reset memory information */ + SELF_RX->memory_sz = 0U; + SELF_RX->memory_ptr = NULL; + SELF_RX->memory_info_ptr = NULL; +} + +/*! \brief Copies all attributes and payload from a Tx message to the Rx message + * \details The caller has to ensure that the payload size of the Rx message is equal + * or greater than the payload size of the Tx message. + * \param self Reference to an Rx message object + * \param tx_ptr Reference to an Tx message object + * \param source_address The source address that shall be set in the Rx message + */ +void Amsg_RxBuildFromTx(Mns_AmsRx_Msg_t *self, Mns_AmsTx_Msg_t *tx_ptr, uint16_t source_address) +{ + TR_ASSERT(0xFU,"[AMSG]", (SELF_RX->memory_sz >= tx_ptr->data_size)); + + self->receive_type = Amsg_RxGetReceiveType(tx_ptr->destination_address); + self->source_address = source_address; + self->fblock_id = tx_ptr->fblock_id; + self->instance_id = tx_ptr->instance_id; + self->function_id = tx_ptr->function_id; + self->op_type = tx_ptr->op_type; + self->data_size = tx_ptr->data_size; + + Misc_MemCpy(self->data_ptr, tx_ptr->data_ptr, (size_t)self->data_size); +} + +/*! \brief Sets all attributes of an internal Rx message to valid values + * \param self Reference to an internal Rx message object + * \details Assigned payload memory has to be freed before calling this function + */ +void Amsg_RxHandleSetup(Mns_AmsRx_Msg_t *self) +{ + MISC_MEM_SET((void *)&SELF_RX->pb_msg, 0, sizeof(SELF_RX->pb_msg)); /* cleanup public message object */ + SELF_RX->pb_msg.data_ptr = SELF_RX->memory_ptr; /* set data to valid memory */ + SELF_RX->gc_marker = false; /* reset garbage collector flag */ + SELF_RX->exp_tel_cnt = 0U; /* reset TelCnt */ +} + +/*! \brief Evaluates if an Application Message has the same functional address + * as a MOST telegram + * \param self Reference to an internal Application Message Rx handle + * \param tel_ptr Reference to a MOST message object + * \return Returns \c true if both message objects have the same functional address, + * otherwise \c false. + */ +bool Amsg_RxHandleIsIdentical(Mns_AmsRx_Msg_t *self, Msg_MostTel_t *tel_ptr) +{ + bool result; + + if ((self->source_address == tel_ptr->source_addr) + && (self->fblock_id == tel_ptr->id.fblock_id) + && (self->instance_id == tel_ptr->id.instance_id) + && (self->function_id == tel_ptr->id.function_id) + && (self->op_type == tel_ptr->id.op_type)) + { + result = true; + } + else + { + result = false; + } + + return result; +} + +/*! \brief Copies the Rx message signature from a MOST message object to an + * internal Application message object + * \param self Reference to an internal Application Message Rx handle + * \param src_ptr Reference to a MOST message object + */ +void Amsg_RxCopySignatureFromTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr) +{ + self->source_address = src_ptr->source_addr; + self->receive_type = Amsg_RxGetReceiveType(src_ptr->destination_addr); + self->fblock_id = src_ptr->id.fblock_id; + self->instance_id = src_ptr->id.instance_id; + self->function_id = src_ptr->id.function_id; + self->op_type = src_ptr->id.op_type; +} + +/*! \brief Copies the Rx message signature from an internal Application + * message object to a MOST message object + * \param self Reference to an internal Application Message Rx handle + * \param target_ptr Reference to a MOST message object + */ +void Amsg_RxCopySignatureToTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* target_ptr) +{ + target_ptr->source_addr = self->source_address; + target_ptr->destination_addr = MNS_ADDR_DEBUG; + target_ptr->id.fblock_id = self->fblock_id; + target_ptr->id.instance_id = self->instance_id; + target_ptr->id.function_id = self->function_id; + target_ptr->id.op_type = self->op_type; +} + +/*! \brief Retrieves the addressing type related to a destination_address of an Rx message + * \param destination_address The destination address of an Rx message + * \return The receive type related to the destination address + */ +static Mns_AmsRx_ReceiveType_t Amsg_RxGetReceiveType(uint16_t destination_address) +{ + Mns_AmsRx_ReceiveType_t ret = MNS_AMSRX_RCT_SINGLECAST; + + if ((destination_address == MNS_ADDR_BROADCAST_BLOCKING) || + (destination_address == MNS_ADDR_BROADCAST_UNBLOCKING)) + { + ret = MNS_AMSRX_RCT_BROADCAST; + } + else if ((destination_address >= 0x0300U) && /* 0x300..0x3FF is reserved for group cast */ + (destination_address < 0x0400U)) + { + ret = MNS_AMSRX_RCT_GROUPCAST; + } + + return ret; +} + +/*! \brief Appends payload of an Rx MOST message object to internal Application + * message object + * \param self Reference to an internal Application Message Rx handle + * \param src_ptr Reference to a MOST message object + * \return Returns \c true if the payload was appended successfully, + * otherwise \c false. + */ +bool Amsg_RxAppendPayload(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr) +{ + uint8_t cnt; + bool ret = false; + const uint16_t curr_size = SELF_RX->pb_msg.data_size; /* get current message size */ + + if ((SELF_RX->memory_sz - src_ptr->tel.tel_len) >= SELF_RX->pb_msg.data_size) /* is size sufficient */ + { + for (cnt = 0U; cnt < src_ptr->tel.tel_len; cnt++) + { + SELF_RX->pb_msg.data_ptr[curr_size + (uint16_t)cnt] = src_ptr->tel.tel_data_ptr[cnt]; + } + + SELF_RX->pb_msg.data_size = curr_size + src_ptr->tel.tel_len; /* update message size */ + SELF_RX->exp_tel_cnt++; + ret = true; + } + + return ret; +} + +/*! \brief Copies data to allocated payload buffer + * \param self The instance + * \param data Reference to external payload data + * \param data_sz Size of external payload data + */ +void Amsg_RxCopyToPayload(Mns_AmsRx_Msg_t *self, uint8_t data[], uint8_t data_sz) +{ + MISC_MEM_CPY(&self->data_ptr[0], &data[0], (size_t)data_sz); /* parasoft-suppress MISRA2004-20_3 "data_sz is limited and checked via Msg_VerifyContent()" */ + self->data_size = data_sz; /* remember payload size */ +} + +/*! \brief Checks if the message has externally allocated payload memory + * \param self The instance + * \return Returns \c true if external payload is assigned to the message, otherwise \c false. + */ +bool Amsg_RxHasExternalPayload(Mns_AmsRx_Msg_t *self) +{ + return (SELF_RX->memory_sz > 0U); +} + +/*! \brief Sets payload memory provided by memory management and updates data pointer and size + * \param self The instance + * \param mem_ptr Reference to the provided memory chunk + * \param mem_size Size of the provided memory chunk + * \param info_ptr Optional reference for memory management + */ +void Amsg_RxHandleSetMemory(Mns_AmsRx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *info_ptr) +{ + SELF_RX->memory_ptr = mem_ptr; + SELF_RX->memory_info_ptr = info_ptr; + SELF_RX->memory_sz = mem_size; + + SELF_RX->pb_msg.data_ptr = mem_ptr; + SELF_RX->pb_msg.data_size = 0U; +} + +/*! \brief Queues an Rx message at the tail of a list + * \param self The instance + * \param list_ptr Reference to the list + */ +void Amsg_RxEnqueue(Mns_AmsRx_Msg_t* self, CDlList* list_ptr) +{ + Dl_InsertTail(list_ptr, &SELF_RX->node); +} + +/*! \brief Sets or resets the garbage collector flag + * \param self The instance + * \param value New value of the flag + */ +void Amsg_RxSetGcMarker(Mns_AmsRx_Msg_t* self, bool value) +{ + SELF_RX->gc_marker = value; +} + +/*! \brief Retrieves the value of the garbage collector flag + * \param self The instance + * \return The current value of the flag + */ +bool Amsg_RxGetGcMarker(Mns_AmsRx_Msg_t* self) +{ + return SELF_RX->gc_marker; +} + +/*! \brief Retrieves the next expected telegram count + * \param self The instance + * \return The next expected telegram count as uint8_t + */ +uint8_t Amsg_RxGetExpTelCnt(Mns_AmsRx_Msg_t* self) +{ + return SELF_RX->exp_tel_cnt; +} + +/*! \brief Peeks an Rx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Rx message + */ +Mns_AmsRx_Msg_t* Amsg_RxPeek(CDlList* list_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PeekHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*! \brief Removes an Rx message from the head of a list + * \param list_ptr Reference to the list + * \return Reference to the Rx message + */ +Mns_AmsRx_Msg_t* Amsg_RxDequeue(CDlList* list_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + CDlNode *node_ptr = Dl_PopHead(list_ptr); + + if (node_ptr != NULL) + { + msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + } + + return msg_ptr; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amsmessage.h b/mnsl/mns_amsmessage.h new file mode 100644 index 0000000..b1faa65 --- /dev/null +++ b/mnsl/mns_amsmessage.h @@ -0,0 +1,217 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of Application Message Classes + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSMSG + * @{ + */ + +#ifndef MNS_AMSMESSAGE_H +#define MNS_AMSMESSAGE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams_pb.h" +#include "mns_message.h" +#include "mns_dl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ +#define AMSG_TX_OBJECT_SZ (sizeof(Amsg_IntMsgTx_t)) +#define AMSG_RX_OBJECT_SZ (sizeof(Amsg_IntMsgRx_t)) + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Internal transmission result of an application message */ +typedef enum Amsg_TxIntStatus_ +{ + AMSG_TX_INTRES_NONE = 0x00U, /*!< \brief The internal transmission is not applicable. */ + AMSG_TX_INTRES_SUCCESS = 0x01U, /*!< \brief The internal transmission succeeded. */ + AMSG_TX_INTRES_ERRBUF = 0x02U /*!< \brief The internal transmission failed. */ + +} Amsg_TxIntStatus_t; + +/*! \brief Assignable function which is invoked as soon as an application message is received + * completely and available in the Rx message queue + * \param self The instance (optional) + * \param msg_ptr Reference to the received message + */ +typedef void (*Amsg_RxCompleteCb_t)(void* self, Mns_AmsRx_Msg_t* msg_ptr); + +/*! \brief Callback function type which is fired as soon as an AMS transmission was finished + * \param self The instance (optional) + * \param msg_ptr Reference to the related message object + * \param result Transmission result + * \param info Detailed INIC transmission result + */ +typedef void (*Amsg_TxCompleteCb_t)(void* self, Mns_AmsTx_Msg_t* msg_ptr, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); + +/*! \brief Single instance API callback function type which is fired as soon as an AMS transmission was finished + * \param msg_ptr Reference to the related message object + * \param result Transmission result + * \param info Detailed INIC transmission result + */ +typedef void (*Amsg_TxCompleteSiaCb_t)(Mns_AmsTx_Msg_t* msg_ptr, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); + +/*! \brief Callback function which is invoked to free a Tx message object to the owning pool + * \param owner_ptr The owning pool of the message object + * \param msg_ptr Reference to the related message object + */ +typedef void (*Amsg_TxFreedCb_t)(void *owner_ptr, Mns_AmsTx_Msg_t* msg_ptr); + +/*! \brief Keeps callback functions to an external memory management for Rx payload */ +typedef struct Ams_MemAllocator_ +{ + void *inst_ptr; /*!< \brief The instance of the (external) memory management */ + Mns_Ams_AllocMemCb_t alloc_fptr; /*!< \brief This function is invoked to allocate Rx user payload */ + Mns_Ams_FreeMemCb_t free_fptr; /*!< \brief This function is invoked to free Rx user payload */ + +} Ams_MemAllocator_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Class */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Internal Tx message structure */ +typedef struct Amsg_IntMsgTx_ +{ + Mns_AmsTx_Msg_t pb_msg; /*!< \brief Public message struct must be the first member */ + void *info_ptr; /*!< \brief Custom object information required by memory management */ + + void *free_inst_ptr; /*!< \brief Reference which is passed to free_ptr */ + Amsg_TxFreedCb_t free_fptr; /*!< \brief Callback function which is called to free the object */ + + uint8_t *memory_ptr; /*!< \brief Reference to payload provided by memory management */ + void *memory_info_ptr; /*!< \brief Custom payload information required by memory management */ + uint16_t memory_sz; /*!< \brief Size of the payload that is provided by memory management */ + + uint16_t next_segment_cnt; /*!< \brief Specifies the next segment count. '0xFF' means size prefixed */ + uint8_t follower_id; /*!< \brief Identifier of segmented messages and corresponding telegrams */ + Mns_MsgTxStatus_t temp_result; /*!< \brief Stores the temporary result that is notified when then transmission + * has completed + */ + uint16_t backup_dest_address; /*!< \brief Backup of replaced target address. */ + Amsg_TxIntStatus_t internal_status; /*!< \brief Stores the internal transmission status */ + bool ignore_wrong_target; /*!< \brief Forces the message to report transmission result "success", although + * the INIC has reported transmission error "wrong target" + */ + CDlNode node; /*!< \brief Node required for message pool */ + + Amsg_TxCompleteSiaCb_t complete_sia_fptr; /*!< \brief Single instance API Callback function which is invoked + * after transmission completed + */ + Amsg_TxCompleteCb_t complete_fptr; /*!< \brief Callback function which is invoked after transmission + * completed + */ + void *complete_inst_ptr; /*!< \brief Instance pointer which is required to invoke complete_fptr */ + +} Amsg_IntMsgTx_t; + +/*! \brief Internal Rx message structure */ +typedef struct Amsg_IntMsgRx_ +{ + Mns_AmsRx_Msg_t pb_msg; /*!< \brief Public message structure must be the first member */ + void *info_ptr; /*!< \brief Custom object information required by memory management */ + + uint8_t *memory_ptr; /*!< \brief Reference to payload provided by memory management */ + void *memory_info_ptr; /*!< \brief Custom payload information required by memory management */ + uint16_t memory_sz; /*!< \brief The size of the allocated user payload in bytes */ + + CDlNode node; /*!< \brief Node required for message pool */ + + uint8_t exp_tel_cnt; /*!< \brief The expected TelCnt used for segmented transfer */ + bool gc_marker; /*!< \brief Identifies message objects that were already + * marked by the garbage collector. + */ +} Amsg_IntMsgRx_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Class methods */ +/*------------------------------------------------------------------------------------------------*/ +/* Tx */ +extern void Amsg_TxCtor(Mns_AmsTx_Msg_t *self, void *info_ptr, Amsg_TxFreedCb_t free_fptr, void *free_inst_ptr); +extern void Amsg_TxSetInternalPayload(Mns_AmsTx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *mem_info_ptr); +extern void Amsg_TxReuse(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxSetCompleteCallback(Mns_AmsTx_Msg_t *self, Amsg_TxCompleteSiaCb_t compl_sia_fptr, + Amsg_TxCompleteCb_t compl_fptr, void* compl_inst_ptr); +extern void Amsg_TxNotifyComplete(Mns_AmsTx_Msg_t *self, Mns_AmsTx_Result_t result, Mns_AmsTx_Info_t info); +extern void Amsg_TxFreeUnused(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxUpdateInternalResult(Mns_AmsTx_Msg_t *self, Amsg_TxIntStatus_t result); +extern void Amsg_TxUpdateResult(Mns_AmsTx_Msg_t *self, Mns_MsgTxStatus_t result); +extern Mns_AmsTx_Result_t Amsg_TxGetResultCode(Mns_AmsTx_Msg_t *self); +extern Mns_AmsTx_Info_t Amsg_TxGetResultInfo(Mns_AmsTx_Msg_t *self); +extern uint16_t Amsg_TxGetNextSegmCnt(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxIncrementNextSegmCnt(Mns_AmsTx_Msg_t *self); +extern uint8_t Amsg_TxGetFollowerId(Mns_AmsTx_Msg_t *self); +extern void Amsg_TxSetFollowerId(Mns_AmsTx_Msg_t *self, uint8_t id); +extern void Amsg_TxReplaceDestinationAddr(Mns_AmsTx_Msg_t *self, uint16_t new_destination); +extern void Amsg_TxRemoveFromQueue(Mns_AmsTx_Msg_t *self, CDlList *list_ptr); +extern void Amsg_TxEnqueue(Mns_AmsTx_Msg_t* self, CDlList* list_ptr); +extern Mns_AmsTx_Msg_t* Amsg_TxPeek(CDlList* list_ptr); +extern Mns_AmsTx_Msg_t* Amsg_TxDequeue(CDlList* list_ptr); + +/* Rx */ +extern void Amsg_RxCtor(Mns_AmsRx_Msg_t *self, void *info_ptr); +extern void Amsg_RxBuildFromTx(Mns_AmsRx_Msg_t *self, Mns_AmsTx_Msg_t *tx_ptr, uint16_t source_address); +extern void Amsg_RxHandleSetup(Mns_AmsRx_Msg_t *self); +extern void Amsg_RxHandleSetMemory(Mns_AmsRx_Msg_t *self, uint8_t *mem_ptr, uint16_t mem_size, void *info_ptr); +extern bool Amsg_RxHandleIsIdentical(Mns_AmsRx_Msg_t *self, Msg_MostTel_t *tel_ptr); +extern void Amsg_RxCopySignatureFromTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr); +extern void Amsg_RxCopySignatureToTel(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* target_ptr); +extern void Amsg_RxCopyToPayload(Mns_AmsRx_Msg_t *self, uint8_t data[], uint8_t data_sz); +extern bool Amsg_RxAppendPayload(Mns_AmsRx_Msg_t *self, Msg_MostTel_t* src_ptr); +extern bool Amsg_RxHasExternalPayload(Mns_AmsRx_Msg_t *self); +extern void Amsg_RxEnqueue(Mns_AmsRx_Msg_t* self, CDlList* list_ptr); +extern void Amsg_RxSetGcMarker(Mns_AmsRx_Msg_t* self, bool value); +extern bool Amsg_RxGetGcMarker(Mns_AmsRx_Msg_t* self); +extern uint8_t Amsg_RxGetExpTelCnt(Mns_AmsRx_Msg_t* self); +/* Rx helpers */ +extern Mns_AmsRx_Msg_t* Amsg_RxPeek(CDlList* list_ptr); +extern Mns_AmsRx_Msg_t* Amsg_RxDequeue(CDlList* list_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_AMSMESSAGE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amspool.c b/mnsl/mns_amspool.c new file mode 100644 index 0000000..3ce89ae --- /dev/null +++ b/mnsl/mns_amspool.c @@ -0,0 +1,336 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of Application Message Pool + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSPOOL + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_amspool.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal macros */ +/*------------------------------------------------------------------------------------------------*/ +#define INT_RX(ptr) ((Amsg_IntMsgRx_t*)(void*)(ptr)) /* parasoft-suppress MISRA2004-19_7 "common definition of type cast improves code" */ +#define INT_TX(ptr) ((Amsg_IntMsgTx_t*)(void*)(ptr)) /* parasoft-suppress MISRA2004-19_7 "common definition of type cast improves code" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Amsp_FreeTxObj(void *self, Mns_AmsTx_Msg_t* msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of application message pool class + * \param self The instance + * \param mem_allocator_ptr Reference to memory allocator + * \param mns_inst_id MOST NetServices instance ID + */ +void Amsp_Ctor(CAmsMsgPool *self, Ams_MemAllocator_t *mem_allocator_ptr, uint8_t mns_inst_id) +{ + self->mns_inst_id = mns_inst_id; + self->allocator_ptr = mem_allocator_ptr; + self->rx_rsvd_msg_ptr = Amsp_AllocRxObj(self, 45U); + self->rx_rsvd_msg_ref = self->rx_rsvd_msg_ptr; + self->terminated = false; + self->tx_notify_freed = false; + self->rx_notify_freed = false; + Sub_Ctor(&self->tx_freed_subject, self->mns_inst_id); + Sub_Ctor(&self->rx_freed_subject, self->mns_inst_id); + + TR_ASSERT(self->mns_inst_id, "[AMSP]", (self->rx_rsvd_msg_ptr != NULL)); +} + +/*! \brief Frees pre-allocated message memory + * \param self The instance + */ +void Amsp_Cleanup(CAmsMsgPool *self) +{ + Amsg_IntMsgRx_t *msg_ptr = INT_RX(self->rx_rsvd_msg_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Amsp_Cleanup: rx_rsvd_msg_ptr=0x%p", 1U, self->rx_rsvd_msg_ptr)); + + self->terminated = true; + self->tx_notify_freed = false; + self->rx_notify_freed = false; + + if (msg_ptr != NULL) + { + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr->memory_ptr, MNS_AMS_RX_PAYLOAD, msg_ptr->memory_info_ptr); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_RX_OBJECT, msg_ptr->info_ptr); + self->rx_rsvd_msg_ref = NULL; + self->rx_rsvd_msg_ptr = NULL; + } +} + +/*! \brief Assigns an observer which is invoked as soon as memory dedicated to a Tx message is + * freed.The data_ptr of the update callback function is not used (always \c NULL). + * See \ref Obs_UpdateCb_t. + * \param self The instance + * \param observer_ptr The observer + */ +void Amsp_AssignTxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr) +{ + (void)Sub_AddObserver(&self->tx_freed_subject, observer_ptr); +} + +/*! \brief Assigns an observer which is invoked as soon as memory dedicated to a Tx message is + * freed.The data_ptr of the update callback function is not used (always \c NULL). + * See \ref Obs_UpdateCb_t. + * \param self The instance + * \param observer_ptr The observer + */ +void Amsp_AssignRxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr) +{ + (void)Sub_AddObserver(&self->rx_freed_subject, observer_ptr); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx allocations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an internal Tx message object (without payload) + * \param self The instance + * \param payload_sz The required payload size in bytes + * \return Reference to the Tx message object if the allocation succeeds. Otherwise \c NULL. + */ +Mns_AmsTx_Msg_t* Amsp_AllocTxObj(CAmsMsgPool *self, uint16_t payload_sz) +{ + void *payload_info_ptr = NULL; + void *payload_ptr = NULL; + void *obj_info_ptr = NULL; + Mns_AmsTx_Msg_t *msg_ptr = (Mns_AmsTx_Msg_t*)self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, AMSG_TX_OBJECT_SZ, MNS_AMS_TX_OBJECT, &obj_info_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating TxObject: msg_ptr=0x%p, size=%d, info_ptr=0x%p", 3U, msg_ptr, AMSG_TX_OBJECT_SZ, obj_info_ptr)); + + if (msg_ptr != NULL) + { + if (payload_sz > 0U) + { + payload_ptr = self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, payload_sz, MNS_AMS_TX_PAYLOAD, &payload_info_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating TxPayload: msg_ptr=0x%p, mem_ptr=0x%p, size=%d, info_ptr=0x%p", 4U, msg_ptr, payload_ptr, payload_sz, payload_info_ptr)); + + if (payload_ptr == NULL) + { + TR_INFO((self->mns_inst_id, "[AMSP]", "Freeing TxObject: msg_ptr=0x%p, info_ptr=0x%p", 2U, msg_ptr, obj_info_ptr)); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_TX_OBJECT, obj_info_ptr); + msg_ptr = NULL; + } + } + } + + if (msg_ptr != NULL) + { + Amsg_TxCtor(msg_ptr, obj_info_ptr, &Amsp_FreeTxObj, self); + + if (payload_ptr != NULL) + { + Amsg_TxSetInternalPayload(msg_ptr, (uint8_t*)payload_ptr, payload_sz, payload_info_ptr); + } + } + else + { + self->tx_notify_freed = true; + } + + return msg_ptr; +} + +/*! \brief Frees an internal Tx message object including its payload + * \param self The instance + * \param msg_ptr Reference to the internal Tx message object + */ +static void Amsp_FreeTxObj(void *self, Mns_AmsTx_Msg_t* msg_ptr) +{ + CAmsMsgPool *self_ = (CAmsMsgPool*)self; + Amsg_IntMsgTx_t *obj_ptr = INT_TX(msg_ptr); + + if (obj_ptr->memory_ptr != NULL) + { + TR_INFO((self_->mns_inst_id, "[AMSP]", "Freeing TxPayload: msg_ptr=0x%p, mem_ptr=0x%p, info_ptr=0x%p", 3U, msg_ptr, obj_ptr->memory_ptr, obj_ptr->memory_info_ptr)); + self_->allocator_ptr->free_fptr(self_->allocator_ptr->inst_ptr, obj_ptr->memory_ptr, MNS_AMS_TX_PAYLOAD, obj_ptr->memory_info_ptr); + Amsg_TxSetInternalPayload(msg_ptr, NULL, 0U, NULL); + } + + TR_INFO((self_->mns_inst_id, "[AMSP]", "Freeing TxObject: msg_ptr=0x%p, info_ptr=0x%p", 2U, msg_ptr, obj_ptr->info_ptr)); + self_->allocator_ptr->free_fptr(self_->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_TX_OBJECT, obj_ptr->info_ptr); + + if (self_->tx_notify_freed) + { + Sub_Notify(&self_->tx_freed_subject, NULL); + self_->tx_notify_freed = false; + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx allocations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an internal Rx message object (optionally with payload) + * \param self The instance + * \param payload_sz The required payload size that shall be allocated and assigned to the object. + * Value "0" means that no payload memory shall be allocated in the same turn. + * \return Reference to the Rx message object if the allocation succeeds. Otherwise \c NULL. + */ +Mns_AmsRx_Msg_t* Amsp_AllocRxObj(CAmsMsgPool *self, uint16_t payload_sz) +{ + void *info_ptr = NULL; + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, AMSG_RX_OBJECT_SZ, MNS_AMS_RX_OBJECT, &info_ptr); + + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating RxObject: msg_ptr=0x%p, size=%d, info_ptr=0x%p", 3U, msg_ptr, AMSG_RX_OBJECT_SZ, info_ptr)); + + if (msg_ptr != NULL) + { + Amsg_RxCtor(msg_ptr, info_ptr); + Amsg_RxHandleSetup(msg_ptr); + + if (payload_sz != 0U) + { + if (!Amsp_AllocRxPayload(self, payload_sz, msg_ptr)) + { + Amsp_FreeRxObj(self, msg_ptr); /* payload allocation has failed - release message object */ + msg_ptr = NULL; + } + } + } + + return msg_ptr; +} + +/*! \brief Allocates a reserved Rx message object with payload up to 45 bytes payload + * \param self The instance + * \return Reference to the Rx message object if the allocation succeeds. Otherwise \c NULL. + */ +Mns_AmsRx_Msg_t* Amsp_AllocRxRsvd(CAmsMsgPool *self) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + + if (self->rx_rsvd_msg_ptr != NULL) + { + msg_ptr = self->rx_rsvd_msg_ptr; + self->rx_rsvd_msg_ptr = NULL; + Amsg_RxHandleSetup(msg_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Retrieving reserved RxObject: msg_ptr=0x%p", 1U, msg_ptr)); + } + else + { + self->rx_notify_freed = true; + } + + return msg_ptr; +} + +/*! \brief Allocates payload for an internal Rx message object + * \param self The instance + * \param payload_sz Payload size in bytes + * \param msg_ptr Reference to the internal Rx message object + * \return Returns \c true if the allocation succeeds. Otherwise \c NULL. + */ +bool Amsp_AllocRxPayload(CAmsMsgPool *self, uint16_t payload_sz, Mns_AmsRx_Msg_t* msg_ptr) +{ + bool success = false; + void *info_ptr = NULL; + void *mem_ptr = self->allocator_ptr->alloc_fptr(self->allocator_ptr->inst_ptr, payload_sz, MNS_AMS_RX_PAYLOAD, &info_ptr); + + TR_INFO((self->mns_inst_id, "[AMSP]", "Allocating RxPayload: msg_ptr=0x%p, mem_ptr=0x%p, size=%d, info_ptr=0x%p", 4U, msg_ptr, mem_ptr, payload_sz, info_ptr)); + TR_ASSERT(self->mns_inst_id, "[AMSP]", (msg_ptr != NULL)); /* message reference is required */ + TR_ASSERT(self->mns_inst_id, "[AMSP]", (msg_ptr != self->rx_rsvd_msg_ref)); /* forbidden overwrite of pre-allocated message payload */ + + if (mem_ptr != NULL) + { + Amsg_RxHandleSetMemory(msg_ptr, (uint8_t*)mem_ptr, payload_sz, info_ptr); + success = true; + } + + return success; +} + +/*! \brief Frees an internal Rx message object + * \param self The instance + * \param msg_ptr Reference to the internal Rx message object + * \details Payload that is assigned to the message object has to be freed + * separately by using Amsp_FreeRxPayload(). + */ +void Amsp_FreeRxObj(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr) +{ + if (msg_ptr == self->rx_rsvd_msg_ref) + { + TR_ASSERT(self->mns_inst_id, "[AMSP]", (self->rx_rsvd_msg_ptr == NULL)); /* before freeing, message shall be reserved */ + TR_INFO((self->mns_inst_id, "[AMSP]", "Restoring reserved RxObject: msg_ptr=0x%p", 1U, msg_ptr)); + self->rx_rsvd_msg_ptr = self->rx_rsvd_msg_ref; /* restore reserved message */ + + if (self->terminated != false) + { /* also free reserved message if it is freed */ + Amsp_Cleanup(self); /* from any queue after Amsp_Cleanup() */ + } + } + else + { + Amsg_IntMsgRx_t *obj_ptr = INT_RX(msg_ptr); + TR_INFO((self->mns_inst_id, "[AMSP]", "Freeing RxObject: msg_ptr=0x%p, info_ptr=0x%p", 2U, msg_ptr, obj_ptr->info_ptr)); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, msg_ptr, MNS_AMS_RX_OBJECT, obj_ptr->info_ptr); + } + + if (self->rx_notify_freed) + { + Sub_Notify(&self->rx_freed_subject, NULL); + self->rx_notify_freed = false; + } +} + +/*! \brief Frees payload that is associated with an internal Rx message object + * \param self The instance + * \param msg_ptr Reference to the internal Rx message object + */ +void Amsp_FreeRxPayload(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr) +{ + Amsg_IntMsgRx_t *obj_ptr = INT_RX(msg_ptr); + + if (msg_ptr == self->rx_rsvd_msg_ref) + { + TR_ASSERT(self->mns_inst_id, "[AMSP]", (self->rx_rsvd_msg_ptr == NULL)); /* release payload before object */ + TR_INFO((self->mns_inst_id, "[AMSP]", "Restoring reserved RxPayload: msg_ptr=0x%p", 1U, msg_ptr)); + } + else if (obj_ptr->memory_ptr != NULL) + { + TR_INFO((self->mns_inst_id, "[AMSP]", "Freeing RxPayload: msg_ptr=0x%p, mem_ptr=0x%p, info_ptr=0x%p", 3U, msg_ptr, obj_ptr->memory_ptr, obj_ptr->memory_info_ptr)); + self->allocator_ptr->free_fptr(self->allocator_ptr->inst_ptr, obj_ptr->memory_ptr, MNS_AMS_RX_PAYLOAD, obj_ptr->memory_info_ptr); + Amsg_RxHandleSetMemory(msg_ptr, NULL, 0U, NULL); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_amspool.h b/mnsl/mns_amspool.h new file mode 100644 index 0000000..387dc9b --- /dev/null +++ b/mnsl/mns_amspool.h @@ -0,0 +1,100 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of Application Message Pools + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSPOOL + * @{ + */ + +#ifndef MNS_AMSPOOL_H +#define MNS_AMSPOOL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_ams_pb.h" +#include "mns_amsmessage.h" +#include "mns_obs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Classes */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class members of AMS Pool */ +typedef struct CAmsMsgPool_ +{ + Ams_MemAllocator_t *allocator_ptr; /*!< \brief Interface to memory allocator */ + Mns_AmsRx_Msg_t *rx_rsvd_msg_ptr; /*!< \brief Pre-allocated Rx message or NULL if no + * reserved message is available */ + Mns_AmsRx_Msg_t *rx_rsvd_msg_ref; /*!< \brief Stores the reference of the reserved message + * to identify it and restore the + * \c rx_rsvd_msg_ptr. */ + CSubject tx_freed_subject; /*!< \brief Allows to observe freed Tx message event */ + CSubject rx_freed_subject; /*!< \brief Allows to observe freed Rx message event */ + bool tx_notify_freed; /*!< \brief Is \c true when to notify the next Tx freed object */ + bool rx_notify_freed; /*!< \brief Is \c true when to notify the next Rx freed object */ + bool terminated; /*!< \brief Is \c true if a cleanup was done. Helps to release the + * pre-allocated message after the first cleanup attempt. */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CAmsMsgPool; + +/*------------------------------------------------------------------------------------------------*/ +/* Class methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Amsp_Ctor(CAmsMsgPool *self, Ams_MemAllocator_t *mem_allocator_ptr, uint8_t mns_inst_id); +extern void Amsp_Cleanup(CAmsMsgPool *self); +/* Tx */ +extern void Amsp_AssignTxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr); +extern Mns_AmsTx_Msg_t* Amsp_AllocTxObj(CAmsMsgPool *self, uint16_t payload_sz); +/* Rx */ +extern void Amsp_AssignRxFreedObs(CAmsMsgPool *self, CObserver *observer_ptr); +extern Mns_AmsRx_Msg_t* Amsp_AllocRxObj(CAmsMsgPool *self, uint16_t payload_sz); +extern Mns_AmsRx_Msg_t* Amsp_AllocRxRsvd(CAmsMsgPool *self); +extern bool Amsp_AllocRxPayload(CAmsMsgPool *self, uint16_t payload_sz, Mns_AmsRx_Msg_t* msg_ptr); +extern void Amsp_FreeRxObj(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr); +extern void Amsp_FreeRxPayload(CAmsMsgPool *self, Mns_AmsRx_Msg_t* msg_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_AMSPOOL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_base.c b/mnsl/mns_base.c new file mode 100644 index 0000000..542e485 --- /dev/null +++ b/mnsl/mns_base.c @@ -0,0 +1,70 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the Base class. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_BASE + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_base.h" +#include "mns_misc.h" +#include "mns_message.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CBase */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the Base class. + * \param self Instance pointer + * \param init_ptr Reference to the initialization data + */ +void Base_Ctor(CBase *self, Base_InitData_t *init_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + /* Save MOST NetServices instance ID */ + self->mns_inst_id = init_ptr->mns_inst_id; + /* Create the scheduler instance */ + Scd_Ctor(&self->scd, &init_ptr->scd); + /* Create the timer management instance */ + Tm_Ctor(&self->tm, &self->scd, &init_ptr->tm); + /* Create the event handler instance */ + Eh_Ctor(&self->eh, self->mns_inst_id); + /* Create the API locking manager instance */ + Alm_Ctor(&self->alm, &self->tm, &self->eh, self->mns_inst_id); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_base.h b/mnsl/mns_base.h new file mode 100644 index 0000000..132243d --- /dev/null +++ b/mnsl/mns_base.h @@ -0,0 +1,93 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the Base class. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_BASE + * @{ + */ + +#ifndef MNS_BASE_H +#define MNS_BASE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_timer.h" +#include "mns_scheduler.h" +#include "mns_trace.h" +#include "mns_eh.h" +#include "mns_alm.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of the Base class. */ +typedef struct Base_InitData_ +{ + Scd_InitData_t scd; /*!< \brief Initialization data of the scheduler */ + Tm_InitData_t tm; /*!< \brief Initialization data of the timer management */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} Base_InitData_t; + +/*! \brief Class structure of the Base class. */ +typedef struct CBase_ +{ + CScheduler scd; /*!< \brief Scheduler instance */ + CTimerManagement tm; /*!< \brief Timer management instance */ + CEventHandler eh; /*!< \brief Event handler instance */ + CApiLockingManager alm; /*!< \brief API locking manager instance */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CBase; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +void Base_Ctor(CBase *self, Base_InitData_t *init_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_BASE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_cfg.h b/mnsl/mns_cfg.h new file mode 100644 index 0000000..dcf6e2b --- /dev/null +++ b/mnsl/mns_cfg.h @@ -0,0 +1,67 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Configuration header file of MOST NetServices Light + */ + +#ifndef MNSL_CFG_H +#define MNSL_CFG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* File only needed for other includes. */ +#include "mns_types_cfg.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Message Pool */ +/*------------------------------------------------------------------------------------------------*/ +/* Sets the number of pre-allocated Rx messages which are shared by all FIFOs. Default value: 35*/ +/* #define MNSL_CHANNEL_POOL_SIZE_RX 35 */ + +/*------------------------------------------------------------------------------------------------*/ +/* Tracing & Debugging */ +/*------------------------------------------------------------------------------------------------*/ +/* Define the following macros to map info and error trace output to user defined functions. + * The purpose of these functions is debugging. It is not recommended to define these functions + * in a production system. + */ +#define MNS_TR_ERROR My_TraceError +// #define MNS_TR_INFO My_TraceInfo + +extern void My_TraceError(uint8_t mns_inst_id, const char module_str[], const char entry_str[], uint16_t vargs_cnt, ...); +extern void My_TraceInfo(uint8_t mns_inst_id, const char module_str[], const char entry_str[], uint16_t vargs_cnt, ...); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNSL_CFG_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ diff --git a/mnsl/mns_dl.c b/mnsl/mns_dl.c new file mode 100644 index 0000000..7a7a322 --- /dev/null +++ b/mnsl/mns_dl.c @@ -0,0 +1,392 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the doubly linked list. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_DL + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_dl.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CDlList */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the doubly linked list class. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Dl_Ctor(CDlList *self, uint8_t mns_inst_id) +{ + self->head = NULL; + self->tail = NULL; + self->size = 0U; + self->mns_inst_id = mns_inst_id; +} + +/*! \brief Inserts a new node after an arbitrary node. + * \param self Instance pointer + * \param node Reference of the initial node + * \param new_node Reference of the new node are to be inserted + */ +void Dl_InsertAfter(CDlList *self, CDlNode *node, CDlNode *new_node) +{ + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size <= 0xFFFFU)); + new_node->prev = node; + new_node->next = node->next; + if(node->next == NULL) /* Is initial node last node in list? */ + { + self->tail = new_node; /* Set new node as tail of list */ + } + else + { + node->next->prev = new_node; /* Adjust follower node */ + } + node->next = new_node; /* Adjust parent node */ + new_node->in_use = true; /* Signals that node is part of a list */ + self->size++; /* Increment number of nodes */ +} + +/*! \brief Inserts a new node before an arbitrary node. + * \param self Instance pointer + * \param node Reference of the initial node + * \param new_node Reference of the new node are to be inserted + */ +void Dl_InsertBefore(CDlList *self, CDlNode *node, CDlNode *new_node) +{ + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size <= 0xFFFFU)); + new_node->prev = node->prev; + new_node->next = node; + if(node->prev == NULL) /* Is initial node first node in list? */ + { + self->head = new_node; /* Set new node as head of list */ + } + else + { + node->prev->next = new_node; /* Adjust parent node */ + } + node->prev = new_node; /* Adjust follower node */ + new_node->in_use = true; /* Signals that node is part of a list */ + self->size++; /* Increment number of nodes */ +} + +/*! \brief Sets the new node as head of a doubly linked list. + * \param self Instance pointer + * \param new_node Reference of the new node are to be placed as head of the list + */ +void Dl_InsertHead(CDlList *self, CDlNode *new_node) +{ + if(self->head == NULL) /* Is list empty? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size <= 0xFFFFU)); + self->head = new_node; + self->tail = new_node; + new_node->prev = NULL; + new_node->next = NULL; + new_node->in_use = true; /* Signals that node is part of a list */ + self->size++; /* Increment number of nodes */ + } + else + { + Dl_InsertBefore(self, self->head, new_node); + } +} + +/*! \brief Inserts the new node at the end of a doubly linked list. + * \param self Instance pointer + * \param new_node Reference of the new node are to be placed at the end of the list + */ +void Dl_InsertTail(CDlList *self, CDlNode *new_node) +{ + if(self->tail == NULL) /* Is list empty? */ + { + Dl_InsertHead(self, new_node); + } + else + { + Dl_InsertAfter(self, self->tail, new_node); + } +} + +/*! \brief Removes an arbitrary node from a doubly linked list. + * \param self Instance pointer + * \param node Reference of the node are to be removed from the list + * \return \c DL_OK: No error + * \return \c DL_UNKNOWN_NODE: Given node is not part of this list + */ +Dl_Ret_t Dl_Remove(CDlList *self, CDlNode *node) +{ + Dl_Ret_t ret_val = DL_UNKNOWN_NODE; + + if(Dl_IsNodeInList(self, node) != false) /* Is node part of list? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size > 0U)); + if(node->prev == NULL) /* First node in list? */ + { + self->head = node->next; /* Replace head node with next node in list */ + } + else /* -> Not first node in list */ + { + node->prev->next = node->next; /* Set next pointer of previous node to next node */ + } + if(node->next == NULL) /* Last node in list? */ + { + self->tail = node->prev; /* Replace tail node with previous node in list */ + } + else /* -> Not last node in list */ + { + node->next->prev = node->prev; /* Set previous ptr of next node to previous node */ + } + node->prev = NULL; + node->next = NULL; + node->in_use = false; /* Signals that node is not part of a list */ + ret_val = DL_OK; + self->size--; /* Decrement number of nodes */ + } + + return ret_val; +} + +/*! \brief Removes the first node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the removed head node or \c NULL if the list is empty. + */ +CDlNode * Dl_PopHead(CDlList *self) +{ + CDlNode *node = self->head; + + if(NULL != node) /* Is list not empty? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size > 0U)); + self->head = node->next; /* Replace head node with next node in list */ + if(node->next == NULL) /* Last node in list? */ + { + self->tail = NULL; /* Replace tail node and set list's tail pointer + * to NULL + */ + } + else /* -> Not last node in list */ + { + node->next->prev = NULL; /* Set previous pointer of next node to NULL */ + } + node->prev = NULL; + node->next = NULL; + node->in_use = false; /* Signals that node is not part of a list */ + self->size--; /* Decrement number of nodes */ + } + + return node; +} + +/*! \brief Removes the last node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the removed tail node or \c NULL if the list is empty. + */ +CDlNode * Dl_PopTail(CDlList *self) +{ + CDlNode *node = self->tail; + + if(NULL != node) /* Is list not empty? */ + { + TR_ASSERT(self->mns_inst_id, "[DL]", (self->size > 0U)); + if(node->prev == NULL) /* First node in list? */ + { + self->head = NULL; /* Replace head node and set list's head pointer + * to NULL + */ + } + else /* -> Not first node in list */ + { + node->prev->next = NULL; /* Set next pointer of previous node to NULL */ + } + self->tail = node->prev; /* Replace tail node with previous node in list */ + node->prev = NULL; + node->next = NULL; + node->in_use = false; /* Signals that node is not part of a list */ + self->size--; /* Decrement number of nodes */ + } + + return node; +} + +/*! \brief Returns the reference of the first node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the head node or \c NULL if the list is empty. + */ +CDlNode * Dl_PeekHead(CDlList *self) +{ + return self->head; +} + +/*! \brief Returns the reference of the last node in a doubly linked list. + * \param self Instance pointer + * \return The reference of the tail node or NULL if the list is empty. + */ +CDlNode * Dl_PeekTail(CDlList *self) +{ + return self->tail; +} + +/*! \brief Calls the given function for each node in the doubly linked list. If the func_ptr + * returns true the loop is stopped and the current node will be returned. + * \param self Instance pointer + * \param func_ptr Reference of the callback function which is called for each node + * \param user_data_ptr Reference of optional user data given to func_ptr + * \return Returns the current node or \c NULL if the whole list is processed. + */ +CDlNode * Dl_Foreach(CDlList *self, Dl_ForeachFunc_t func_ptr, void *user_data_ptr) +{ + CDlNode *ret_val = NULL; + CDlNode *node = self->head; + + while(NULL != node) /* End of list reached? */ + { + if(func_ptr(node->data_ptr, user_data_ptr) != false) /* Data found? */ + { + ret_val = node; + break; + } + node = node->next; + } + return ret_val; +} + +/*! \brief Checks if a node is part of the given doubly linked list. + * \param self Instance pointer + * \param node Reference of the searched node + * \return \c true: Node is part of the given list + * \return \c false: Node is not part of the given list + */ +bool Dl_IsNodeInList(CDlList *self, const CDlNode *node) +{ + bool ret_val = false; + CDlNode *current_node = self->head; + + while(NULL != current_node) /* End of list reached? */ + { + if(current_node == node) /* Is current node the searched one */ + { + ret_val = true; + break; + } + current_node = current_node->next; + } + return ret_val; +} + +/*! \brief Appends one doubly linked list to another doubly linked list. + * \param self Instance pointer + * \param list_ptr Reference to the doubly linked list + */ +void Dl_AppendList(CDlList *self, CDlList *list_ptr) +{ + TR_ASSERT(self->mns_inst_id, "[DL]", (list_ptr != NULL)); + if(list_ptr->head != NULL) + { + if(self->tail == NULL) /* Is list empty? */ + { + self->head = list_ptr->head; + self->tail = list_ptr->tail; + self->size = list_ptr->size; + } + else + { + list_ptr->head->prev = self->tail; + self->tail->next = list_ptr->head; + self->tail = list_ptr->tail; + self->size += list_ptr->size; + } + list_ptr->head = NULL; + list_ptr->tail = NULL; + list_ptr->size = 0U; + } +} + +/*! \brief Interface function to retrieve the list size. + * \param self Instance pointer + * \return Size of the list + */ +uint16_t Dl_GetSize(CDlList *self) +{ + return self->size; +} + + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CDlNode */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of doubly linked list nodes. + * \param self Instance pointer + * \param data_ptr Optional reference to data + */ +void Dln_Ctor(CDlNode *self, void *data_ptr) +{ + self->next = NULL; + self->prev = NULL; + self->in_use = false; + self->data_ptr = data_ptr; +} + +/*! \brief Interface function to set the data pointer of the given node. + * \param self Instance pointer + * \param data_ptr Reference of the new data + */ +void Dln_SetData(CDlNode *self, void *data_ptr) +{ + self->data_ptr = data_ptr; +} + +/*! \brief Interface function to request the data pointer of the given node. + * \param self Instance pointer + */ +void * Dln_GetData(CDlNode *self) +{ + return self->data_ptr; +} + +/*! \brief Checks if a node is part of a doubly linked list. + * \param self Instance pointer of the searched node + * \return \c true: Node is part of a list + * \return \c false: Node is not part of a list + */ +bool Dln_IsNodePartOfAList(CDlNode *self) +{ + return self->in_use; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_dl.h b/mnsl/mns_dl.h new file mode 100644 index 0000000..96e89e3 --- /dev/null +++ b/mnsl/mns_dl.h @@ -0,0 +1,132 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the doubly linked list. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_DL + * @{ + */ + +#ifndef MNS_DL_H +#define MNS_DL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback signature used by foreach-function + * \param d_ptr Reference to the data of the current node + * \param up_ptr Reference to the user data + * \return true: Stop the for-each-loop + * \return false: Continue the for-each-loop + */ +typedef bool(*Dl_ForeachFunc_t)(void *d_ptr, void *ud_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerators */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return values of the list module. */ +typedef enum Dl_Ret_ +{ + DL_OK, /*!< \brief No error */ + DL_UNKNOWN_NODE, /*!< \brief Unknown node */ + DL_STOPPED /*!< \brief Search process stopped */ + +} Dl_Ret_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of doubly linked list node. */ +typedef struct DlNode_ +{ + struct DlNode_ *prev; /*!< \brief Reference to previous node in list */ + struct DlNode_ *next; /*!< \brief Reference to next node in list */ + void *data_ptr; /*!< \brief Reference to optional data */ + bool in_use; /*!< \brief Flag which signals that the node is in use */ + +} CDlNode; + +/*! \brief Class structure of the doubly linked list. */ +typedef struct CDlList_ +{ + struct DlNode_ *head; /*!< \brief Reference to head of the list */ + struct DlNode_ *tail; /*!< \brief Reference to tail of the list */ + uint16_t size; /*!< \brief Number of nodes in the list */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CDlList; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CDlList */ +/*------------------------------------------------------------------------------------------------*/ +extern void Dl_Ctor(CDlList *self, uint8_t mns_inst_id); +extern void Dl_InsertAfter(CDlList *self, CDlNode *node, CDlNode *new_node); +extern void Dl_InsertBefore(CDlList *self, CDlNode *node, CDlNode *new_node); +extern void Dl_InsertHead(CDlList *self, CDlNode *new_node); +extern void Dl_InsertTail(CDlList *self, CDlNode *new_node); +extern Dl_Ret_t Dl_Remove(CDlList *self, CDlNode *node); +extern CDlNode * Dl_PopHead(CDlList *self); +extern CDlNode * Dl_PopTail(CDlList *self); +extern CDlNode * Dl_PeekHead(CDlList *self); +extern CDlNode * Dl_PeekTail(CDlList *self); +extern CDlNode * Dl_Foreach(CDlList *self, Dl_ForeachFunc_t func_ptr, void *user_data_ptr); +extern bool Dl_IsNodeInList(CDlList *self, const CDlNode *node); +extern void Dl_AppendList(CDlList *self, CDlList *list_ptr); +extern uint16_t Dl_GetSize(CDlList *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CDlNode */ +/*------------------------------------------------------------------------------------------------*/ +extern void Dln_Ctor(CDlNode *self, void *data_ptr); +extern void Dln_SetData(CDlNode *self, void *data_ptr); +extern void * Dln_GetData(CDlNode *self); +extern bool Dln_IsNodePartOfAList(CDlNode *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_DL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_eh.c b/mnsl/mns_eh.c new file mode 100644 index 0000000..36e0168 --- /dev/null +++ b/mnsl/mns_eh.c @@ -0,0 +1,155 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the event handler. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_EH + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_eh.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static bool Eh_EncodeEvent(uint32_t event_code, Mns_Error_t *public_error_code_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CEventHandler */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the event handler class. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Eh_Ctor(CEventHandler *self, uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + /* Save MOST NetServices instance ID */ + self->mns_inst_id = mns_inst_id; + /* Initialize subject for internal events */ + Sub_Ctor(&self->internal_event_subject, self->mns_inst_id); + /* Initialize subject for public error reporting */ + Ssub_Ctor(&self->public_error_subject, self->mns_inst_id); +} + +/*! \brief Adds an observer which reports public errors + * \param self Instance pointer + * \param obs_ptr Reference to an observer + */ +void Eh_AddObsrvPublicError(CEventHandler *self, CSingleObserver *obs_ptr) +{ + (void)Ssub_AddObserver(&self->public_error_subject, obs_ptr); +} + +/*! \brief Removes an observer registered by Eh_AddObsrvPublicError(). + * \param self Instance pointer + */ +void Eh_DelObsrvPublicError(CEventHandler *self) +{ + Ssub_RemoveObserver(&self->public_error_subject); +} + +/*! \brief Reports an event to the event handler. + * \param self Instance pointer + * \param event_code Event code to report + */ +void Eh_ReportEvent(CEventHandler *self, uint32_t event_code) +{ + Mns_Error_t public_error_code; + /* Check if event code exists */ + if((event_code & EH_M_ALL_EVENTS) != 0U) + { + /* Encode internal event code */ + bool result = Eh_EncodeEvent(event_code, &public_error_code); + /* Notify all registered observers */ + Msub_Notify(&self->internal_event_subject, &event_code, event_code); + /* Report error to application? */ + if(result != false) + { + Ssub_Notify(&self->public_error_subject, &public_error_code, false); + } + } +} + +/*! \brief Encodes an internal event code. Some internal event codes are mapped to public + * error codes. + * \param event_code Internal event code to report + * \param public_error_code_ptr Returned public error code + * \return true if error must be reported to the application, otherwise false + */ +static bool Eh_EncodeEvent(uint32_t event_code, Mns_Error_t *public_error_code_ptr) +{ + bool ret_val = true; + + /* Translate internal event code into public error code */ + switch(event_code) + { + case EH_E_BIST_FAILED: + *public_error_code_ptr = MNS_GEN_ERR_INIC; + break; + case EH_E_UNSYNC_COMPLETE: + case EH_E_UNSYNC_FAILED: + *public_error_code_ptr = MNS_GEN_ERR_COMMUNICATION; + break; + default: + ret_val = false; /* Do not report this event to application. */ + break; + } + + return ret_val; +} + +/*! \brief Registers an observer on the given event code. + * \param self Instance pointer + * \param obs_ptr Reference to the masked-observer object + */ +void Eh_AddObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr) +{ + (void)Sub_AddObserver(&self->internal_event_subject, &obs_ptr->parent); +} + +/*! \brief Unregisters the given observer from the given event code. + * \param self Instance pointer + * \param obs_ptr Reference to the masked-observer object + */ +void Eh_DelObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr) +{ + (void)Sub_RemoveObserver(&self->internal_event_subject, &obs_ptr->parent); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_eh.h b/mnsl/mns_eh.h new file mode 100644 index 0000000..2186a2f --- /dev/null +++ b/mnsl/mns_eh.h @@ -0,0 +1,130 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the event handler. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_EH + * @{ + */ + +#ifndef MNS_EH_H +#define MNS_EH_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_eh_pb.h" +#include "mns_obs.h" +#include "mns_trace.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief A control FIFO synchronization is lost. When this event occurs the PMS still waits + * until all FIFOs are unsynchronized. So this event is no termination event. + */ +#define EH_E_SYNC_LOST 0x0001U +/*! \brief INIC Build-In-Self-Test failed + */ +#define EH_E_BIST_FAILED 0x0002U +/*! \brief Notifies completed un-synchronization of Port Message FIFOs + */ +#define EH_E_UNSYNC_COMPLETE 0x0004U +/*! \brief Notifies that the Port Message Channel was not able to un-synchronize its FIFOs + * within a definite time + */ +#define EH_E_UNSYNC_FAILED 0x0008U +/*! \brief MOST NetServices initialization succeeded + */ +#define EH_E_INIT_SUCCEEDED 0x0010U +/*! \brief MOST NetServices initialization failed + */ +#define EH_E_INIT_FAILED 0x0020U + +/*! \brief Mask including all events that lead to the termination of the MNS + */ +#define EH_M_TERMINATION_EVENTS (EH_E_UNSYNC_COMPLETE | EH_E_UNSYNC_FAILED | \ + EH_E_BIST_FAILED | EH_E_INIT_FAILED) + +/*! \brief Bitmask to identify all internal event codes + */ +#define EH_M_ALL_EVENTS (EH_M_TERMINATION_EVENTS | EH_E_INIT_SUCCEEDED | EH_E_SYNC_LOST) + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for callback functions which notifies the event handler + * observers. + * \param self Instance pointer + * \param event_code Reported event code + */ +typedef void (*Ehobs_UpdateCb_t)(void *self, uint32_t event_code); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of the event handler. */ +typedef struct CEventHandler_ +{ + /*! \brief Subject used for internal events */ + CSubject internal_event_subject; + /*! \brief Single subject to report error to application */ + CSingleSubject public_error_subject; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} CEventHandler; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CEventHandler */ +/*------------------------------------------------------------------------------------------------*/ +extern void Eh_Ctor(CEventHandler *self, uint8_t mns_inst_id); +extern void Eh_AddObsrvPublicError(CEventHandler *self, CSingleObserver *obs_ptr); +extern void Eh_DelObsrvPublicError(CEventHandler *self); +extern void Eh_ReportEvent(CEventHandler *self, uint32_t event_code); +extern void Eh_AddObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr); +extern void Eh_DelObsrvInternalEvent(CEventHandler *self, CMaskedObserver *obs_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_EH_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_eh_pb.h b/mnsl/mns_eh_pb.h new file mode 100644 index 0000000..422ae15 --- /dev/null +++ b/mnsl/mns_eh_pb.h @@ -0,0 +1,68 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Public header file of the event handler. + */ +/*! + * \addtogroup G_MNS_INIT_AND_SRV_TYPES + * @{ + */ + +#ifndef MNS_EH_PB_H +#define MNS_EH_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MOST NetServices general error codes */ +typedef enum Mns_Error_ +{ + MNS_GEN_ERR_COMMUNICATION = 1, /*!< \brief Fatal communication error between EHC and INIC */ + MNS_GEN_ERR_INIC = 2 /*!< \brief INIC internal error */ + +} Mns_Error_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_EH_PB_H */ + +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_encoder.c b/mnsl/mns_encoder.c new file mode 100644 index 0000000..5153e64 --- /dev/null +++ b/mnsl/mns_encoder.c @@ -0,0 +1,254 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of message encoder + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ENCODER + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_encoder.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Constants */ +/*------------------------------------------------------------------------------------------------*/ +#define ENC_LLR_TIME_DEFAULT 11U /*! \brief Default LLR time required to transmit valid messages + * with ContentType 0x81 + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Enc_Encode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]); +static void Enc_Decode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +static void Enc_Encode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]); +static void Enc_Decode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +static void Enc_Encode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]); +static void Enc_Decode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the interface of a specific encoder + * \details Creates all encoder interfaces as singletons + * \param type Specifies the type of encoder to retrieve + * \return The desired interface to the specified encoder + */ +IEncoder *Enc_GetEncoder(Enc_MsgContent_t type) +{ + static IEncoder enc_content_00 = {ENC_CONTENT_00, 8U, 12U, &Enc_Encode_00, &Enc_Decode_00}; + static IEncoder enc_content_80 = {ENC_CONTENT_80, 6U, 11U, &Enc_Encode_80, &Enc_Decode_80}; + static IEncoder enc_content_81 = {ENC_CONTENT_81, 6U, 13U, &Enc_Encode_81, &Enc_Decode_81}; + IEncoder *encoder_ptr = NULL; + + switch (type) + { + case ENC_CONTENT_00: + encoder_ptr = &enc_content_00; + break; + case ENC_CONTENT_80: + encoder_ptr = &enc_content_80; + break; + case ENC_CONTENT_81: + encoder_ptr = &enc_content_81; + break; + default: + encoder_ptr = NULL; + break; + } + + return encoder_ptr; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Content type "00" */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Encodes a message telegram to the "ContentType 0x00" MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Encode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + header[0] = MISC_HB(tel_ptr->source_addr); + header[1] = MISC_LB(tel_ptr->source_addr); + header[2] = MISC_HB(tel_ptr->destination_addr); + header[3] = MISC_LB(tel_ptr->destination_addr); + + header[4] = tel_ptr->id.fblock_id; + header[5] = tel_ptr->id.instance_id; + + header[6] = MISC_HB(tel_ptr->id.function_id); + header[7] = MISC_LB(tel_ptr->id.function_id); + + header[8] = (uint8_t)(tel_ptr->tel.tel_id << 4) | (uint8_t)((uint8_t)tel_ptr->id.op_type & 0xFU); + header[9] = tel_ptr->opts.llrbc; + + header[10] = tel_ptr->tel.tel_cnt; + header[11] = tel_ptr->tel.tel_len; +} + +/*! \brief Decodes a "ContentType 0x00" MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Decode_00(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + tel_ptr->source_addr = (uint16_t)((uint16_t)header[0] << 8) | (uint16_t)header[1]; + tel_ptr->destination_addr = (uint16_t)((uint16_t)header[2] << 8) | (uint16_t)header[3]; + + tel_ptr->id.fblock_id = header[4]; + tel_ptr->id.instance_id = header[5]; + + tel_ptr->id.function_id = (uint16_t)((uint16_t)header[6] << 8) | (uint16_t)header[7]; + + tel_ptr->tel.tel_id = header[8] >> 4; /* high nibble: TelId */ + tel_ptr->id.op_type = (Mns_OpType_t)(header[8] & 0x0FU); /* low nibble: OPType */ + + tel_ptr->opts.llrbc = header[9]; + tel_ptr->tel.tel_cnt = header[10]; + tel_ptr->tel.tel_len = header[11]; + + tel_ptr->tel.tel_data_ptr = &header[12]; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Content type "0x80" */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Encodes a message telegram to the "ContentType 0x80" MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Encode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ /* high nibble: TelId low nibble: OPType */ + header[0] = (uint8_t)(tel_ptr->tel.tel_id << 4) | (uint8_t)((uint8_t)tel_ptr->id.op_type & 0xFU); + header[1] = tel_ptr->tel.tel_cnt; + header[2] = tel_ptr->tel.tel_len; + + header[3] = MISC_HB(tel_ptr->id.function_id); + header[4] = MISC_LB(tel_ptr->id.function_id); + + header[5] = MISC_HB(tel_ptr->source_addr); + header[6] = MISC_LB(tel_ptr->source_addr); + + header[7] = MISC_HB(tel_ptr->destination_addr); + header[8] = MISC_LB(tel_ptr->destination_addr); + + header[9] = tel_ptr->id.fblock_id; + header[10] = tel_ptr->id.instance_id; +} + +/*! \brief Decodes a "ContentType 0x80" MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Decode_80(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + tel_ptr->tel.tel_id = header[0] >> 4; /* high nibble: TelId */ + tel_ptr->id.op_type = (Mns_OpType_t)(header[0] & 0x0FU); /* low nibble: OPType */ + + tel_ptr->tel.tel_cnt = header[1]; + tel_ptr->tel.tel_len = header[2]; + + tel_ptr->id.function_id = (uint16_t)((uint16_t)header[3] << 8) | (uint16_t)header[4]; + + tel_ptr->source_addr = (uint16_t)((uint16_t)header[5] << 8) | (uint16_t)header[6]; + tel_ptr->destination_addr = (uint16_t)((uint16_t)header[7] << 8) | (uint16_t)header[8]; + + tel_ptr->id.fblock_id = header[9]; + tel_ptr->id.instance_id = header[10]; + + tel_ptr->tel.tel_data_ptr = &header[11]; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Content type "0x81" */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Encodes a message telegram to the "ContentType 0x81" MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Encode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + header[0] = tel_ptr->opts.llrbc; + header[1] = ENC_LLR_TIME_DEFAULT; + /* high nibble: TelId low nibble: OPType */ + header[2] = (uint8_t)(tel_ptr->tel.tel_id << 4) | (uint8_t)((uint8_t)tel_ptr->id.op_type & 0xFU); + header[3] = tel_ptr->tel.tel_cnt; + header[4] = tel_ptr->tel.tel_len; + + header[5] = MISC_HB(tel_ptr->id.function_id); + header[6] = MISC_LB(tel_ptr->id.function_id); + + header[7] = MISC_HB(tel_ptr->source_addr); + header[8] = MISC_LB(tel_ptr->source_addr); + + header[9] = MISC_HB(tel_ptr->destination_addr); + header[10] = MISC_LB(tel_ptr->destination_addr); + + header[11] = tel_ptr->id.fblock_id; + header[12] = tel_ptr->id.instance_id; +} + +/*! \brief Decodes a "ContentType 0x81" MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +static void Enc_Decode_81(Msg_MostTel_t *tel_ptr, uint8_t header[]) +{ + tel_ptr->opts.llrbc = header[0]; + + tel_ptr->tel.tel_id = header[2] >> 4; /* high nibble: TelId */ + tel_ptr->id.op_type = (Mns_OpType_t)(header[2] & 0x0FU); /* low nibble: OPType */ + + tel_ptr->tel.tel_cnt = header[3]; + tel_ptr->tel.tel_len = header[4]; + + tel_ptr->id.function_id = (uint16_t)((uint16_t)header[5] << 8) | (uint16_t)header[6]; + + tel_ptr->source_addr = (uint16_t)((uint16_t)header[7] << 8) | (uint16_t)header[8]; + tel_ptr->destination_addr = (uint16_t)((uint16_t)header[9] << 8) | (uint16_t)header[10]; + + tel_ptr->id.fblock_id = header[11]; + tel_ptr->id.instance_id = header[12]; + + tel_ptr->tel.tel_data_ptr = &header[13]; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_encoder.h b/mnsl/mns_encoder.h new file mode 100644 index 0000000..439a622 --- /dev/null +++ b/mnsl/mns_encoder.h @@ -0,0 +1,118 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of message encoder + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_ENCODER + * @{ + */ + +#ifndef MNS_ENCODER_H +#define MNS_ENCODER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Defines */ +/*------------------------------------------------------------------------------------------------*/ +#define ENC_MAX_SIZE_CONTENT 16U /*!< \brief Maximum content size in bytes, quadlet aligned */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the size of a MOST message header + * \return The size of the MOST message header in bytes. + */ +typedef uint8_t (*Enc_GetSize_t)(void); + +/*! \brief Retrieves the content type of a MOST message header + * \return The content type of the MOST message header in bytes. + */ +typedef uint8_t (*Enc_GetContType_t)(void); + +/*! \brief Encodes a message telegram to the MOST message header + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +typedef void (*Enc_Encode_t)(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +/*! \brief Decodes a MOST message header to a message telegram structure + * \param tel_ptr Reference to the Msg_MostTel_t structure + * \param header The header buffer + */ +typedef void (*Enc_Decode_t)(Msg_MostTel_t *tel_ptr, uint8_t header[]); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Identifier for a MOST Message Content */ +typedef enum Enc_MsgContent_ +{ + ENC_CONTENT_00 = 0x00, /*!< \brief Content Type "0x00": Uncompressed, excluding retry values */ + ENC_CONTENT_80 = 0x80, /*!< \brief Content Type "0x80": Compressed, excluding retry values */ + ENC_CONTENT_81 = 0x81 /*!< \brief Content Type "0x81": Compressed, including retry values */ + +} Enc_MsgContent_t; + +/*! \brief Interface for message encoder */ +typedef struct IEncoder_ +{ + Enc_MsgContent_t content_type; /*!< \brief Retrieves the content type of the MOST message header */ + uint8_t pm_hdr_sz; /*!< \brief Retrieves the size of the Port Message header */ + uint8_t msg_hdr_sz; /*!< \brief Retrieves the size of the MOST message header */ + Enc_Encode_t encode_fptr; /*!< \brief Function required to encode a MOST message header */ + Enc_Decode_t decode_fptr; /*!< \brief Function required to decode a MOST message header */ + +} IEncoder; + +/*------------------------------------------------------------------------------------------------*/ +/* Function prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern IEncoder *Enc_GetEncoder(Enc_MsgContent_t type); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_ENCODER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_lld_pb.h b/mnsl/mns_lld_pb.h new file mode 100644 index 0000000..d1a087b --- /dev/null +++ b/mnsl/mns_lld_pb.h @@ -0,0 +1,221 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of the low-level driver interface + * \defgroup G_LLD Low-Level Driver + * \brief API functions to be used by the low-level driver. + * \details The MOST NetServices provides a certain set of functions which are only dedicated to the low-level driver. + * The low-level driver \em API is a set of functions which shall be used by the low-level driver. + * The low-level driver \em callbacks is a set of function that shall be implemented by the low-level driver. + * The low-level driver \em callbacks shall be assigned to the MOST NetServices initialization structure. + * During initialization MOST NetServices invokes the callback \ref Mns_Lld_Callbacks_t "start_fptr" and + * passes the low-level driver \em API as pointer to \ref Mns_Lld_Api_t. + * \mns_ic_started{ See also Getting Started with \ref P_UM_STARTED_LLD. } + * \mns_ic_examples{ See also Examples, section \ref P_UM_EXAMPLE_LLD_01, \ref P_UM_EXAMPLE_LLD_02 and \ref P_UM_EXAMPLE_LLD_03. } + * \ingroup G_MNS_API + * \defgroup G_LLD_TYPES Referred Types + * \brief Referred types used by the low-level driver interface + * \ingroup G_LLD + * \defgroup G_LLD_API Low-Level Driver API + * \brief Function pointers to be used by the low-level driver + * \ingroup G_LLD + */ + +#ifndef MNS_LLD_PB_H +#define MNS_LLD_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mns_memory_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * \addtogroup G_LLD_TYPES + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Tx message object providing the raw port message byte stream */ +typedef struct Mns_Lld_TxMsg_ +{ + struct Mns_Lld_TxMsg_ *custom_next_msg_ptr;/*!< \brief Shall be used by the LLD implementation to queue messages for + * asynchronous transmission + * \details MOST NetServices will set this value to \c NULL since only + * single messages are forwarded to the LLD. Within the transmit function + * it is recommended that the LLD queues the message for asynchronous + * transmission. Despite a driver's transmit function might signal busy for + * a short term MOST NetServices might forward multiple messages for + * transmission. If a driver works asynchronously (interrupt driven) it + * can easily use this pointer build a queue of waiting messages. + * Nonetheless, it is important that \ref Mns_Lld_Api_t::tx_release_fptr + * "tx_release_fptr" is invoked for every message separately. The Interface + * between MOST NetServices and the LLD does only support single messages. + */ + Mns_Mem_Buffer_t *memory_ptr; /*!< \brief Points to the data buffer */ + +} Mns_Lld_TxMsg_t; + +/*! \brief Rx message object pointing to the raw port message byte stream. */ +typedef struct Mns_Lld_RxMsg_ +{ + uint8_t* data_ptr; /*!< \brief Points to a MOST NetServices allocated memory chunk. */ + uint16_t data_size; /*!< \brief Size of the memory chunk in bytes. Valid values: 6..72. */ + +} Mns_Lld_RxMsg_t; + +/*! + * @} + * \addtogroup G_LLD_API + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Low-level driver API */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an Rx message object + * \param inst_ptr Reference to internal MOST NetServices handler + * \param buffer_size The size in bytes of the received Rx message. + * Valid values: 6..72. + * \return The Rx message object or \c NULL if no message object is available. In the latter + * case the low-level driver can wait until Mns_Lld_RxMsgAvailableCb_t() is invoked. + * The low-level driver is allowed to pre-allocate Rx messages with the maximum size + * of 72 bytes. After writing received data into Mns_Lld_RxMsg_t::data_ptr the + * low-level driver must set Mns_Lld_RxMsg_t::data_size to the actual message size. + * \warning + * The function will also return \c NULL if the requested \c buffer_size exceeds the valid range. + * In such a case the MOST NetServices cannot guarantee that Mns_Lld_RxMsgAvailableCb_t() is + * called as expected. Received messages exceeding the valid range must be discarded by the LLD. + */ +typedef Mns_Lld_RxMsg_t* (*Mns_Lld_RxAllocateCb_t)(void *inst_ptr, uint16_t buffer_size); + +/*! \brief Frees an unused Rx message object + * \param inst_ptr Reference to internal MOST NetServices handler + * \param msg_ptr Reference to the unused Rx message object + */ +typedef void (*Mns_Lld_RxFreeUnusedCb_t)(void *inst_ptr, Mns_Lld_RxMsg_t *msg_ptr); + +/*! \brief Pass an Rx message to MOST NetServices + * \param inst_ptr Reference to internal MOST NetServices handler + * \param msg_ptr Reference to the Rx message object containing the received + * message. + */ +typedef void (*Mns_Lld_RxReceiveCb_t)(void *inst_ptr, Mns_Lld_RxMsg_t *msg_ptr); + +/*! \brief Notifies that the LLD no longer needs to access the Tx message object + * \param inst_ptr Reference to internal MOST NetServices handler + * \param msg_ptr Reference to the Tx message object which is no longer accessed + * by the low-level driver + */ +typedef void (*Mns_Lld_TxReleaseCb_t)(void *inst_ptr, Mns_Lld_TxMsg_t *msg_ptr); + +/*! \brief Initialization required for one communication channel (control or packet) + */ +typedef struct Mns_Lld_Api_ +{ + Mns_Lld_RxAllocateCb_t rx_allocate_fptr; /*!< \brief Allocates an Rx message object */ + Mns_Lld_RxFreeUnusedCb_t rx_free_unused_fptr; /*!< \brief Frees an unused Rx message object */ + Mns_Lld_RxReceiveCb_t rx_receive_fptr; /*!< \brief Pass an Rx message to MOST NetServices */ + Mns_Lld_TxReleaseCb_t tx_release_fptr; /*!< \brief Notifies that the LLD no longer needs to access the Tx message object */ + +} Mns_Lld_Api_t; + +/*! + * @} + * \addtogroup G_LLD + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* LLD interface functions */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Notifies the LLD to start transmitting and receiving messages + * \param api_ptr Reference to MOST NetServices LLD interface + * \param ns_ptr Reference to internal MOST NetServices handler + */ +typedef void (*Mns_Lld_StartCb_t)(Mns_Lld_Api_t* api_ptr, void *ns_ptr, void *inst_ptr); + +/*! \brief Notifies the LLD to stop/abort transmitting and receiving messages + * \details As soon as this function is called the low-level driver is not allowed + * to call any MOST NetServices function. + */ +typedef void (*Mns_Lld_StopCb_t)(void *inst_ptr); + +/*! \brief Notifies the LLD to reset the INIC + * \details If this function is called the low-level driver is responsible to + * perform an INIC hardware reset. + */ +typedef void (*Mns_Lld_ResetInicCb_t)(void *inst_ptr); + +/*! \brief Callback function which is invoked as soon as port message objects are available again. + * \details By implementing this callback function the low-level driver can avoid polling for + * Rx message objects. The low-level driver should wait for the function call as soon + * as Mns_Lld_RxAllocateCb_t() returns NULL. Only then it shall call those functions again. + */ +typedef void (*Mns_Lld_RxMsgAvailableCb_t)(void *inst_ptr); + +/*! \brief Callback function which is invoked to transmit a single message to the INIC + * \param msg_ptr Reference to a single Tx message. + */ +typedef void (*Mns_Lld_TxTransmitCb_t)(Mns_Lld_TxMsg_t *msg_ptr, void *inst_ptr); + +/*! + * @} + * \addtogroup G_LLD_TYPES + * @{ + */ + +/*! \brief Set of functions implemented by the low-level driver + */ +typedef struct Mns_Lld_Callbacks_ +{ + Mns_Lld_StartCb_t start_fptr; /*!< \brief Callback function to initialize the low-level driver and + * start the transmission and reception of messages */ + Mns_Lld_StopCb_t stop_fptr; /*!< \brief Callback function to stop/abort the transmission and reception of messages */ + Mns_Lld_RxMsgAvailableCb_t rx_available_fptr; /*!< \brief Callback function which is invoked as soon as Rx message objects are available again */ + Mns_Lld_TxTransmitCb_t tx_transmit_fptr; /*!< \brief Callback function to transmit one or multiple messages to the INIC */ + +} Mns_Lld_Callbacks_t; + +/*! @} */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_LLD_PB_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_lldpool.c b/mnsl/mns_lldpool.c new file mode 100644 index 0000000..ae9f33d --- /dev/null +++ b/mnsl/mns_lldpool.c @@ -0,0 +1,101 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of LLD Message Pool + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_lldpool.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Returns an unused LLD Tx message object back to the pool + * \param self The instance + * \param owner_ptr Assigns messages to the respective FIFO + * \param mns_inst_id MOST NetServices instance ID + */ +void Lldp_Ctor(CLldPool *self, void *owner_ptr, uint8_t mns_inst_id) +{ + uint8_t cnt; + MISC_MEM_SET(self, 0, sizeof(*self)); + + Dl_Ctor(&self->list, mns_inst_id); + + for (cnt = 0U; cnt < LLDP_NUM_HANDLES; cnt++) /* setup LLD Tx handles */ + { + TR_ASSERT(mns_inst_id, "[FIFO]", (NULL == self->messages[cnt].msg_ptr)); + Dln_Ctor(&self->messages[cnt].node, &self->messages[cnt]); + self->messages[cnt].owner_ptr = owner_ptr; + Dl_InsertTail(&self->list, &self->messages[cnt].node); + } +} + +/*! \brief Returns an unused LLD Tx message object back to the pool + * \param self The instance + * \param msg_ptr The unused LLD Tx message object + */ +void Lldp_ReturnTxToPool(CLldPool *self, Lld_IntTxMsg_t *msg_ptr) +{ + Dl_InsertTail(&self->list, &msg_ptr->node); +} + +/*! \brief Allocates an unused LLD Tx message object from the pool + * \param self The instance + * \return An internal LLD Tx message object or \c NULL if no message object is + * available. + */ +Lld_IntTxMsg_t* Lldp_GetTxFromPool(CLldPool *self) +{ + CDlNode *node_ptr = NULL; + Lld_IntTxMsg_t *handle_ptr = NULL; + + node_ptr = Dl_PopHead(&self->list); + + if (node_ptr != NULL) + { + handle_ptr = (Lld_IntTxMsg_t*)Dln_GetData(node_ptr); + } + + return handle_ptr; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_lldpool.h b/mnsl/mns_lldpool.h new file mode 100644 index 0000000..fdf4419 --- /dev/null +++ b/mnsl/mns_lldpool.h @@ -0,0 +1,112 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of LLD Message Pool + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +#ifndef MNS_LLDPOOL_H +#define MNS_LLDPOOL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_base.h" +#include "mns_lld_pb.h" +#include "mns_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Number of LLD Tx handles dedicated to each FIFO */ +#define LLDP_NUM_HANDLES 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Internal LLD Tx message */ +typedef struct Lld_IntTxMsg_ +{ + Mns_Lld_TxMsg_t lld_msg; /*!< \brief Contains the public LLD Tx message + * \details This attribute needs to be the first one in this structure + */ + CDlNode node; /*!< \brief Node required for queuing */ + CMessage *msg_ptr; /*!< \brief Reference to the associated common message object, or + * \c NULL if the object is a command */ + void *owner_ptr; /*!< \brief Points to the FIFO which owns the message object + * or NULL if the object is a command */ + +} Lld_IntTxMsg_t; + +/*! \brief Internal LLD Rx message */ +typedef struct Lld_IntRxMsg_ +{ + Mns_Lld_RxMsg_t lld_msg; /*!< \brief Contains the public LLD Rx message + * \details This attribute needs to be the first one in this structure + */ + CMessage *msg_ptr; /*!< \brief Reference to the associated common message object*/ + +} Lld_IntRxMsg_t; + +/*! \brief The class CLldPool*/ +typedef struct CLldPool_ +{ + CDlList list; /*!< \brief Points to the first available message in Tx pool */ + Lld_IntTxMsg_t messages[LLDP_NUM_HANDLES];/*!< \brief Available messages in Tx pool */ + +} CLldPool; + +/*------------------------------------------------------------------------------------------------*/ +/* Function prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Lldp_Ctor(CLldPool *self, void *owner_ptr, uint8_t mns_inst_id); +extern void Lldp_ReturnTxToPool(CLldPool *self, Lld_IntTxMsg_t *msg_ptr); +extern Lld_IntTxMsg_t* Lldp_GetTxFromPool(CLldPool *self); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_LLDPOOL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_memory.h b/mnsl/mns_memory.h new file mode 100644 index 0000000..5b80bf1 --- /dev/null +++ b/mnsl/mns_memory.h @@ -0,0 +1,112 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of internal memory buffer + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MEMORY + * @{ + */ + +#ifndef MNS_MEMORY_H +#define MNS_MEMORY_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_memory_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* IAllocator Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback function which frees memory + * \param allocator Reference to the Mem_Allocator_t object + * \param mem_ptr Reference to memory chunk + * \param mem_info_ptr Customer specific information needed to free + * the related memory chunk + */ +typedef void (*Mem_Free_t)(void *allocator, void* mem_ptr, void* mem_info_ptr); + +/*! \brief Callback function which allocated memory + * \param allocator Reference to the Mem_Allocator_t object + * \param size Size of the demanded memory chunk + * \param mem_info_ptr Customer specific information needed to free + * the related memory chunk + * \return Reference to a memory chunk with a minimum size of \c size. + * Otherwise NULL. + */ +typedef void* (*Mem_Allocate_t)(void *allocator, uint16_t size, void** mem_info_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Interface IAllocator */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Interface which is needed to be implemented by a memory allocator */ +typedef struct IAllocator_ +{ + void* base; /*!< Reference to the base class */ + Mem_Allocate_t allocate_fptr; /*!< Callback function required to allocate memory */ + Mem_Free_t free_fptr; /*!< Callback function required to free memory */ + +} IAllocator; + + +/*------------------------------------------------------------------------------------------------*/ +/* Memory buffer */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Memory chunk comprising non public fields */ +typedef struct Mem_IntBuffer_ +{ + Mns_Mem_Buffer_t public_buffer; /*!< \brief Public attributes of memory buffer + * \details This has to be the first member in this + * struct + */ + IAllocator *allocator_ptr; /*!< \brief Reference to the allocator which is + * required to free the memory chunk + */ + void *mem_info_ptr; /*!< \brief Customer specific information needed to + * free the related memory chunk + */ +} Mem_IntBuffer_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MEMORY_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_memory_pb.h b/mnsl/mns_memory_pb.h new file mode 100644 index 0000000..2165e73 --- /dev/null +++ b/mnsl/mns_memory_pb.h @@ -0,0 +1,72 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of memory buffer and memory allocator + */ +/*! + * \addtogroup G_LLD_TYPES + * @{ + */ + +#ifndef MNS_MEMORY_PB_H +#define MNS_MEMORY_PB_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Memory buffer */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Memory chunk representing a message or part of a message. */ +typedef struct Mns_Mem_Buffer_ +{ + struct Mns_Mem_Buffer_ *next_buffer_ptr; /*!< \brief Points to an additional memory buffer + * that belongs to the same message. + */ + uint8_t *data_ptr; /*!< \brief Points to the data buffer */ + uint16_t data_size; /*!< \brief Size of the data buffer */ + uint16_t total_size; /*!< \brief Reserved for future use. Size of this and all concatenated data buffers */ + +} Mns_Mem_Buffer_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MEMORY_PB_H */ + +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_message.c b/mnsl/mns_message.c new file mode 100644 index 0000000..33a0673 --- /dev/null +++ b/mnsl/mns_message.c @@ -0,0 +1,331 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of class message + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MESSAGE + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of common MOST message class + * \param self The instance + */ +void Msg_Ctor(CMessage *self) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + + Dln_Ctor(&self->node, self); + + self->rsvd_memory.allocator_ptr = NULL; + self->rsvd_memory.mem_info_ptr = NULL; + self->rsvd_memory.public_buffer.next_buffer_ptr = NULL; + self->rsvd_memory.public_buffer.data_ptr = &self->rsvd_buffer[0]; + self->rsvd_memory.public_buffer.data_size = MSG_SIZE_RSVD_BUFFER; + self->rsvd_memory.public_buffer.total_size = MSG_SIZE_RSVD_BUFFER; + + self->start_ptr = &self->rsvd_buffer[0]; + self->pb_msg.tel.tel_data_ptr = &self->rsvd_buffer[0]; +/* self->pb_msg.tel.tel_id = 0U; + self->pb_msg.tel.tel_cnt = 0U; + self->pb_msg.tel.tel_len = 0U; */ + + self->pb_msg.opts.llrbc = MSG_LLRBC_DEFAULT; + +/* self->header_rsvd_sz = 0U; + self->header_curr_idx = 0U; + self->header_curr_sz = 0U; + self->ref_ptr = NULL; */ +} + +/*! \brief Prepares the message for re-usage + * \details In future this function has to take care that external memory + * has to be reinitialize properly. + * \param self The instance + */ +void Msg_Cleanup(CMessage *self) +{ + void *handle = self->lld_handle_ptr; /* restore associated LLD message object */ + void *pool_ptr = self->pool_ptr; /* restore associated pool reference */ + + Msg_Ctor(self); /* simply call constructor now */ + + self->lld_handle_ptr = handle; + self->pool_ptr = pool_ptr; +} + +/*! \brief Adds external message payload to the message + * \details The internally reserved message payload is no longer in in use. + * \param self The instance + * \param payload_ptr Pointer to externally allocated payload + * \param payload_sz Size of externally allocated payload + * \param mem_info_ptr Reference to additional memory information + */ +void Msg_SetExtPayload(CMessage *self, uint8_t *payload_ptr, uint8_t payload_sz, void* mem_info_ptr) +{ + self->pb_msg.tel.tel_data_ptr = payload_ptr; + self->pb_msg.tel.tel_len = payload_sz; + + self->ext_memory.allocator_ptr = NULL; + self->ext_memory.mem_info_ptr = mem_info_ptr; + self->ext_memory.public_buffer.data_ptr = payload_ptr; + self->ext_memory.public_buffer.data_size = payload_sz; + self->ext_memory.public_buffer.total_size = payload_sz; + self->ext_memory.public_buffer.next_buffer_ptr = NULL; +} + +/*! \brief Initially defines a header space in front of the data body + * \details Ensure that \c start_ptr is assigned correctly before calling + * this functions. + * \param self The instance + * \param header_sz Size of the header + */ +void Msg_ReserveHeader(CMessage *self, uint8_t header_sz) +{ + /* self->start_ptr stays */ + self->header_rsvd_sz = header_sz; + self->header_curr_idx = header_sz; + self->header_curr_sz = 0U; + + self->pb_msg.tel.tel_data_ptr = &self->start_ptr[header_sz]; +} + +/*! \brief Adds a defined header space in front of the current header + * \param self The instance + * \param header_sz Size of the header + */ +void Msg_PullHeader(CMessage *self, uint8_t header_sz) +{ +/* MNS_ASSERT(header_sz <= self->curr_header_sz); */ + +/* self->pb_msg.tel.tel_data_ptr = &self->rsvd_buffer[MSG_SIZE_RSVD_HEADER];*/ + self->header_curr_idx -= header_sz; + self->header_curr_sz += header_sz; +} + +/*! \brief Undoes a message header of a defined size + * \param self The instance + * \param header_sz Size of the header + */ +void Msg_PushHeader(CMessage *self, uint8_t header_sz) +{ + self->header_curr_idx += header_sz; + self->header_curr_sz -= header_sz; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Class Properties (get/set) */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the reference to the containing MOST Telegrams structure + * \param self The instance + * \return Pointer to the internal MOST Telegram structure + */ +Msg_MostTel_t* Msg_GetMostTel(CMessage *self) +{ + return &self->pb_msg; +} + +/*! \brief Retrieves the start of the current message header + * \param self The instance + * \return Pointer to the current header start + */ +uint8_t* Msg_GetHeader(CMessage *self) +{ + return &(self->rsvd_buffer[self->header_curr_idx]); +} + +/*! \brief Retrieves the size of the current message header + * \param self The instance + * \return Size of the current header in bytes + */ +uint8_t Msg_GetHeaderSize(CMessage * self) +{ + return (self->header_curr_sz); +} + +/*! \brief Retrieves the message buffer as memory structure + * \param self The instance + * \return Reference to the message memory structure + */ +Mns_Mem_Buffer_t* Msg_GetMemTx(CMessage *self) +{ + self->rsvd_memory.public_buffer.data_ptr = &(self->rsvd_buffer[self->header_curr_idx]); + + if (self->ext_memory.public_buffer.data_size == 0U) + { + self->rsvd_memory.public_buffer.next_buffer_ptr = NULL; + self->rsvd_memory.public_buffer.data_size = (uint16_t)self->header_curr_sz + (uint16_t)self->pb_msg.tel.tel_len; + self->rsvd_memory.public_buffer.total_size = (uint16_t)self->header_curr_sz + (uint16_t)self->pb_msg.tel.tel_len; + } + else + { + self->rsvd_memory.public_buffer.next_buffer_ptr = &self->ext_memory.public_buffer; + self->rsvd_memory.public_buffer.data_size = (uint16_t)self->header_curr_sz; /* only header is enclosed */ + self->rsvd_memory.public_buffer.total_size = self->rsvd_memory.public_buffer.data_size + + self->ext_memory.public_buffer.data_size; + } + + return &self->rsvd_memory.public_buffer; +} + +/*! \brief Assigns a message status handler which is called as soon as the message is processed + * \param self The instance + * \param callback_fptr Reference to the status callback function + * \param inst_ptr The instance which implements the status callback + */ +void Msg_SetTxStatusHandler(CMessage *self, Msg_TxStatusCb_t callback_fptr, void *inst_ptr) +{ + self->tx_status_inst = inst_ptr; + self->tx_status_fptr = callback_fptr; +} + +/*! \brief Marks the message as occupied by the LLD + * \param self The instance + * \param active Set to \c true if the message is occupied by the LLD, otherwise \c false. + */ +void Msg_SetTxActive(CMessage *self, bool active) +{ + self->tx_active = active; +} + +/*! \brief Checks if the message as occupied by the LLD + * \param self The instance + * \return Returns \c true if the message is occupied by the LLD, otherwise \c false. + */ +bool Msg_IsTxActive(CMessage *self) +{ + return self->tx_active; +} + +/*! \brief Marks the message as bypass message + * \param self The instance + * \param bypass Set to \c true if the message is supposed to be a bypass message, otherwise \c false. + */ +void Msg_SetTxBypass(CMessage *self, bool bypass) +{ + self->tx_bypass = bypass; +} + +/*! \brief Checks if the message is marked as bypass message + * \param self The instance + * \return Returns \c true if the message is marked as bypass message, otherwise \c false. + */ +bool Msg_IsTxBypass(CMessage *self) +{ + return self->tx_bypass; +} + +/*! \brief Fires a status notification for the message object + * \param self The instance + * \param status The transmission status + */ +void Msg_NotifyTxStatus(CMessage *self, Mns_MsgTxStatus_t status) +{ + if (self->tx_status_fptr != NULL) + { + self->tx_status_fptr(self->tx_status_inst, &self->pb_msg, status); + } +} + +/*! \brief Assigns a low-level driver message + * \param self The instance + * \param handle The reference to a low-level driver message object (Tx or Rx) + */ +void Msg_SetLldHandle(CMessage *self, void *handle) +{ + self->lld_handle_ptr = handle; +} + +/*! \brief Retrieves the reference to a low-level driver message + * \param self The instance + * \return The reference to a low-level driver message object or \c NULL + * if no message is assigned. + */ +void *Msg_GetLldHandle(CMessage *self) +{ + return self->lld_handle_ptr; +} + +/*! \brief Assigns a reference for the owning pool + * \param self The instance + * \param pool_ptr The reference to the owning pool + */ +void Msg_SetPoolReference(CMessage *self, void *pool_ptr) +{ + self->pool_ptr = pool_ptr; +} + +/*! \brief Retrieves a reference for the owning pool + * \param self The instance + * \return The reference to the owning pool or \c NULL + * if no pool is assigned. + */ +void *Msg_GetPoolReference(CMessage *self) +{ + return self->pool_ptr; +} + +/*! \brief Retrieves the reference to the internal node member + * \param self The instance + * \return The reference the internal list node + */ +CDlNode *Msg_GetNode(CMessage *self) +{ + return &self->node; +} + +/*! \brief Performs checks on length payload length + * \param self The instance + * \return Returns \c true if the verification succeeded. Otherwise \c false. + */ +bool Msg_VerifyContent(CMessage *self) +{ + bool success = (self->pb_msg.tel.tel_len <= MSG_MAX_SIZE_PAYLOAD) ? true : false; + + return success; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_message.h b/mnsl/mns_message.h new file mode 100644 index 0000000..69d82ea --- /dev/null +++ b/mnsl/mns_message.h @@ -0,0 +1,238 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of class message + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MESSAGE + * @{ + */ + +#ifndef MNS_MESSAGE_H +#define MNS_MESSAGE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_memory.h" +#include "mns_dl.h" +#include "mns_message_pb.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Common macros */ +/*------------------------------------------------------------------------------------------------*/ +#define MSG_ADDR_INVALID 0U /*!< \brief The (source) address the INIC uses to declare an invalid source address. + * \details Invalid source addresses can be: + * - invalid messages from MOST: source_address = [0x0000..0x000F] + * - invalid messages from EHC: source_address != [0x0002, 0x0003] + * . + */ +#define MSG_ADDR_INIC 1U /*!< \brief The address of the local INIC */ +#define MSG_ADDR_EHC_CFG 2U /*!< \brief The address of the EHC configuration interface (ICM and RCM FIFO) */ +#define MSG_ADDR_EHC_APP 3U /*!< \brief The address of the EHC application interface (MCM FIFO) */ + +#define MSG_LLRBC_DEFAULT 10U /*!< \brief The default LowLevelRetry BlockCount */ +#define MSG_LLRBC_MAX 100U/*!< \brief The maximum LowLevelRetry BlockCount */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MOST message id "FBlockID.InstID.FktID.OPType" */ +typedef struct Msg_MsgId_ +{ + uint8_t fblock_id; /*!< \brief FBlockID */ + uint8_t instance_id; /*!< \brief InstID */ + uint16_t function_id; /*!< \brief FktID */ + Mns_OpType_t op_type; /*!< \brief Operation type */ + +} Msg_MsgId_t; + +/*! \brief Retry options */ +typedef struct Msg_TxOptions_ +{ + uint8_t llrbc; /*!< \brief Low-level retry block count performed by the INIC. + * \details The LLRBC are applicable for MCMs. ICMs don't care. + * Values exceeding the maximum value are be corrected + * by the INIC silently to the maximum value. + * Valid range: 0..100 + */ + uint8_t cancel_id; /*!< \brief Either "0" or label for a group of dependent telegrams. + * \details The value determines the required action if the transmission + * has failed. + * Valid range: + * - 0: Only the failed telegram will is removed from the FIFO. + * - 1..255: All telegrams with the same cancel_id as a failed telegram + * will be removed from the FIFO queue. + */ + +} Msg_TxOptions_t; + +/*! \brief Most telegram data */ +typedef struct Msg_TelData_ +{ + uint8_t tel_id; /*!< \brief Telegram id which indicates the telegram as part of + * segmented message or as single transfer. */ + uint8_t tel_len; /*!< \brief The telegram length. + * I.e. the number of telegram bytes starting at address + * which is referred in \c tel_data_ptr. The INIC will add + * \em one in case of \"tel_id = 1..3\". + */ + uint8_t tel_cnt; /*!< \brief The message count indexing the telegram within a segmented + * message. + * The respective tel_cnt is moved by the INIC to \"DATA[0]\" + * in case of \"tel_id = 1..3\". Otherwise it is ignored. + */ + uint8_t *tel_data_ptr; /*!< \brief Points to telegram data. */ + +} Msg_TelData_t; + +/*! \brief Common MOST message */ +typedef struct Msg_MostTel_ +{ + uint16_t destination_addr; /*!< \brief MOST destination address */ + uint16_t source_addr; /*!< \brief MOST source address */ + + Msg_MsgId_t id; /*!< \brief MOST message id "FBlockID.InstID.FktID.OPType" */ + Msg_TxOptions_t opts; /*!< \brief Message transmission options */ + Msg_TelData_t tel; /*!< \brief MOST telegram data */ + void *info_ptr; /*!< \brief Possible reference to additional data */ + +} Msg_MostTel_t; + +/* necessary forward declaration */ +struct CMessage_; +/*! \brief Common message class which provides MOST style message addressing */ +typedef struct CMessage_ CMessage; + +/*! \brief Assignable function which is invoked as soon as transmission + * of the message object is finished. + * \param self The instance + * \param msg_ptr Reference to the message object + * \param status Transmission status + */ +typedef void (*Msg_TxStatusCb_t)(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status); + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Size in bytes of reserved message header */ +#define MSG_SIZE_RSVD_HEADER 24U +/*! \brief Size in bytes of message payload */ +#define MSG_MAX_SIZE_PAYLOAD 45U +/*! \brief Size in bytes of pre-allocated message buffer + * \details Size = 24(header) + 45(payload) + 3(stuffing) = 72 */ +#define MSG_SIZE_RSVD_BUFFER 72U + +/*------------------------------------------------------------------------------------------------*/ +/* Class CMessage */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CMessage + * \details Common internal message class which embeds the public message attributes + */ +struct CMessage_ +{ + Msg_MostTel_t pb_msg; /*!< \brief Public part which defines the MOST telegram + * structure. This attribute must be the first + * element inside the message structure. + */ + uint8_t rsvd_buffer[MSG_SIZE_RSVD_BUFFER]; /*!< \brief Reserved memory space */ + Mem_IntBuffer_t rsvd_memory; /*!< \brief Reserved memory which is needed at least for the + * Port message header (24 bytes) */ + Mem_IntBuffer_t ext_memory; /*!< \brief Possible user memory */ + + uint8_t *start_ptr; /*!< \brief Points to the start of the message buffer */ + uint8_t header_curr_idx; /*!< \brief Index of the end of the current header */ + uint8_t header_curr_sz; /*!< \brief Current size of header in bytes */ + uint8_t header_rsvd_sz; /*!< \brief Reserved size of header in bytes */ + + void *pool_ptr; /*!< \brief Point to the pool the message is allocated from and released to */ + void *lld_handle_ptr; /*!< \brief Possible reference to another message object */ + CDlNode node; /*!< \brief Node for usage in a doubly linked list */ + + Msg_TxStatusCb_t tx_status_fptr; /*!< \brief Pointer to Tx status callback */ + void *tx_status_inst; /*!< \brief Reference to instance which needs Tx status notification */ + + bool tx_active; /*!< \brief Is \c true if the object is occupied by the LLD, otherwise \c false */ + bool tx_bypass; /*!< \brief Is \c true if a message was queued as bypass message */ +}; + + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Msg_Ctor(CMessage *self); +extern void Msg_Cleanup(CMessage *self); + +extern void Msg_ReserveHeader(CMessage *self, uint8_t header_sz); +extern void Msg_PullHeader(CMessage *self, uint8_t header_sz); +extern void Msg_PushHeader(CMessage *self, uint8_t header_sz); + +extern void Msg_NotifyTxStatus(CMessage *self, Mns_MsgTxStatus_t status); + +/*------------------------------------------------------------------------------------------------*/ +/* Properties */ +/*------------------------------------------------------------------------------------------------*/ +extern Msg_MostTel_t* Msg_GetMostTel(CMessage *self); + +extern uint8_t* Msg_GetHeader(CMessage *self); +extern uint8_t Msg_GetHeaderSize(CMessage *self); +extern Mns_Mem_Buffer_t* Msg_GetMemTx(CMessage *self); + +extern void Msg_SetLldHandle(CMessage *self, void *handle); +extern void *Msg_GetLldHandle(CMessage *self); +extern void Msg_SetPoolReference(CMessage *self, void *pool_ptr); +extern void *Msg_GetPoolReference(CMessage *self); + +extern CDlNode *Msg_GetNode(CMessage *self); + +extern void Msg_SetTxStatusHandler(CMessage *self, Msg_TxStatusCb_t callback_fptr, void *inst_ptr); +extern void Msg_SetExtPayload(CMessage *self, uint8_t *payload_ptr, uint8_t payload_sz, void* mem_info_ptr); +extern void Msg_SetTxActive(CMessage *self, bool active); +extern bool Msg_IsTxActive(CMessage *self); +extern void Msg_SetTxBypass(CMessage *self, bool bypass); +extern bool Msg_IsTxBypass(CMessage *self); + +extern bool Msg_VerifyContent(CMessage *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MESSAGE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_message_pb.h b/mnsl/mns_message_pb.h new file mode 100644 index 0000000..8656519 --- /dev/null +++ b/mnsl/mns_message_pb.h @@ -0,0 +1,121 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of public message types + */ + +#ifndef MNS_MESSAGE_PB_H +#define MNS_MESSAGE_PB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * \addtogroup G_MNS_AMS + * @{ + */ +/*------------------------------------------------------------------------------------------------*/ +/* Defines */ +/*------------------------------------------------------------------------------------------------*/ +#define MNS_ADDR_INTERNAL 0x0000U /*!< \brief Internal transmission destination address + * \details Can be used for internal message transmission + * to avoid possible race conditions during + * recalculation of the own node address. + */ +#define MNS_ADDR_BROADCAST_BLOCKING 0x03C8U /*!< \brief Blocking broadcast destination address */ +#define MNS_ADDR_BROADCAST_UNBLOCKING 0x03FFU /*!< \brief Unblocking broadcast destination address */ +#define MNS_ADDR_DEBUG 0x0FF0U /*!< \brief Optional debug destination address */ +/*! @} */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Message transmission status for internal/debug use + * \ingroup G_MNS_MISC_RET_RES + */ +typedef enum Mns_MsgTxStatus_ +{ + MNS_MSG_STAT_OK = 0x00U, /*!< \brief Transmission succeeded */ + MNS_MSG_STAT_ERROR_CFG_NO_RCVR = 0x01U, /*!< \brief No internal receiver exists */ + MNS_MSG_STAT_ERROR_BF = 0x08U, /*!< \brief Buffer full */ + MNS_MSG_STAT_ERROR_CRC = 0x09U, /*!< \brief CRC */ + MNS_MSG_STAT_ERROR_ID = 0x0AU, /*!< \brief Corrupted identifiers */ + MNS_MSG_STAT_ERROR_ACK = 0x0BU, /*!< \brief Corrupted PACK or CACK */ + MNS_MSG_STAT_ERROR_TIMEOUT = 0x0CU, /*!< \brief TX timeout */ + MNS_MSG_STAT_ERROR_FATAL_WT = 0x10U, /*!< \brief Wrong target */ + MNS_MSG_STAT_ERROR_FATAL_OA = 0x11U, /*!< \brief Own node address */ + MNS_MSG_STAT_ERROR_NA_TRANS = 0x18U, /*!< \brief Control channel was switched off and + * a pending transmission was canceled */ + MNS_MSG_STAT_ERROR_NA_OFF = 0x19U, /*!< \brief Control channel not available */ + MNS_MSG_STAT_ERROR_UNKNOWN = 0xFEU, /*!< \brief Unknown error status */ + MNS_MSG_STAT_ERROR_SYNC = 0xFFU /*!< \brief Internal error which is notified if + * communication link with INIC is lost + */ +} Mns_MsgTxStatus_t; + +/*! \brief Operation Types + * \ingroup G_MNS_AMS_TYPES + */ +typedef enum Mns_OpType_ +{ + MNS_OP_SET = 0x0, /*!< \brief Operation Set (Property) */ + MNS_OP_GET = 0x1, /*!< \brief Operation Get (Property) */ + MNS_OP_SETGET = 0x2, /*!< \brief Operation SetGet (Property) */ + MNS_OP_INC = 0x3, /*!< \brief Operation Increment (Property) */ + MNS_OP_DEC = 0x4, /*!< \brief Operation Decrement (Property) */ + MNS_OP_STATUS = 0xC, /*!< \brief Operation Status (Property) */ + + MNS_OP_START = 0x0, /*!< \brief Operation Start (Method) */ + MNS_OP_ABORT = 0x1, /*!< \brief Operation Abort (Method) */ + MNS_OP_STARTRESULT = 0x2, /*!< \brief Operation StartResult (Method) */ + MNS_OP_PROCESSING = 0xB, /*!< \brief Operation Processing (Method) */ + MNS_OP_RESULT = 0xC, /*!< \brief Operation Result (Method) */ + + MNS_OP_STARTACK = 0x8, /*!< \brief Operation StartAck (Method) */ + MNS_OP_ABORTACK = 0x7, /*!< \brief Operation AbortAck (Method) */ + MNS_OP_STARTRESULTACK = 0x6, /*!< \brief Operation StartResultAck (Method) */ + MNS_OP_PROCESSINGACK = 0xA, /*!< \brief Operation ProcessingAck (Method) */ + MNS_OP_RESULTACK = 0xD, /*!< \brief Operation ResultAck (Method) */ + + MNS_OP_GETINTERFACE = 0x5, /*!< \brief Operation GetInterface (Property/Method) */ + MNS_OP_INTERFACE = 0xE, /*!< \brief Operation Interface (Property/Method) */ + MNS_OP_ERROR = 0xF, /*!< \brief Operation Error (Property/Method) */ + MNS_OP_ERRORACK = 0x9 /*!< \brief Operation ErrorAck (Property/Method) */ + +} Mns_OpType_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MESSAGE_PB_H */ + + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_misc.c b/mnsl/mns_misc.c new file mode 100644 index 0000000..c020c11 --- /dev/null +++ b/mnsl/mns_misc.c @@ -0,0 +1,82 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the library module which contains miscellaneous helper functions. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MISC + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MOST NetServices internal memset-function. + * \param dst_ptr Pointer to the block of memory to fill + * \param value Value to be set + * \param size Number of bytes to be set to the value + */ +void Misc_MemSet(void *dst_ptr, int32_t value, uint32_t size) +{ + uint8_t *dst_ptr_ = (uint8_t *)dst_ptr; + uint32_t i; + + for(i=0U; i. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the library module which contains miscellaneous helper functions. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MISC + * @{ + */ + +#ifndef MNS_MISC_H +#define MNS_MISC_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Standard library functions */ +/*------------------------------------------------------------------------------------------------*/ +/* parasoft suppress item MISRA2004-19_7 reason "function-like macros allowed for stdlib and helper functions" */ + +/*! \def MISC_MEM_SET + * \brief Macro to encapsulate memset function + * \details By defining the macro MNS_MEM_SET the application is able to specify its own memset + * function. If the macro is not defined MOST NetServices internal memset function + * Misc_MemSet() is used. + * \param dest Pointer to the block of memory to fill + * \param value Value to be set + * \param size Number of bytes to be set to the value. + */ +#ifdef MNS_MEM_SET +#define MISC_MEM_SET(dest, value, size) (MNS_MEM_SET((dest), (value), (size))) +#else +#define MISC_MEM_SET(dest, value, size) (Misc_MemSet((dest), (value), (size))) +#endif + +/*! \def MISC_MEM_CPY + * \brief Macro to encapsulate memcpy function + * \details By defining the macro MNS_MEM_CPY the application is able to specify its own memcpy + * function. If the macro is not defined MOST NetServices internal memcpy function + * Misc_MemCpy() is used. + * \param dest Pointer to the destination array where the content is to be copied + * \param src Pointer to the source of data to be copied + * \param size Number of bytes to copy + */ +#ifdef MNS_MEM_CPY +#define MISC_MEM_CPY(dest, src, size) (MNS_MEM_CPY((dest), (src), (size))) +#else +#define MISC_MEM_CPY(dest, src, size) (Misc_MemCpy((dest), (src), (size))) +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Helper Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Macro to avoid compiler warning "Unused Parameter" */ +#define MISC_UNUSED(p) ((p) = (p)) + +/*! \brief High Byte of 16-bit value */ +#define MISC_HB(value) ((uint8_t)((uint16_t)(value) >> 8)) + +/*! \brief Low Byte of 16-bit value */ +#define MISC_LB(value) ((uint8_t)((uint16_t)(value) & (uint16_t)0xFF)) + +/*! \brief Big-Endian to target 16 bit */ +#define MISC_DECODE_WORD(w_ptr, msb_ptr) (*(w_ptr) = \ + (uint16_t)((uint16_t)((uint16_t)(msb_ptr)[0] << 8) | (uint16_t)(msb_ptr)[1])) + +/*! \brief Big-Endian to target 32 bit */ +#define MISC_DECODE_DWORD(dw_ptr, msb_ptr) (*(dw_ptr) = \ + (uint32_t)((uint32_t)((uint32_t)(msb_ptr)[0] << 24) | \ + (uint32_t)((uint32_t)(msb_ptr)[1] << 16) | \ + (uint32_t)((uint32_t)(msb_ptr)[2] << 8) | (uint32_t)(msb_ptr)[3])) + +/*! \brief Checks if a value is inside a certain range */ +#define MISC_IS_VALUE_IN_RANGE(val, min, max) ((((val) >= (min)) && ((val) <= (max))) ? true : false) + +/*! \brief Checks if the given size is a multiple of 4. If not, the given size is corrected + * by that macro. + */ +#define MISC_QUADLET_ALGINED_SIZE(size) (((((size)+4U)-1U)/4U)*4U) + +/* parasoft unsuppress item MISRA2004-19_7 reason "function-like macros allowed for stdlib and helper functions" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Misc_MemSet(void *dst_ptr, int32_t value, uint32_t size); +extern void Misc_MemCpy(void *dst_ptr, void *src_ptr, uint32_t size); + +/*! + * @} + * \endcond + */ + +/*! + * \def MNS_MEM_SET + * \brief Customer assignment of memset function + * \details By defining the macro MNS_MEM_SET the application is able to specify its own memset + * function to be used by the MOST NetServices. If the macro is not set the MOST + * NetServices will use byte wise write operations. + * \ingroup G_MNS_MISC + */ +#ifndef MNS_MEM_SET +#define MNS_MEM_SET +#endif + +/*! + * \def MNS_MEM_CPY + * \brief Customer assignment of memcpy function + * \details By defining the macro MNS_MEM_CPY the application is able to specify its own memcpy + * function to be used by the MOST NetServices. If the macro is not set the MOST + * NetServices will use byte wise copy operations. + * \ingroup G_MNS_MISC + */ +#ifndef MNS_MEM_CPY +#define MNS_MEM_CPY +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MISC_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_obs.c b/mnsl/mns_obs.c new file mode 100644 index 0000000..7183fb2 --- /dev/null +++ b/mnsl/mns_obs.c @@ -0,0 +1,453 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the observer library module. The module consists of the two classes + * CSubject and CObserver. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_OBS + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_obs.h" +#include "mns_misc.h" +#include "mns_trace.h" + +#include //TKU + +/*------------------------------------------------------------------------------------------------*/ +/* Internal Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Sub_UpdateList(CSubject *self); +static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CSubject */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the subject class. Initializes a subject which distributes its data to + * a list of observers. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Sub_Ctor(CSubject *self, uint8_t mns_inst_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = mns_inst_id; + Dl_Ctor(&self->list, self->mns_inst_id); + Dl_Ctor(&self->add_list, self->mns_inst_id); +} + +/*! \brief Adds an observer to a subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_ALREADY_ADDED: Observer is already added + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Sub_AddObserver(CSubject *self, CObserver *obs_ptr) +{ + Sub_Ret_t ret_val; + if(obs_ptr == NULL) + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + else if(obs_ptr->valid != false) + { + ret_val = SUB_ALREADY_ADDED; + } + else if((self->notify != false) && + (false == Dl_IsNodeInList(&self->list, &obs_ptr->node)) && + (false == Dl_IsNodeInList(&self->add_list, &obs_ptr->node))) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers < 0xFFU)); + Dl_InsertTail(&self->add_list, &obs_ptr->node); + obs_ptr->valid = true; + self->changed = true; + ret_val = SUB_DELAYED; + } + else if((self->notify == false) && (false == Dl_IsNodeInList(&self->list, &obs_ptr->node))) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers < 0xFFU)); + ret_val = SUB_OK; + Dl_InsertTail(&self->list, &obs_ptr->node); + obs_ptr->valid = true; + self->num_observers++; + } + else + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + return ret_val; +} + +/*! \brief Removes an observer from a subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Sub_RemoveObserver(CSubject *self, CObserver *obs_ptr) +{ + Sub_Ret_t ret_val; + if(obs_ptr == NULL) + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + else if(obs_ptr->valid == false) + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + else if((self->notify != false) && + (Dl_IsNodeInList(&self->list, &obs_ptr->node) != false)) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers > 0U)); + obs_ptr->valid = false; + self->changed = true; + self->num_observers--; + ret_val = SUB_DELAYED; + } + else if((self->notify == false) && + (DL_OK == Dl_Remove(&self->list, &obs_ptr->node))) + { + TR_ASSERT(self->mns_inst_id, "[OBS]", (self->num_observers > 0U)); + self->num_observers--; + ret_val = SUB_OK; + } + else + { + ret_val = SUB_UNKNOWN_OBSERVER; + } + return ret_val; +} + +/*! \brief Notifies all registered observers of a subject. + * \param self Instance pointer + * \param data_ptr Reference to value to distribute (optional) + */ +void Sub_Notify(CSubject *self, void *data_ptr) +{ + if(NULL != self) + { + CDlNode *n_tmp = self->list.head; + self->notify = true; + self->changed = false; + while(NULL != n_tmp) + { + CObserver *o_tmp = (CObserver *)n_tmp->data_ptr; + if((NULL != o_tmp->update_fptr) && (o_tmp->valid != false)) + { + (o_tmp->update_fptr)(o_tmp->inst_ptr, data_ptr); + } + n_tmp = n_tmp->next; + } + if(self->changed != false) + { + Sub_UpdateList(self); + } + self->notify = false; + } +} + +/*! \brief Updates the list of observers. Delayed remove- and add-operations are processed. + * \param self Instance pointer + */ +static void Sub_UpdateList(CSubject *self) +{ + (void)Dl_Foreach(&self->list, &Sub_CheckObserver, self); + Dl_AppendList(&self->list, &self->add_list); +} + +/*! \brief Checks if the given observer is still valid. If the observer is invalid it will be + * removed from the list. This function is used by the foreach loop in Sub_UpdateList(). + * \param current_obs_ptr Reference to the current observer object + * \param subject_ptr Reference to the subject object + * \return Returns always \c false. Force to process the whole list. + */ +static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr) +{ + CObserver *current_obs_ptr_ = (CObserver *)current_obs_ptr; + CSubject *subject_ptr_ = (CSubject *)subject_ptr; + + if(false == current_obs_ptr_->valid) + { + (void)Dl_Remove(&subject_ptr_->list, ¤t_obs_ptr_->node); + } + return false; +} + +/*! \brief Returns the number of registered observers of a subject. + * \param self Instance pointer + * \return The number of registered observers + */ +uint8_t Sub_GetNumObservers(CSubject *self) +{ + return self->num_observers; +} + +/*! \brief Switches all observers of the source-subject to the target-subject. + * \param sub_target Target subject + * \param sub_source Source subject + * \return \c SUB_OK: No error + * \return \c SUB_INVALID_OPERATION: Target and source must be different objects + */ +Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source) +{ + Sub_Ret_t ret_val; + + if(sub_target == sub_source) + { + ret_val = SUB_INVALID_OPERATION; + } + else + { + Dl_AppendList(&sub_target->list, &sub_source->list); + sub_target->num_observers += sub_source->num_observers; + sub_source->num_observers = 0U; + ret_val = SUB_OK; + } + return ret_val; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the observer class. Initializes an observer which is notified + * by a corresponding subject. + * \param self Instance pointer + * \param inst_ptr Instance pointer used by update_fptr() + * \param update_fptr Callback function to update the observer + */ +void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr) +{ + assert(NULL != inst_ptr); + MISC_MEM_SET(self, 0, sizeof(*self)); + self->inst_ptr = inst_ptr; + self->update_fptr = update_fptr; + Dln_Ctor(&self->node, self); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CSingleSubject */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the single-subject class. Initializes a single-subject which distributes + * its data to the registered single-observer. + * \param self Instance pointer + * \param mns_inst_id MOST NetServices instance ID + */ +void Ssub_Ctor(CSingleSubject *self, uint8_t mns_inst_id) +{ + self->observer_ptr = NULL; + self->mns_inst_id = mns_inst_id; +} + +/*! \brief Adds a single-observer to a single-subject. + * \param self Instance pointer + * \param obs_ptr Pointer to single-observer instance + * \return \c SSUB_OK: No error + * \return \c SSUB_ALREADY_ADDED: Observer is already added + * \return \c SSUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Ssub_Ret_t Ssub_AddObserver(CSingleSubject *self, CSingleObserver *obs_ptr) +{ + Ssub_Ret_t ret_val; + if(obs_ptr == NULL) + { + ret_val = SSUB_UNKNOWN_OBSERVER; + } + else if(self->observer_ptr != obs_ptr) + { +#ifdef MNS_TR_INFO + if(self->observer_ptr != NULL) + { + TR_INFO((self->mns_inst_id, "[SSUB]", "Observer callback has been overwritten", 0U)); + } +#endif + ret_val = SSUB_OK; + self->observer_ptr = obs_ptr; + } + else + { + ret_val = SSUB_ALREADY_ADDED; + } + + return ret_val; +} + +/*! \brief Removes an single-observer from a single-subject. + * \param self Instance pointer + */ +void Ssub_RemoveObserver(CSingleSubject *self) +{ + self->observer_ptr = NULL; +} + +/*! \brief Notifies the registered single-observer of the given single-subject. + * \param self Instance pointer + * \param data_ptr Reference to value to distribute (optional) + * \param auto_remove If true the observer will be removed + */ +void Ssub_Notify(CSingleSubject *self, void *data_ptr, bool auto_remove) +{ + void *inst_ptr = NULL; + Obs_UpdateCb_t update_fptr = NULL; + if(self->observer_ptr != NULL) + { + inst_ptr = self->observer_ptr->inst_ptr; + update_fptr = self->observer_ptr->update_fptr; + if(auto_remove != false) + { + self->observer_ptr = NULL; + } + } + if(update_fptr != NULL) + { + update_fptr(inst_ptr, data_ptr); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CSingleObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the single-observer class. Initializes an single-observer which is + * notified by a corresponding single-subject. + * \param self Instance pointer + * \param inst_ptr Instance pointer used by update_fptr() + * \param update_fptr Callback function to update the observer + */ +void Sobs_Ctor(CSingleObserver *self, void *inst_ptr, Sobs_UpdateCb_t update_fptr) +{ + self->inst_ptr = inst_ptr; + self->update_fptr = update_fptr; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the masked-observer class. Initializes an observer which is notified + * by a corresponding subject. + * \param self Instance pointer + * \param inst_ptr Instance pointer used by update_fptr() + * \param notification_mask Notification bitmask + * \param update_fptr Callback function to update the observer + */ +void Mobs_Ctor(CMaskedObserver *self, + void *inst_ptr, + uint32_t notification_mask, + Obs_UpdateCb_t update_fptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + Obs_Ctor(&self->parent, inst_ptr, update_fptr); + self->notification_mask = notification_mask; +} + +/*! \brief Sets the notification mask of a masked-observer. + * \param self Instance pointer + * \param mask Bitmask to set + */ +void Mobs_SetNotificationMask(CMaskedObserver *self, uint32_t mask) +{ + self->notification_mask = mask; +} + +/*! \brief Retrieves the notification mask of a masked-observer. + * \param self Instance pointer + * \return Returns the current notification bitmask of the given observer + */ +uint32_t Mobs_GetNotificationMask(CMaskedObserver *self) +{ + return self->notification_mask; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Additional methods of class CSubject used in combination with CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Adds an masked-observer to a masked-subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_ALREADY_ADDED: Observer is already added + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Msub_AddObserver(CSubject *self, CMaskedObserver *obs_ptr) +{ + return Sub_AddObserver(self, &obs_ptr->parent); +} + +/*! \brief Removes an masked-observer from a subjects list. + * \param self Instance pointer + * \param obs_ptr Pointer to observer instance + * \return \c SUB_OK: No error + * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given + * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid + */ +Sub_Ret_t Msub_RemoveObserver(CSubject *self, CMaskedObserver *obs_ptr) +{ + return Sub_RemoveObserver(self, &obs_ptr->parent); +} + +/*! \brief Notifies all registered masked-observers of a masked-subject. + * \param self Instance pointer + * \param data_ptr Reference to value to distribute (optional) + * \param notification_mask Bitmask indicates notified observers + */ +void Msub_Notify(CSubject *self, void *data_ptr, uint32_t notification_mask) +{ + if(NULL != self) + { + CDlNode *n_tmp = self->list.head; + self->notify = true; + self->changed = false; + while(NULL != n_tmp) + { + CMaskedObserver *o_tmp = (CMaskedObserver *)n_tmp->data_ptr; + if( (NULL != o_tmp->parent.update_fptr) && + (o_tmp->parent.valid != false) && + ((o_tmp->notification_mask & notification_mask) != 0U) ) + { + (o_tmp->parent.update_fptr)(o_tmp->parent.inst_ptr, data_ptr); + } + n_tmp = n_tmp->next; + } + if(self->changed != false) + { + Sub_UpdateList(self); + } + self->notify = false; + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_obs.h b/mnsl/mns_obs.h new file mode 100644 index 0000000..088c6c8 --- /dev/null +++ b/mnsl/mns_obs.h @@ -0,0 +1,196 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the observer library module. The module consists of the two + * classes CSubject and CObserver. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_OBS + * @{ + */ + +#ifndef MNS_OBS_H +#define MNS_OBS_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mns_dl.h" +#include "mns_ret.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for callback functions which notifies the observers. + * \param self Instance pointer + * \param data_ptr Reference to optional data + */ +typedef void (*Obs_UpdateCb_t)(void *self, void *data_ptr); + +/*! \brief Function signature used for callback functions which notifies the single-observers. + * \param self Instance pointer + * \param data_ptr Reference to optional data + */ +typedef void (*Sobs_UpdateCb_t)(void *self, void *data_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerators */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return values of the subject class. */ +typedef enum Sub_Ret_ +{ + SUB_OK, /*!< \brief No error */ + SUB_DELAYED, /*!< \brief Operation is queued since notification is still active */ + SUB_ALREADY_ADDED, /*!< \brief Observer already added */ + SUB_UNKNOWN_OBSERVER, /*!< \brief Unknown observer */ + SUB_INVALID_OPERATION /*!< \brief Invalid operation */ + +} Sub_Ret_t; + +/*! \brief Standard return values of the single-subject class. */ +typedef enum Ssub_Ret_ +{ + SSUB_OK, /*!< \brief No error */ + SSUB_ALREADY_ADDED, /*!< \brief Observer already added */ + SSUB_UNKNOWN_OBSERVER /*!< \brief Unknown observer */ + +} Ssub_Ret_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class structure of observers which are notified by subjects. */ +typedef struct CObserver_ +{ + CDlNode node; /*!< \brief Node element to be able to add observer to list */ + void *inst_ptr; /*!< \brief Reference to instance used by update_fptr() */ + Obs_UpdateCb_t update_fptr; /*!< \brief Callback function to update the observer */ + bool valid; /*!< \brief Used for queued remove operation */ + +} CObserver; + +/*! \brief Class structure of subjects. */ +typedef struct CSubject_ +{ + CDlList list; /*!< \brief Doubly linked list to manage observers */ + CDlList add_list; /*!< \brief List to manage delayed add operations */ + uint8_t num_observers; /*!< \brief Number of added observers */ + bool notify; /*!< \brief Signals that the notification is in progress */ + bool changed; /*!< \brief Signals that an add- or a remove-operation + has been queued */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CSubject; + +/*! \brief Class structure of a single-observer which is notified by a single-subject. */ +typedef struct CSingleObserver_ +{ + void *inst_ptr; /*!< \brief Reference to instance used by update_fptr() */ + Obs_UpdateCb_t update_fptr; /*!< \brief Callback function to update the observer */ + +} CSingleObserver; + +/*! \brief Class structure of a single-subject. */ +typedef struct CSingleSubject_ +{ + CSingleObserver *observer_ptr; /*!< \brief Reference to the assigned single-observer */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + +} CSingleSubject; + +/*! \brief Class structure of masked observers which are notified by subjects. */ +typedef struct CMaskedObserver_ +{ + CObserver parent; /*!< \brief Parent class instance */ + uint32_t notification_mask; /*!< \brief Notification bitmask */ + +} CMaskedObserver; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CSubject */ +/*------------------------------------------------------------------------------------------------*/ +extern void Sub_Ctor(CSubject *self, uint8_t mns_inst_id); +extern Sub_Ret_t Sub_AddObserver(CSubject *self, CObserver *obs_ptr); +extern Sub_Ret_t Sub_RemoveObserver(CSubject *self, CObserver *obs_ptr); +extern void Sub_Notify(CSubject *self, void *data_ptr); +extern uint8_t Sub_GetNumObservers(CSubject *self); +extern Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CSingleSubject */ +/*------------------------------------------------------------------------------------------------*/ +extern void Ssub_Ctor(CSingleSubject *self, uint8_t mns_inst_id); +extern Ssub_Ret_t Ssub_AddObserver(CSingleSubject *self, CSingleObserver *obs_ptr); +extern void Ssub_RemoveObserver(CSingleSubject *self); +extern void Ssub_Notify(CSingleSubject *self, void *data_ptr, bool auto_remove); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CSingleObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern void Sobs_Ctor(CSingleObserver *self, void *inst_ptr, Sobs_UpdateCb_t update_fptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern void Mobs_Ctor(CMaskedObserver *self, + void *inst_ptr, + uint32_t notification_mask, + Obs_UpdateCb_t update_fptr); +extern void Mobs_SetNotificationMask(CMaskedObserver *self, uint32_t mask); +extern uint32_t Mobs_GetNotificationMask(CMaskedObserver *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Additional prototypes of class CSubject used in combination with CMaskedObserver */ +/*------------------------------------------------------------------------------------------------*/ +extern Sub_Ret_t Msub_AddObserver(CSubject *self, CMaskedObserver *obs_ptr); +extern Sub_Ret_t Msub_RemoveObserver(CSubject *self, CMaskedObserver *obs_ptr); +extern void Msub_Notify(CSubject *self, void *data_ptr, uint32_t notification_mask); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_OBS_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmchannel.c b/mnsl/mns_pmchannel.c new file mode 100644 index 0000000..e98a2b2 --- /dev/null +++ b/mnsl/mns_pmchannel.c @@ -0,0 +1,311 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of Port Message Channel + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMC + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmchannel.h" +#include "mns_pmp.h" +#include "mns_pmcmd.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal Constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal typedefs */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +/* LLD related interface functions */ +static Mns_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size); +static void Pmch_RxUnused(void *self, Mns_Lld_RxMsg_t *msg_ptr); +static void Pmch_RxReceive(void *self, Mns_Lld_RxMsg_t *msg_ptr); +static void Pmch_TxRelease(void *self, Mns_Lld_TxMsg_t *msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Constructor of class CPmChannel + * \param self The instance + * \param init_ptr Reference to initialization data structure + * \param inst_ptr TKU: MultiInstance param + */ +void Pmch_Ctor(CPmChannel *self, const Pmch_InitData_t *init_ptr, void *inst_ptr) +{ + uint16_t cnt; + MISC_MEM_SET(self, 0, sizeof(*self)); + + self->init_data = *init_ptr; + self->inst_ptr = inst_ptr; + self->lld_active = false; + + self->mns_iface.rx_allocate_fptr = &Pmch_RxAllocate; + self->mns_iface.rx_receive_fptr = &Pmch_RxReceive; + self->mns_iface.rx_free_unused_fptr = &Pmch_RxUnused; + self->mns_iface.tx_release_fptr = &Pmch_TxRelease; + + Pool_Ctor(&self->rx_msgs_pool, self->rx_msgs, /* initialize Rx message pool */ + PMCH_POOL_SIZE_RX, self->init_data.mns_inst_id); + for (cnt = 0U; cnt < PMCH_POOL_SIZE_RX; cnt++) /* and assign LLD Rx handles */ + { + Msg_SetLldHandle(&self->rx_msgs[cnt], &self->lld_rx_msgs[cnt]); + self->lld_rx_msgs[cnt].msg_ptr = &self->rx_msgs[cnt]; + } +} + +/*! \brief Registers an Rx callback function dedicated to one FIFO + * \param self The instance + * \param fifo_id The FIFO identifier + * \param rx_fptr The Rx callback function + * \param inst_ptr Reference to the instance required to invoke the callback + */ +void Pmch_RegisterReceiver(CPmChannel *self, Pmp_FifoId_t fifo_id, Pmch_OnRxMsg_t rx_fptr, void *inst_ptr) +{ + TR_ASSERT(self->init_data.mns_inst_id, "[PMCH]", (((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_ICM)||((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_MCM)||((uint8_t)fifo_id == (uint8_t)PMP_FIFO_ID_RCM))); + + self->receivers[fifo_id].rx_fptr = rx_fptr; + self->receivers[fifo_id].inst_ptr = inst_ptr; +} + +/*! \brief Un-initializes the LLD interface of the channel + * \param self The instance + */ +void Pmch_Initialize(CPmChannel *self) +{ + if (self->lld_active == false) + { + self->lld_active = true; + TR_INFO((self->init_data.mns_inst_id, "[PMCH]", "Pmch_Initialize(): LLD_START()", 0U)); + self->init_data.lld_iface.start_fptr(&self->mns_iface, self, self->inst_ptr); + } +} + +/*! \brief Un-initializes the LLD interface of the channel + * \param self The instance + */ +extern void Pmch_Uninitialize(CPmChannel *self) +{ + TR_INFO((self->init_data.mns_inst_id, "[PMCH]", "Pmch_Uninitialize(): Channel un-synchronization started", 0U)); + + if (self->lld_active != false) + { + self->lld_active = false; + TR_INFO((self->init_data.mns_inst_id, "[PMCH]", "Pmch_Uninitialize(): LLD_STOP()", 0U)); + self->init_data.lld_iface.stop_fptr(self->inst_ptr); + } +} + +/*! \brief Wrapper for LLD transmit + * \details This function which shall be used by all internal classes. No class shall + * invoke the LLD transmit function directly. Thus, it might be possible + * in future to handle transmission failures and retries. + * \param self The instance + * \param msg_ptr Reference to the public LLD message structure + */ +void Pmch_Transmit(CPmChannel *self, Mns_Lld_TxMsg_t *msg_ptr) +{ + if (self->lld_active != false) + { + self->init_data.lld_iface.tx_transmit_fptr(msg_ptr, self->inst_ptr); + } + else + { + Pmch_TxRelease(self, msg_ptr); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* The exposed low-level driver interface */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Allocates an Rx message object + * \param self The instance + * \param buffer_size Size of the memory chunk in bytes which is needed to + * copy the Rx message. + * \return Reference to an allocated Rx message object or \c NULL if no message object is available. + */ +static Mns_Lld_RxMsg_t* Pmch_RxAllocate(void *self, uint16_t buffer_size) +{ + CMessage *msg_ptr = NULL; + Mns_Lld_RxMsg_t *handle = NULL; + CPmChannel *self_ = (CPmChannel*)self; + + if (buffer_size <= MSG_SIZE_RSVD_BUFFER) + { + msg_ptr = Pool_GetMsg(&self_->rx_msgs_pool); + + if (msg_ptr != NULL) + { + Msg_Cleanup(msg_ptr); + handle = &((Lld_IntRxMsg_t*)Msg_GetLldHandle(msg_ptr))->lld_msg; + + TR_ASSERT(self_->init_data.mns_inst_id, "[PMCH]", (handle != NULL)); + + handle->data_size = buffer_size; + handle->data_ptr = Msg_GetHeader(msg_ptr); + } + else + { + self_->rx_trigger_available = true; + TR_INFO((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxAllocate(): Allocation failed, size=%u", 1U, buffer_size)); + } + } + else + { + self_->rx_trigger_available = true; + TR_FAILED_ASSERT(self_->init_data.mns_inst_id, "[PMCH]"); + } + + return handle; +} + +/*! \brief Frees an unused Rx message object + * \param self The instance + * \param msg_ptr Reference to the unused Rx message object + */ +static void Pmch_RxUnused(void *self, Mns_Lld_RxMsg_t *msg_ptr) +{ + CPmChannel *self_ = (CPmChannel*)self; + CMessage *pb_handle = ((Lld_IntRxMsg_t*)(void*)msg_ptr)->msg_ptr; + + TR_ASSERT(self_->init_data.mns_inst_id, "[PMCH]", (pb_handle != NULL)); + Pmch_ReturnRxToPool(self_, pb_handle); +} + +/*! \brief Pass an Rx message to MOST NetServices + * \param self The instance + * \param msg_ptr Reference to the Rx message object containing the received + * message. + */ +static void Pmch_RxReceive(void *self, Mns_Lld_RxMsg_t *msg_ptr) +{ + bool found = false; + CPmChannel *self_ = (CPmChannel*)self; + + if (msg_ptr->data_ptr != NULL) + { + if (msg_ptr->data_size >= PMP_PM_MIN_SIZE_HEADER) /* ignore incomplete messages */ + { + uint8_t fifo_no = (uint8_t)Pmp_GetFifoId(msg_ptr->data_ptr); /* get channel id (FIFO number) */ + + if ((fifo_no < PMP_MAX_NUM_FIFOS) && (self_->receivers[fifo_no].inst_ptr != NULL)) + { + CMessage *handle = ((Lld_IntRxMsg_t*)(void*)msg_ptr)->msg_ptr; + /* forward message to the respective FIFO/channel */ + self_->receivers[fifo_no].rx_fptr(self_->receivers[fifo_no].inst_ptr, handle); + found = true; + } + else + { + TR_ERROR((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxReceive(): received message for unregistered FIFO no=%u", 1U, fifo_no)); + } + } + else + { + TR_ERROR((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxReceive(): received incomplete message of size=%u", 1U, msg_ptr->data_size)); + } + } + else + { + TR_ERROR((self_->init_data.mns_inst_id, "[PMCH]", "Pmch_RxReceive(): message data is not valid", 0U)); + } + + if (false == found) + { + Pmch_RxUnused(self_, msg_ptr); /* Just return message to pool until PMC is implemented */ + } +} + +/*! \brief Notifies that the LLD no longer needs to access the Tx message object + * \param self The instance + * \param msg_ptr Reference to the Tx message object which is no longer accessed + * by the low-level driver + */ +static void Pmch_TxRelease(void *self, Mns_Lld_TxMsg_t *msg_ptr) +{ + CPmChannel *self_ = (CPmChannel*)self; + Lld_IntTxMsg_t *tx_ptr = (Lld_IntTxMsg_t*)(void*)msg_ptr; + + if ((tx_ptr->owner_ptr == NULL) && (tx_ptr->msg_ptr == NULL)) /* tx_ptr is command */ + { + Pmcmd_Release((CPmCommand*)(void*)tx_ptr); + } + else if (tx_ptr->owner_ptr != NULL) /* release message to FIFO */ + { + self_->init_data.tx_release_fptr(tx_ptr->owner_ptr, msg_ptr); + } + else + { + TR_FAILED_ASSERT(self_->init_data.mns_inst_id, "[PMCH]"); /* unknown FIFO - invalid message object */ + } + + TR_ASSERT(self_->init_data.mns_inst_id, "[PMCH]", (NULL == msg_ptr->custom_next_msg_ptr)); /* concatenation destroyed by the LLD */ + +} + +/*------------------------------------------------------------------------------------------------*/ +/* FIFO Related Callback Functions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Returns an unused Rx message object back to the pool + * \param self The instance + * \param msg_ptr The unused Rx message object + */ +void Pmch_ReturnRxToPool(void *self, CMessage *msg_ptr) +{ + CPmChannel *self_ = (CPmChannel*)self; + + Pool_ReturnMsg(msg_ptr); + + if (self_->rx_trigger_available == true) + { + self_->rx_trigger_available = false; + + if (self_->init_data.lld_iface.rx_available_fptr != NULL) + { + self_->init_data.lld_iface.rx_available_fptr(self_->inst_ptr); + } + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmchannel.h b/mnsl/mns_pmchannel.h new file mode 100644 index 0000000..8b069ad --- /dev/null +++ b/mnsl/mns_pmchannel.h @@ -0,0 +1,179 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of Port Message Channel + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMC + * @{ + */ + +#ifndef MNS_PMCHANNEL_H +#define MNS_PMCHANNEL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_lld_pb.h" +#include "mns_lldpool.h" +#include "mns_pool.h" +#include "mns_base.h" +#include "mns_message.h" +#include "mns_pmp.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define PMCH_POOL_SIZE_RX_MIN 10U /*!< \brief Minimal size of Rx pool which is shared by all FIFOs */ +#define PMCH_POOL_SIZE_RX_OPT 35U /*!< \brief Optimal size of Rx pool which is shared by all FIFOs */ + +#define PMCH_MCM_CREDITS_OPT 21U /*!< \brief Optimal number of credits configured for MCM FIFO */ +#define PMCH_MCM_THRESHOLD_OPT 8U /*!< \brief Optimal threshold configured for MCM FIFO */ + +#define PMCH_FIFO_CREDITS_OPT 5U /*!< \brief Optimal number of credits configured for conventional FIFOs */ +#define PMCH_FIFO_THRESHOLD_OPT 4U /*!< \brief Optimal threshold configured for conventional FIFO */ + +#define PMCH_FIFO_CREDITS_MIN 3U /*!< \brief Minimal number of credits configured for conventional FIFOs */ +#define PMCH_FIFO_THRESHOLD_MIN 2U /*!< \brief Minimal threshold configured for conventional FIFO */ + +/* required rules */ +#if defined(MNS_FOOTPRINT_TINY) && defined(MNSL_CHANNEL_POOL_SIZE_RX) +# error Forbidden combination of macros MNS_FOOTPRINT_TINY and MNSL_CHANNEL_POOL_SIZE_RX +#endif + +#ifdef MNSL_CHANNEL_POOL_SIZE_RX +# if (MNSL_CHANNEL_POOL_SIZE_RX < PMCH_POOL_SIZE_RX_MIN) +# error MNSL_CHANNEL_POOL_SIZE_RX must be at least 10 +# endif +#endif + +/*! \def MNSL_CHANNEL_POOL_SIZE_RX + * \brief MNSL configuration that defines the number of pre-allocated Rx messages which are shared by all FIFOs. + * Valid values: 35...65535. Default value: 35. + * + * \def PMCH_POOL_SIZE_RX + * \brief Defines the number of pre-allocated Rx messages which are shared by all FIFOs. + */ +#ifdef MNS_FOOTPRINT_TINY +# define PMCH_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_MIN) +# define PMCH_MCM_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_FIFO_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_MCM_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +# define PMCH_FIFO_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +# define MNSL_CHANNEL_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_MIN) +#elif defined MNSL_CHANNEL_POOL_SIZE_RX +# define PMCH_POOL_SIZE_RX ((uint16_t)MNSL_CHANNEL_POOL_SIZE_RX) +# define PMCH_MCM_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_FIFO_CREDITS (PMCH_FIFO_CREDITS_MIN) +# define PMCH_MCM_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +# define PMCH_FIFO_THRESHOLD (PMCH_FIFO_THRESHOLD_MIN) +#else +# define PMCH_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_OPT) +# define PMCH_MCM_CREDITS (PMCH_MCM_CREDITS_OPT) +# define PMCH_FIFO_CREDITS (PMCH_FIFO_CREDITS_OPT) +# define PMCH_MCM_THRESHOLD (PMCH_MCM_THRESHOLD_OPT) +# define PMCH_FIFO_THRESHOLD (PMCH_FIFO_THRESHOLD_OPT) +# define MNSL_CHANNEL_POOL_SIZE_RX (PMCH_POOL_SIZE_RX_OPT) +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +typedef void (*Pmch_OnRxMsg_t)(void *fifo_ptr, CMessage *msg_ptr); +typedef void (*Pmch_OnTxRelease_t)(void *fifo_ptr, Mns_Lld_TxMsg_t *handle_ptr); + +/*! \brief Initialization structure of the Base Module. */ +typedef struct Pmch_InitData_ +{ + uint8_t mns_inst_id; /*!< \brief Initialization data of the Trace Module */ + Mns_Lld_Callbacks_t lld_iface; /*!< \brief LLD callback functions */ + Pmch_OnTxRelease_t tx_release_fptr; /*!< \brief Callback which releases a FIFO dedicated LLD buffer */ + +} Pmch_InitData_t; + +/*! \brief Combination of callback and instance for a receiving FIFO */ +typedef struct Pmch_Receiver_ +{ + Pmch_OnRxMsg_t rx_fptr; /*!< \brief Reference to an Rx callback function */ + void *inst_ptr; /*!< \brief Reference to the instance which shall be + * passed to the callback function */ +} Pmch_Receiver_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Class attributes */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Structure of a PMS object */ +typedef struct CPmChannel_ +{ + Pmch_InitData_t init_data; /*!< \brief Copy of initialization data */ + + Lld_IntRxMsg_t lld_rx_msgs[PMCH_POOL_SIZE_RX]; /*!< \brief Pre-allocated LLD Rx message objects */ + CMessage rx_msgs[PMCH_POOL_SIZE_RX]; /*!< \brief Pre-allocated Rx message objects */ + CPool rx_msgs_pool; /*!< \brief Pre-allocated Rx message pool */ + bool rx_trigger_available; /*!< \brief Triggers LLD callback function if a buffer + * is available again. + */ + bool lld_active; /*!< \brief Determines whether the LLD is running */ + Mns_Lld_Api_t mns_iface; /*!< \brief PMS function pointers */ + + Pmch_Receiver_t receivers[PMP_MAX_NUM_FIFOS]; /*!< \brief Registered FIFOs for Rx */ + + void *inst_ptr; /*< TKU: tag for LLD */ + +} CPmChannel; + + +/*------------------------------------------------------------------------------------------------*/ +/* Class methods */ +/*------------------------------------------------------------------------------------------------*/ +/* component creation */ +extern void Pmch_Ctor(CPmChannel *self, const Pmch_InitData_t *init_ptr, void *inst_ptr); +extern void Pmch_Initialize(CPmChannel *self); +extern void Pmch_Uninitialize(CPmChannel *self); +extern void Pmch_RegisterReceiver(CPmChannel *self, Pmp_FifoId_t fifo_id, Pmch_OnRxMsg_t rx_fptr, void *inst_ptr); +extern void Pmch_Transmit(CPmChannel *self, Mns_Lld_TxMsg_t *msg_ptr); +extern void Pmch_ReturnRxToPool(void *self, CMessage *msg_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMCHANNEL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmcmd.c b/mnsl/mns_pmcmd.c new file mode 100644 index 0000000..21d8913 --- /dev/null +++ b/mnsl/mns_pmcmd.c @@ -0,0 +1,157 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of class CPmCommand + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PM_CMD + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmcmd.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of CPmCommand class + * \param self The instance + * \param fifo The dedicated FIFO + * \param type The port message type + */ +void Pmcmd_Ctor(CPmCommand *self, Pmp_FifoId_t fifo, Pmp_MsgType_t type) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); /* setup attributes */ + self->memory.data_ptr = &self->data[0]; + self->tx_obj.lld_msg.memory_ptr = &self->memory; + self->tx_obj.msg_ptr = NULL; /* label message as command by setting */ + self->tx_obj.owner_ptr = NULL; /* msg_ptr and owner_ptr to NULL */ + self->trigger = false; + + Pmp_SetPmhl(self->data, 3U); /* PMHL is always "3" for control/status messages */ + Pmp_SetFph(self->data, fifo, type); +} + +/*! \brief Retrieves reference to the LLD Tx message object required to call Pmch_Transmit() + * \param self The instance + * \return Returns a reference to the LLD Tx message object + */ +Mns_Lld_TxMsg_t* Pmcmd_GetLldTxObject(CPmCommand *self) +{ + return (Mns_Lld_TxMsg_t*)(void*)self; +} + +/*! \brief Sets the content of a command/status message + * \param self The instance + * \param sid The sequence id + * \param ext_type The ExtType type + * \param ext_code The ExtType code + * \param add_data_ptr Additional payload data + * \param add_data_sz The size of additional payload data, valid values: 0..4 + */ +void Pmcmd_SetContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, uint8_t ext_code, uint8_t add_data_ptr[], uint8_t add_data_sz) +{ + if ((add_data_ptr != NULL) && (add_data_sz != 0U)) + { + MISC_MEM_CPY(&self->data[6U], add_data_ptr, (size_t)add_data_sz); + } + + self->memory.data_size = 6U + (uint16_t)add_data_sz; + self->memory.total_size = 6U + (uint16_t)add_data_sz; + + Pmp_SetPml(self->data, 4U + add_data_sz); + Pmp_SetSid(self->data, sid); + Pmp_SetExtType(self->data, ext_type, ext_code); +} + +/*! \brief Updates the content of a command/status message + * \details The length and the content of the payload is not modified. + * It is important to call Pmcmd_SetContent() before. + * \param self The instance + * \param sid The sequence id + * \param ext_type The ExtType type + * \param ext_code The ExtType code + */ +void Pmcmd_UpdateContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, uint8_t ext_code) +{ + Pmp_SetSid(self->data, sid); + Pmp_SetExtType(self->data, ext_type, ext_code); +} + +/*! \brief Reserves the command object if it is available + * \param self The instance + * \return \c true if the command object is available, \c false + * if the command object is (still) in usage + */ +bool Pmcmd_Reserve(CPmCommand *self) +{ + bool succ = false; + + if (self->reserved == false) + { + self->reserved = true; + succ = true; + } + return succ; +} + +/*! \brief Releases the command object after usage + * \param self The instance + */ +void Pmcmd_Release(CPmCommand *self) +{ + self->reserved = false; +} + +/*! \brief Sets or resets the trigger attribute + * \param self The instance + * \param trigger The trigger value + */ +void Pmcmd_SetTrigger(CPmCommand *self, bool trigger) +{ + self->trigger = trigger; +} + +/*! \brief Returns the trigger value + * \param self The instance + * \return Returns \c true if the trigger attribute is set, otherwise \c false. + */ +bool Pmcmd_IsTriggered(CPmCommand *self) +{ + return self->trigger; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmcmd.h b/mnsl/mns_pmcmd.h new file mode 100644 index 0000000..5905beb --- /dev/null +++ b/mnsl/mns_pmcmd.h @@ -0,0 +1,92 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of class CPmCommand + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PM_CMD + * @{ + */ + +#ifndef MNS_PMCMD_H +#define MNS_PMCMD_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mns_memory.h" +#include "mns_lldpool.h" +#include "mns_pmp.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Class CPmCommand */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CPmCommand */ +typedef struct CPmCommand_ +{ + Lld_IntTxMsg_t tx_obj; /*!< \brief Required LLD Tx structure, must be first attribute */ + uint8_t data[10]; /*!< \brief Reserved memory space */ + Mns_Mem_Buffer_t memory; /*!< \brief Public memory structure */ + bool reserved; /*!< \brief \c true if the command is in use, otherwise \c false. */ + bool trigger; /*!< \brief \c true if the command is triggered, otherwise \c false. */ + +} CPmCommand; + + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pmcmd_Ctor(CPmCommand *self, Pmp_FifoId_t fifo, Pmp_MsgType_t type); +extern Mns_Lld_TxMsg_t* Pmcmd_GetLldTxObject(CPmCommand *self); +extern bool Pmcmd_Reserve(CPmCommand *self); +extern void Pmcmd_Release(CPmCommand *self); +extern void Pmcmd_SetContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, + uint8_t ext_code, uint8_t add_data_ptr[], uint8_t add_data_sz); +extern void Pmcmd_UpdateContent(CPmCommand *self, uint8_t sid, uint8_t ext_type, uint8_t ext_code); +extern void Pmcmd_SetTrigger(CPmCommand *self, bool trigger); +extern bool Pmcmd_IsTriggered(CPmCommand *self); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_PMCMD_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifo.c b/mnsl/mns_pmfifo.c new file mode 100644 index 0000000..a1cafbf --- /dev/null +++ b/mnsl/mns_pmfifo.c @@ -0,0 +1,1368 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of Port Message FIFO + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmfifo.h" +#include "mns_pmp.h" +#include "mns_pmcmd.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal macros */ +/*------------------------------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------------------------------*/ +/* Internal Constants */ +/*------------------------------------------------------------------------------------------------*/ +static const uint8_t FIFO_SRV_PRIO = 252U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */ +static const Srv_Event_t FIFO_SE_RX_SERVICE = 1U; /*!< \brief Event which triggers the Rx service */ +static const Srv_Event_t FIFO_SE_TX_SERVICE = 2U; /*!< \brief Event which triggers the Rx service */ +static const Srv_Event_t FIFO_SE_TX_APPLY_STATUS = 4U; /*!< \brief Event which triggers to apply the current INIC status */ +static const Srv_Event_t FIFO_SE_ALL = 7U; /* parasoft-suppress MISRA2004-8_7 "configuration property" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Fifo_InitCounters(CPmFifo *self, uint8_t tx_sid_complete, uint8_t tx_credits); +static void Fifo_Service(void *self); + +static void Fifo_RxService(CPmFifo *self); +static void Fifo_RxCheckStatusTrigger(CPmFifo *self); +static void Fifo_RxGetCredit(CPmFifo *self); +static void Fifo_RxReleaseCredit(CPmFifo *self); +static bool Fifo_RxProcessData(CPmFifo *self, CMessage *msg_ptr); +static void Fifo_RxProcessStatus(CPmFifo *self, CMessage *msg_ptr); +static void Fifo_RxProcessCommand(CPmFifo *self, CMessage *msg_ptr); +static void Fifo_RxProcessSyncStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code, uint8_t *header_ptr); +static uint8_t Fifo_RxCheckFailureCode(CPmFifo *self, uint8_t failure_code); +static void Fifo_OnRx(void *self, CMessage *msg_ptr); + +static void Fifo_TxService(CPmFifo *self); +static void Fifo_TxProcessData(CPmFifo *self); +static void Fifo_TxProcessStatus(CPmFifo *self); +static void Fifo_TxProcessCommand(CPmFifo *self); + +static void Fifo_TxEnqueueBypassMsg(CPmFifo *self, CDlList *q_ptr, CMessage *msg_ptr); +static bool Fifo_FindFirstRegularMsg(void *d_ptr, void *ud_ptr); + +static void Fifo_TxExecuteCancel(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code); +static void Fifo_TxExecuteCancelAll(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code); +static void Fifo_TxFinishedCancelAll(CPmFifo *self); +static uint8_t Fifo_TxPendingGetFollowerId(CPmFifo *self); +static void Fifo_TxCancelFollowers(CPmFifo *self, uint8_t follower_id, Mns_MsgTxStatus_t status); + +static bool Fifo_TxHasAccessPending(CPmFifo *self); +static void Fifo_TxRestorePending(CPmFifo *self); + +static void Fifo_TxOnWatchdogTimer(void *self); +static void Fifo_TxStartWatchdog(CPmFifo *self); + +static uint8_t Fifo_TxGetValidAcknowledges(CPmFifo *self, uint8_t sid); +static bool Fifo_TxNotifyStatus(CPmFifo *self, uint8_t sid, Mns_MsgTxStatus_t status); +static void Fifo_TxApplyCurrentStatus(CPmFifo *self); +static void Fifo_TxUpdateCurrentStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code); +static bool Fifo_TxIsIncomingSidValid(CPmFifo *self, uint8_t sid); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of message FIFO + * \param self The instance + * \param init_ptr Reference to initialization data + * \param config_ptr Reference to configuration + */ +void Fifo_Ctor(CPmFifo *self, const Fifo_InitData_t *init_ptr, const Fifo_Config_t *config_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + + self->init = *init_ptr; + self->config = *config_ptr; + + self->sync_state = FIFO_S_UNSYNCED_INIT; /* initialize members */ + Sub_Ctor(&self->sync_state_subject, self->init.base_ptr->mns_inst_id); + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Ctor(): state: %u", 1U, self->sync_state)); + + Srv_Ctor(&self->service, FIFO_SRV_PRIO, self, &Fifo_Service); /* registration of service */ + (void)Scd_AddService(&self->init.base_ptr->scd, &self->service); + + T_Ctor(&self->wd.timer); /* setup watchdog */ + self->wd.timer_value = self->config.tx_wd_timer_value; + Pmcmd_Ctor(&self->wd.wd_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD); + Pmcmd_SetContent(&self->wd.wd_cmd, 0U, PMP_CMD_TYPE_REQ_STATUS, PMP_CMD_CODE_REQ_STATUS, NULL, 0U); + + /* init Rx part */ + Dl_Ctor(&self->rx.queue, self->init.base_ptr->mns_inst_id); + self->rx.encoder_ptr = self->init.rx_encoder_ptr; + self->rx.on_complete_fptr = self->init.rx_cb_fptr; + self->rx.on_complete_inst = self->init.rx_cb_inst; + + self->rx.ack_threshold = self->config.rx_threshold; + + if (self->config.rx_threshold > self->config.rx_credits)/* configuration error - use single acknowledge */ + { + self->rx.ack_threshold = 1U; + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + + self->rx.wait_processing = false; + Pmcmd_Ctor(&self->rx.status, self->config.fifo_id, PMP_MSG_TYPE_STATUS); + Pmcmd_SetContent(&self->rx.status, 0U, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_SUCCESS, NULL, 0U); + + /* init Tx part */ + Dl_Ctor(&self->tx.waiting_queue, self->init.base_ptr->mns_inst_id); + Dl_Ctor(&self->tx.pending_q, self->init.base_ptr->mns_inst_id); + + Pmcmd_Ctor(&self->tx.cancel_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD); + Pmcmd_SetContent(&self->tx.cancel_cmd, 0U, PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL, NULL, 0U); + + Fifo_InitCounters(self, 0U, 0U); /* values are incremented on each sync attempt */ + self->tx.encoder_ptr = init_ptr->tx_encoder_ptr; + + /* FIFO synchronization command */ + self->sync_cnt = 0xFFU; + self->sync_params[0] = config_ptr->rx_credits; + self->sync_params[1] = config_ptr->rx_busy_allowed; + self->sync_params[2] = config_ptr->rx_ack_timeout; + self->sync_params[3] = config_ptr->tx_wd_timeout; + Pmcmd_Ctor(&self->tx.sync_cmd, self->config.fifo_id, PMP_MSG_TYPE_CMD); + Pmcmd_SetContent(&self->tx.sync_cmd, 0U, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_SYNC, self->sync_params, 4U); + + /* default PM header for Tx */ + self->tx.pm_header.pml = 6U; + self->tx.pm_header.pmhl = self->tx.encoder_ptr->pm_hdr_sz - 3U; + Pmh_SetFph(&self->tx.pm_header, self->config.fifo_id, PMP_MSG_TYPE_DATA); + self->tx.pm_header.sid = 0U; + self->tx.pm_header.ext_type = (uint8_t)self->tx.encoder_ptr->content_type; + + Lldp_Ctor(&self->tx.lld_pool, self, self->init.base_ptr->mns_inst_id); + + Pmch_RegisterReceiver(self->init.channel_ptr, self->config.fifo_id, &Fifo_OnRx, self); +} + +/*! \brief Initializes flow control and related counters + * \param self The instance + * \param tx_sid_complete Reference to initialization data + * \param tx_credits Number of credits for Tx + */ +static void Fifo_InitCounters(CPmFifo *self, uint8_t tx_sid_complete, uint8_t tx_credits) +{ + self->rx.busy_num = 0U; + self->rx.expected_sid = tx_sid_complete + 1U; + self->rx.ack_last_ok_sid = tx_sid_complete; + + self->tx.credits = tx_credits; + self->tx.sid_next_to_use = tx_sid_complete +1U; + self->tx.sid_last_completed = tx_sid_complete; + + self->tx.failure_status = 0U; + self->tx.failure_sid = 0U; + + self->tx.current_sid = tx_sid_complete; + self->tx.current_type = PMP_STATUS_TYPE_FLOW; + self->tx.current_code = (uint8_t)PMP_STATUS_CODE_SUCCESS; +} + +/*! \brief Adds an observer of synchronization state changes + * \param self The instance + * \param obs_ptr The observer. The notification result type is \ref Pmp_FifoId_t. + */ +void Fifo_AddStateObserver(CPmFifo *self, CObserver *obs_ptr) +{ + (void)Sub_AddObserver(&self->sync_state_subject, obs_ptr); +} + +/*! \brief Removes an observer of synchronization state changes + * \param self The instance + * \param obs_ptr The observer. + */ +void Fifo_RemoveStateObserver(CPmFifo *self, CObserver *obs_ptr) +{ + (void)Sub_RemoveObserver(&self->sync_state_subject, obs_ptr); +} + +/*! \brief Stops execution of a FIFO and notifies sync lost if necessary + * \param self The instance + * \param new_state The new synchronization state + * \param allow_notification Set to \c false in order to avoid recursion + */ +void Fifo_Stop(CPmFifo *self, Fifo_SyncState_t new_state, bool allow_notification) +{ + bool notify = false; + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Stop(): FIFO: %u, state: %u, new_state: %u", 3U, self->config.fifo_id, self->sync_state, new_state)); + + if (self->sync_state != new_state) + { + notify = true; + } + + self->sync_state = new_state; + self->tx.credits = 0U; + + if (self->wd.timer_value != 0U) + { + Tm_ClearTimer(&self->init.base_ptr->tm, &self->wd.timer); + } + + if ((notify != false) && (allow_notification != false)) + { + Sub_Notify(&self->sync_state_subject, &self->config.fifo_id); + } +} + +/*! \brief Releases all external references + * \details It is important to call Fifo_Stop() prior to this functions. The low-level driver + * must be stopped as well to avoid concurrent access to message objects. + * \param self The instance + */ +void Fifo_Cleanup(CPmFifo *self) +{ + CMessage *msg_ptr = NULL; + CDlNode *node_ptr = NULL; + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (self->sync_state == FIFO_S_UNSYNCED_INIT)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Cleanup(): FIFO: %u", 1U, self->config.fifo_id)); + + /* cleanup pending queue */ + for (node_ptr = Dl_PopHead(&self->tx.pending_q); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.pending_q)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + Msg_NotifyTxStatus(msg_ptr, MNS_MSG_STAT_ERROR_SYNC); + Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(msg_ptr)); + Msg_SetLldHandle(msg_ptr, NULL); /* remove link to LLD message object */ + } + + /* cleanup waiting queue */ + for (node_ptr = Dl_PopHead(&self->tx.waiting_queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.waiting_queue)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + Msg_NotifyTxStatus(msg_ptr, MNS_MSG_STAT_ERROR_SYNC); + } + + /* cleanup Rx queue */ + for (node_ptr = Dl_PopHead(&self->rx.queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->rx.queue)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr); + } + + Srv_ClearEvent(&self->service, FIFO_SE_ALL); +} + + +/*! \brief Service function of FIFO + * \details The processing order of Rx followed by Tx is important for Fifo_RxProcessCommand() + * \param self The instance + */ +static void Fifo_Service(void *self) +{ + CPmFifo *self_ = (CPmFifo*)self; + Srv_Event_t event_mask; + + Srv_GetEvent(&self_->service, &event_mask); + + if(FIFO_SE_RX_SERVICE == (event_mask & FIFO_SE_RX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, FIFO_SE_RX_SERVICE); + Fifo_RxService(self_); + } + + if(FIFO_SE_TX_APPLY_STATUS == (event_mask & FIFO_SE_TX_APPLY_STATUS)) + { + Srv_ClearEvent(&self_->service, FIFO_SE_TX_APPLY_STATUS); + Fifo_TxApplyCurrentStatus(self_); + } + + if(FIFO_SE_TX_SERVICE == (event_mask & FIFO_SE_TX_SERVICE)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->service, FIFO_SE_TX_SERVICE); + Fifo_TxService(self_); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Enqueues a message for transmission + * \param self The instance + * \param msg_ptr The Tx message object + * \param bypass Use \c true if the message shall bypass all other messages + * in the FIFO. Otherwise \c false. + */ +void Fifo_Tx(CPmFifo *self, CMessage *msg_ptr, bool bypass) +{ + uint8_t *msg_hdr_ptr = NULL; + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (msg_ptr != NULL)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Tx(): FIFO: %u, msg_ptr: 0x%p, FuncId: 0x%X, queued Tx message", 3U, self->config.fifo_id, msg_ptr, msg_ptr->pb_msg.id.function_id)); + + Msg_PullHeader(msg_ptr, self->tx.encoder_ptr->msg_hdr_sz); + msg_hdr_ptr = Msg_GetHeader(msg_ptr); + self->tx.encoder_ptr->encode_fptr(Msg_GetMostTel(msg_ptr), msg_hdr_ptr); + + if (bypass == false) + { + Dl_InsertTail(&self->tx.waiting_queue, Msg_GetNode(msg_ptr)); /* enqueue message for asynchronous transmission */ + } + else + { + Fifo_TxEnqueueBypassMsg(self, &self->tx.waiting_queue, msg_ptr); /* queue before first non-bypass message */ + } + + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); +} + +/*! \brief Enqueues a bypass message between the last bypass and the first regular message in a queue + * \param self The instance + * \param q_ptr The message queue + * \param msg_ptr The Tx message object + */ +static void Fifo_TxEnqueueBypassMsg(CPmFifo *self, CDlList *q_ptr, CMessage *msg_ptr) +{ + CDlNode *node_ptr = Dl_Foreach(q_ptr, &Fifo_FindFirstRegularMsg, NULL); /* find first "non-bypass" message */ + Msg_SetTxBypass(msg_ptr, true); /* mark new message as bypass message */ + + if (node_ptr == NULL) /* no message or only bypass messages found */ + { + Dl_InsertTail(&self->tx.waiting_queue, Msg_GetNode(msg_ptr)); /* enqueue message to tail */ + } + else /* first "non-bypass" message is found */ + { /* insert the bypass message before the first regular message found */ + Dl_InsertBefore(&self->tx.waiting_queue, node_ptr, Msg_GetNode(msg_ptr)); + } +} + +/*! \brief Required as "for-each" function to find the first "regular message" + * \param d_ptr Points to a message object in the queue + * \param ud_ptr Unused data reference, always \c NULL + * \return Returns \c true if a regular (non-bypass) message is found. + */ +static bool Fifo_FindFirstRegularMsg(void *d_ptr, void *ud_ptr) +{ + bool ret = true; + MISC_UNUSED(ud_ptr); + + if (Msg_IsTxBypass((CMessage*)d_ptr)) + { + ret = false; + } + + return ret; +} + +/*! \brief Processing of data, status and command messages + * \param self The instance + */ +static void Fifo_TxService(CPmFifo *self) +{ + Fifo_TxProcessCommand(self); + Fifo_TxProcessStatus(self); + Fifo_TxProcessData(self); +} + +/*! \brief Processing of status messages + * \param self The instance + */ +static void Fifo_TxProcessStatus(CPmFifo *self) +{ + if (Pmcmd_IsTriggered(&self->rx.status) != false) + { + if (Pmcmd_Reserve(&self->rx.status) != false) + { + Pmcmd_SetTrigger(&self->rx.status, false); + self->rx.ack_last_ok_sid = (self->rx.expected_sid - self->rx.busy_num) - 1U; + self->rx.wait_processing = false; + + if (self->rx.busy_num == 0U) /* currently no processing of data messages active */ + { /* notify the latest with SUCCESS */ + Pmcmd_UpdateContent(&self->rx.status, self->rx.expected_sid - 1U, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_SUCCESS); + } + else /* message processing is active */ + { /* notify code busy according to remaining credits */ + Pmcmd_UpdateContent(&self->rx.status, self->rx.expected_sid - self->rx.busy_num, PMP_STATUS_TYPE_FLOW, PMP_STATUS_CODE_BUSY); + } + + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->rx.status)); + } + } +} + +/*! \brief Processing of queued data messages + * \param self The instance + */ +static void Fifo_TxProcessData(CPmFifo *self) +{ + /* process all queued messages as long as credits are available, + * process all queued messages if FIFO is not synced + */ + while ((self->tx.cancel_all_running == false) && (self->tx.credits > 0U)) + { + CMessage *msg_ptr = NULL; + CDlNode *node_ptr = NULL; + uint8_t *msg_hdr_ptr = NULL; + Lld_IntTxMsg_t *lld_tx_ptr = NULL; + + node_ptr = Dl_PopHead(&self->tx.waiting_queue); /* get message node */ + if (NULL == node_ptr) + { + msg_ptr = NULL; /* stop processing - no further messages in queue */ + break; + } + + msg_ptr = (CMessage*)Dln_GetData(node_ptr); /* get message object */ + + if (self->sync_state != FIFO_S_SYNCED) + { + Msg_NotifyTxStatus(msg_ptr, MNS_MSG_STAT_ERROR_SYNC); /* notify sync error while not synced */ + } + else + { + lld_tx_ptr = Lldp_GetTxFromPool(&self->tx.lld_pool); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (msg_ptr != NULL)); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (lld_tx_ptr != NULL)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxProcessData(): FIFO: %u, msg_ptr: 0x%p, FuncId: 0x%X, SID: 0x%02X, queued Tx message", 4U, self->config.fifo_id, msg_ptr, msg_ptr->pb_msg.id.function_id, self->tx.sid_next_to_use)); + + Msg_SetLldHandle(msg_ptr, lld_tx_ptr); /* link message objects */ + lld_tx_ptr->msg_ptr = msg_ptr; + + Msg_PullHeader(msg_ptr, self->tx.encoder_ptr->pm_hdr_sz); /* get PM header pointer */ + msg_hdr_ptr = Msg_GetHeader(msg_ptr); + + { + uint8_t tel_length = Msg_GetMostTel(msg_ptr)->tel.tel_len; + self->tx.pm_header.pml = (Msg_GetHeaderSize(msg_ptr) + tel_length) - 2U; + } + + self->tx.pm_header.sid = self->tx.sid_next_to_use; /* assign SeqID */ + self->tx.sid_next_to_use++; + + Pmh_BuildHeader(&self->tx.pm_header, msg_hdr_ptr); /* build PM header */ + lld_tx_ptr->lld_msg.memory_ptr = Msg_GetMemTx(msg_ptr); + + Msg_SetTxActive(msg_ptr, true); + Dl_InsertTail(&self->tx.pending_q, Msg_GetNode(msg_ptr)); + + Pmch_Transmit(self->init.channel_ptr, (Mns_Lld_TxMsg_t*)(void*)lld_tx_ptr); + + self->tx.credits--; + } + } +} + +/*! \brief Processing of status messages + * \param self The instance + */ +static void Fifo_TxProcessCommand(CPmFifo *self) +{ + if (Pmcmd_IsTriggered(&self->tx.sync_cmd) != false) + { + if (Pmcmd_Reserve(&self->tx.sync_cmd) != false) + { + Pmcmd_SetTrigger(&self->tx.sync_cmd, false); + + if (self->sync_state == FIFO_S_SYNCING) + { + self->sync_cnt++; + Pmcmd_SetContent(&self->tx.sync_cmd, self->sync_cnt, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_SYNC, self->sync_params, 4U); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.sync_cmd)); + } + else if (self->sync_state == FIFO_S_UNSYNCING) + { + Pmcmd_SetContent(&self->tx.sync_cmd, 0U, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_UNSYNC, NULL, 0U); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.sync_cmd)); + } + else + { + Pmcmd_Release(&self->tx.sync_cmd); + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + } +} + +/*! \brief Releases a LLD Tx message object + * \param self The instance + * \param handle_ptr The unused LLD Tx message object + * \details If Fifo_TxApplyStatus() is waiting for a message object + * being released + */ +void Fifo_TxOnRelease(void *self, Mns_Lld_TxMsg_t *handle_ptr) +{ + CPmFifo *self_ = (CPmFifo*)self; + Lld_IntTxMsg_t *tx_ptr = (Lld_IntTxMsg_t*)(void*)handle_ptr; + + if (tx_ptr->msg_ptr != NULL) + { + Msg_SetTxActive(tx_ptr->msg_ptr, false); + } + else + { + TR_FAILED_ASSERT(self_->init.base_ptr->mns_inst_id, "[FIFO]"); + } + + if (self_->tx.status_waiting_release != false) + { + self_->tx.status_waiting_release = false; + Srv_SetEvent(&self_->service, (FIFO_SE_TX_APPLY_STATUS | FIFO_SE_TX_SERVICE)); + } +} + +/*! \brief Triggers a command CANCEL_ALL and stops further Tx processing + * \details CANCEL_ALL shall be called only, if the front-most pending message + * has followers (is segmented, i.e. \c cancel_id > 0). Use command CANCEL + * if the front-most message has no followers (\c cancel_id == NULL). + * \param self The instance + * \param failure_sid The failure sid + * \param failure_code The failure code reported by the INIC + */ +static void Fifo_TxExecuteCancelAll(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxExecuteCancelAll(): FIFO: %u, SID: %u, Code: %u", 3U, self->config.fifo_id, failure_sid, failure_code)); + + if (Pmcmd_Reserve(&self->tx.cancel_cmd) != false) /* prepare cancel command */ + { + Pmcmd_UpdateContent(&self->tx.cancel_cmd, self->tx.current_sid, + PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL_ALL); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.cancel_cmd)); + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* Unable to reserve cancel command */ + } + + self->tx.cancel_all_running = true; + self->tx.failure_sid = failure_sid; + self->tx.failure_status = failure_code; +} + +/*! \brief Shall be called if the command CANCEL_ALL was processed completely + * \param self The instance + * \details Since the CANCEL_ALL is used to cancel the front-most message and + * all of its followers (same cancel_id) + + for mid-level retries, the canceled messages + * are moved from the processing_q to the waiting_q again. The MLR timer is + * started. As soon as the timer elapses, Tx processing is continued again. + * If the front-most message has a follower id, all pending messages are + * moved to the waiting queue and all messages with the same follower id + * are notified as failed. + */ +static void Fifo_TxFinishedCancelAll(CPmFifo *self) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxFinishedCancelAll(): FIFO: %u, FailureStatus: %u,", 2U, self->config.fifo_id, self->tx.failure_status)); + + if (self->tx.failure_status != 0U) /* avoid multiple execution of the same CANCELED status */ + { /* and all of its followers */ + uint8_t follower_id = Fifo_TxPendingGetFollowerId(self); + Fifo_TxRestorePending(self); /* move remaining messages to waiting_q */ + Fifo_TxCancelFollowers(self, follower_id, (Mns_MsgTxStatus_t)self->tx.failure_status); + /* notify front-most and message and all of its followers */ + self->tx.cancel_all_running = false; /* continue with Tx processing */ + self->tx.failure_sid = 0U; + self->tx.failure_status = 0U; + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); + } +} + +/*! \brief Triggers a command CANCEL while Tx processing continues + * \param self The instance + * \param failure_sid The failure sid + * \param failure_code The failure code reported by the INIC + */ +static void Fifo_TxExecuteCancel(CPmFifo *self, uint8_t failure_sid, uint8_t failure_code) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxExecuteCancel(): FIFO: %u, SID: %u, Code: %u", 3U, self->config.fifo_id, failure_sid, failure_code)); + + if (Pmcmd_Reserve(&self->tx.cancel_cmd) != false) + { + Pmcmd_UpdateContent(&self->tx.cancel_cmd, self->tx.current_sid, + PMP_CMD_TYPE_MSG_ACTION, PMP_CMD_CODE_ACTION_CANCEL); + Pmch_Transmit(self->init.channel_ptr, Pmcmd_GetLldTxObject(&self->tx.cancel_cmd)); + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* Unable to reserve cancel command */ + } + + self->tx.cancel_all_running = false; + self->tx.failure_sid = failure_sid; + self->tx.failure_status = failure_code; +} + +/*! \brief Checks if the LLD has released all messages in the pending_q + * \param self The instance + * \return Returns \c true if all messages are released by the LLD, otherwise \c false. + */ +static bool Fifo_TxHasAccessPending(CPmFifo *self) +{ + bool ret = true; + CDlNode *node_ptr = Dl_PeekTail(&self->tx.pending_q); /* if the tail is not active, then all */ + /* pending message are not active */ + if (node_ptr != NULL) + { + CMessage *msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + if (Msg_IsTxActive(msg_ptr) != false) + { + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxHasAccessPending(): FIFO: %u, msg_ptr: 0x%p, still in use", 2U, self->config.fifo_id, msg_ptr)); + self->tx.status_waiting_release = true; + ret = false; + } + } + + return ret; +} + +/*! \brief Moves all pending messages to the waiting_q + * \details All messages from pending_q will be moved to the waiting_g and + * all consumed credits are restored. The message objects are restored + * to the queue in the same order as they have been forwarded to the LLD. + * This method is typically called to restore the waiting_q in the correct + * order before notifying a + * \param self The instance + */ +static void Fifo_TxRestorePending(CPmFifo *self) +{ + /* take tail from pending_q to the head of waiting_q */ + CMessage *msg_ptr = NULL; + CDlNode *node_ptr = NULL; + + /* cleanup pending queue */ + for (node_ptr = Dl_PopTail(&self->tx.pending_q); node_ptr != NULL; node_ptr = Dl_PopTail(&self->tx.pending_q)) + { + msg_ptr = (CMessage*)Dln_GetData(node_ptr); + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxRestorePending(): FIFO: %u, msg_ptr: 0x%p", 2U, self->config.fifo_id, msg_ptr)); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (Msg_IsTxActive(msg_ptr) == false)); + + self->tx.sid_last_completed++; + self->tx.credits++; + Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(msg_ptr)); + Msg_SetLldHandle(msg_ptr, NULL); /* remove link to LLD message object */ + Msg_PushHeader(msg_ptr, self->tx.encoder_ptr->pm_hdr_sz); /* set index to position of message header */ + Dl_InsertHead(&self->tx.waiting_queue, node_ptr); /* enqueue message to waiting_q */ + } +} + +/*! \brief Retrieves the follower id of the front-most pending message + * \param self The instance + * \return Returns the follower id of the front-most pending message. + */ +static uint8_t Fifo_TxPendingGetFollowerId(CPmFifo *self) +{ + CDlNode *node_ptr; + CMessage *tx_ptr; + uint8_t ret = 0U; + + node_ptr = Dl_PeekHead(&self->tx.pending_q); + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (node_ptr != NULL)); + + if (node_ptr != NULL) + { + tx_ptr = (CMessage*)Dln_GetData(node_ptr); + ret = tx_ptr->pb_msg.opts.cancel_id; + } + + return ret; +} + +/*! \brief Aborts the transmission of all messages in the waiting_q with a given follower id + * \param self The instance + * \param follower_id The follower id a message needs to have to be canceled + * \param status The transmission status that shall be notified + */ +static void Fifo_TxCancelFollowers(CPmFifo *self, uint8_t follower_id, Mns_MsgTxStatus_t status) +{ + CDlNode *node_ptr; + CDlList temp_queue; + + Dl_Ctor(&temp_queue, self->init.base_ptr->mns_inst_id); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxCancelFollowers(): FIFO: %u: FollowerId: %u", 2U, self->config.fifo_id, follower_id)); + + for (node_ptr = Dl_PopHead(&self->tx.waiting_queue); node_ptr != NULL; node_ptr = Dl_PopHead(&self->tx.waiting_queue)) + { + CMessage *tx_ptr = (CMessage*)Dln_GetData(node_ptr); + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (Msg_GetLldHandle(tx_ptr) == NULL)); + + if (tx_ptr->pb_msg.opts.cancel_id == follower_id) + { + Msg_NotifyTxStatus(tx_ptr, status); /* notify failed transmission of message and all followers */ + } + else + { + Dl_InsertTail(&temp_queue, node_ptr); /* add to temporary queue and keep order of messages */ + } + } + + if (Dl_GetSize(&temp_queue) > 0U) /* restore temp_queue to waiting_q */ + { + Dl_AppendList(&self->tx.waiting_queue, &temp_queue);/* temp_queue will be empty now */ + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx Message Processing */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves the number of (implicit) acknowledges that are related to one SID + * \param self The instance + * \param sid The sequence ID + * \return The number of implicit acknowledges that are related to the SID + */ +static uint8_t Fifo_TxGetValidAcknowledges(CPmFifo *self, uint8_t sid) +{ + uint8_t diff_s = (uint8_t)(sid - self->tx.sid_last_completed); /* number of implicit acknowledged data */ + uint8_t diff_b = (uint8_t)(self->tx.sid_next_to_use - self->tx.sid_last_completed); /* number of "sent but un-acknowledged data" + 1 */ + + if (diff_b <= diff_s) /* check valid acknowledges */ + { + diff_s = 0U; + } + + return diff_s; +} + + +/*! \brief Checks id an incoming SID of a status message is valid. + * \param self The instance + * \param sid The sequence ID + * \return Returns \c true if the SID is valid, otherwise \c false. + */ +static bool Fifo_TxIsIncomingSidValid(CPmFifo *self, uint8_t sid) +{ + bool ret = false; + uint8_t diff_s = (uint8_t)(sid - self->tx.sid_last_completed); /* number of implicit acknowledged data */ + uint8_t diff_b = (uint8_t)(self->tx.sid_next_to_use - self->tx.sid_last_completed); /* number of "sent but un-acknowledged data" + 1 */ + uint8_t diff_p = (uint8_t)(self->tx.current_sid - self->tx.sid_last_completed); /* pending/known acknowledges */ + + if (diff_b > diff_s) /* check if SID fits in valid range */ + { + if (diff_s >= diff_p) /* avoid overwriting with smaller values */ + { + ret = true; + } + } + + return ret; +} + +/*! \brief Implicitly notifies transmission status to calling classes + * \param self The instance + * \param sid The sequence ID until the status shall be notified + * \param status The status which is notified + * \return Returns \c true if all desired messages had been notified, + * otherwise \c false. + */ +static bool Fifo_TxNotifyStatus(CPmFifo *self, uint8_t sid, Mns_MsgTxStatus_t status) +{ + bool ret = true; + uint8_t acks = Fifo_TxGetValidAcknowledges(self, sid); + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, calculated_acks: %u", 2U, self->config.fifo_id, acks)); + + while (acks > 0U) + { + CDlNode *node_ptr = Dl_PopHead(&self->tx.pending_q); + + if (node_ptr != NULL) + { + CMessage *tx_ptr = (CMessage*)node_ptr->data_ptr; + + if (!Msg_IsTxActive(tx_ptr)) + { + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (tx_ptr != NULL)); + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, FuncId: 0x%X, notified status: %u", 3U, self->config.fifo_id, tx_ptr->pb_msg.id.function_id, status)); + Msg_NotifyTxStatus(tx_ptr, status); + Lldp_ReturnTxToPool(&self->tx.lld_pool, (Lld_IntTxMsg_t*)Msg_GetLldHandle(tx_ptr)); + Msg_SetLldHandle(tx_ptr, NULL); /* remove link to LLD message object */ + + self->tx.credits++; /* increment credits */ + self->tx.sid_last_completed++; /* update last acknowledge SID */ + } + else + { + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxNotifyStatus(): FIFO: %u, LLD objects still occupied", 1U, self->config.fifo_id)); + Dl_InsertHead(&self->tx.pending_q, node_ptr); + self->tx.status_waiting_release = true; + ret = false; + break; + } + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* not yet handled */ + /* trigger sync again */ + } + + acks--; + } + + return ret; +} + +/*! \brief Updates the current Tx status with the content of a received FIFO status + * \param self The instance + * \param sid The sequence id of the FIFO status + * \param type The type of the FIFO status. Valid types are only: + * - PMP_STATUS_TYPE_FLOW + * - PMP_STATUS_TYPE_FAILURE + * \param code The code of the FIFO status + */ +static void Fifo_TxUpdateCurrentStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code) +{ + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (type == (uint8_t)PMP_STATUS_TYPE_FAILURE) || (type == (uint8_t)PMP_STATUS_TYPE_FLOW)); + if (Fifo_TxIsIncomingSidValid(self, sid)) /* is new or updating status */ + { + self->tx.current_sid = sid; /* update current status */ + self->tx.current_type = (Pmp_StatusType_t)type; + self->tx.current_code = code; + } + else + { + TR_ERROR((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxUpdateCurrentStatus(): FIFO: %u, sid: %u, type: %u, code: %u, INVALID SID", 4U, self->config.fifo_id, sid, type, code)); + } +} + +/*! \brief Analyses the current Tx status, tries to notify statuses to the transmitter and triggers + * retry/cancel actions. + * \param self The instance + */ +static void Fifo_TxApplyCurrentStatus(CPmFifo *self) +{ + if ((self->tx.cancel_all_running == false) && (self->tx.failure_status != 0U)) /* Command(CANCEL) is pending */ + { + if (Fifo_TxGetValidAcknowledges(self, self->tx.current_sid) > 1U) /* ?>=1? "single cancel" is valid and implicit */ + { + if (Fifo_TxNotifyStatus(self, self->tx.failure_sid, (Mns_MsgTxStatus_t)self->tx.failure_status)) + { + self->tx.failure_status = 0U; /* implicit canceled stops retries */ + self->tx.failure_sid = 0U; + } + } + } + + if ((self->tx.current_type == PMP_STATUS_TYPE_FAILURE) && (self->tx.status_waiting_release == false)) + { + if (self->tx.cancel_all_running == false) + { + if (Fifo_TxNotifyStatus(self, self->tx.current_sid - 1U, MNS_MSG_STAT_OK) != false) + { + /* important: failed message now is front-most message in the tx.pending_q, */ + /* any implicit acknowledge was done before */ + if (self->tx.failure_status == 0U) /* failure not yet handled - avoid multiple calls */ + { + if (Fifo_TxPendingGetFollowerId(self) == 0U) + { + Fifo_TxExecuteCancel(self, self->tx.current_sid, self->tx.current_code); /* execute simple cancel */ + } + else + { + Fifo_TxExecuteCancelAll(self, self->tx.current_sid, self->tx.current_code); /* execute cancel all */ + /* self->tx.cancel_all_running now is 'true' and Tx is stopped */ + } + } + } + } + } + + if ((self->tx.current_type == PMP_STATUS_TYPE_FLOW) && (self->tx.status_waiting_release == false)) + { + if ((uint8_t)PMP_STATUS_CODE_SUCCESS == self->tx.current_code) /* acknowledge pending messages */ + { + /* no further retries possible */ + (void)Fifo_TxNotifyStatus(self, self->tx.current_sid, MNS_MSG_STAT_OK); + } + else if ((uint8_t)PMP_STATUS_CODE_CANCELED == self->tx.current_code) + { + if (self->tx.cancel_all_running != false) + { + /* wait until the last SID is notified */ + if (self->tx.current_sid == (uint8_t)(self->tx.sid_next_to_use - (uint8_t)1U)) + { + /* cancel done if none of pending messages is active */ + if (Fifo_TxHasAccessPending(self) != false) + { + Fifo_TxFinishedCancelAll(self); + } + } + } + else if (Fifo_TxNotifyStatus(self, self->tx.current_sid, (Mns_MsgTxStatus_t)self->tx.failure_status)) + { + self->tx.failure_status = 0U; + self->tx.failure_sid = 0U; + } + } + else + { + if (Fifo_TxNotifyStatus(self, self->tx.current_sid - 1U, MNS_MSG_STAT_OK)) /* just implicitly acknowledge preceding message */ + { + if ((uint8_t)PMP_STATUS_CODE_NACK == self->tx.current_code) + { + Fifo_Stop(self, FIFO_S_UNSYNCED_INIT, true); + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); + } + } + } + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Receives a message on the respective FIFO + * \param self The instance + * \param msg_ptr Reference to the Rx message + */ +static void Fifo_OnRx(void *self, CMessage *msg_ptr) +{ + CPmFifo *self_ = (CPmFifo*)self; + Dl_InsertTail(&self_->rx.queue, Msg_GetNode(msg_ptr)); /* enqueue in rx_queue */ + Srv_SetEvent(&self_->service, (FIFO_SE_RX_SERVICE | FIFO_SE_TX_APPLY_STATUS | FIFO_SE_TX_SERVICE)); +} + +/*! \brief Processes the Rx queue completely and triggers possible Tx events + * \param self The instance + */ +static void Fifo_RxService(CPmFifo *self) +{ + while (false == self->rx.wait_processing) /* process all Rx messages if possible */ + { + CMessage *msg_ptr; + uint8_t *header_ptr; + Pmp_MsgType_t type; + bool ok; + + bool free_msg = true; /* default: free every status or command message */ + CDlNode *node_ptr = Dl_PopHead(&self->rx.queue); + + if (NULL == node_ptr) + { + msg_ptr = NULL; /* stop processing - no further messages in queue */ + break; + } + + msg_ptr = (CMessage*)node_ptr->data_ptr; + header_ptr = Msg_GetHeader(msg_ptr); + type = Pmp_GetMsgType(header_ptr); + ok = Pmp_VerifyHeader(header_ptr, MSG_SIZE_RSVD_BUFFER); + + if (ok != false) + { + switch (type) + { + case PMP_MSG_TYPE_CMD: + Fifo_RxProcessCommand(self, msg_ptr); + break; + case PMP_MSG_TYPE_STATUS: + Fifo_RxProcessStatus(self, msg_ptr); + break; + case PMP_MSG_TYPE_DATA: + free_msg = Fifo_RxProcessData(self, msg_ptr); /* important: message can be freed */ + break; /* synchronously */ + default: + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* unknown FIFO message type */ + break; + } + } + else + { + TR_FAILED_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]"); /* invalid message header */ + } + + if (free_msg != false) + { + Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr); + } + } +} + +/*! \brief Evaluates the trigger condition to transmit a Rx status + * \details Needs to be called before and after processing Rx data messages + * \param self The instance + */ +static void Fifo_RxCheckStatusTrigger(CPmFifo *self) +{ + /* calculate the number of credits the INIC has consumed */ + /* if less messages are processing, the freed can be acknowledged */ + uint8_t consumed_inic_credits = (self->rx.expected_sid - self->rx.ack_last_ok_sid) - 1U; + uint8_t possible_acks = consumed_inic_credits - self->rx.busy_num; + + if ((consumed_inic_credits >= self->rx.ack_threshold) && (possible_acks > 0U)) + { + if (Pmcmd_IsTriggered(&self->rx.status) == false) + { + Pmcmd_SetTrigger(&self->rx.status, true); /* INIC might run out of credits */ + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); + } + } +} + +/*! \brief This function shall be called before processing a valid FIFO data message + * \param self The instance + */ +static void Fifo_RxGetCredit(CPmFifo *self) +{ + self->rx.busy_num++; + Fifo_RxCheckStatusTrigger(self); +} + +/*! \brief This function shall be called after processing a valid FIFO data message + * \details It is important to call this function after the message object is freed, + * so that the flow control can be updated. + * \param self The instance + */ +static void Fifo_RxReleaseCredit(CPmFifo *self) +{ + self->rx.busy_num--; + Fifo_RxCheckStatusTrigger(self); +} + +/*! \brief Releases a FIFO data message which was received and forwarded by the FIFO + * \details The function returns the message to the channel's Rx message pool and + * has to update the number of credits (processing handles). + * A FIFO data message is initially allocated from the channel's Rx message pool. + * When processing the handle the determined FIFO need to calculate the amount of + * credits. When freeing the message the handle needs to be returned to the channel's + * Rx pool again and the FIFO needs to refresh the status and credits calculation. + * Therefore the message has to be freed to the respective FIFO again. + * \param self The instance + * \param msg_ptr The Rx data message + */ +void Fifo_RxReleaseMsg(CPmFifo *self, CMessage *msg_ptr) +{ + Pmch_ReturnRxToPool(self->init.channel_ptr, msg_ptr); + Fifo_RxReleaseCredit(self); +} + +/*! \brief Processes an Rx data message + * \param self The instance + * \param msg_ptr The Rx data message + * \return \c true if the message object is no longer needed. + * Otherwise \c false. + */ +static bool Fifo_RxProcessData(CPmFifo *self, CMessage *msg_ptr) +{ + bool free_msg = true; + uint8_t content_header_sz = 0U; + uint8_t sid = 0U; + uint8_t *header_ptr = Msg_GetHeader(msg_ptr); + sid = Pmp_GetSid(header_ptr); + + if (self->sync_state != FIFO_S_SYNCED) + { /* discard Rx messages while FIFO is not synced */ + TR_ERROR((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessData(): FIFO: %u, state: %u, discards Rx message with SID=0x%02X while not synced (warning)", 3U, self->config.fifo_id, self->sync_state, sid)); + } + else if (sid == self->rx.expected_sid) /* check if SID is ok */ + { + uint8_t pm_header_sz = Pmp_GetPmhl(header_ptr) + 3U; + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (pm_header_sz == self->rx.encoder_ptr->pm_hdr_sz)); + + self->rx.expected_sid++; /* update SID */ + content_header_sz = self->rx.encoder_ptr->msg_hdr_sz; + + /* parasoft suppress item MISRA2004-17_4 reason "necessary offset usage" */ + self->rx.encoder_ptr->decode_fptr(Msg_GetMostTel(msg_ptr), &(header_ptr[pm_header_sz])); + /* parasoft unsuppress item MISRA2004-17_4 reason "necessary offset usage" */ + + Msg_ReserveHeader(msg_ptr, content_header_sz + pm_header_sz); + Msg_PullHeader(msg_ptr, content_header_sz + pm_header_sz); + + if (Msg_VerifyContent(msg_ptr)) + { + if (self->rx.on_complete_fptr != NULL) + { + (void)Fifo_RxGetCredit(self); + free_msg = false; /* callback is responsible to free the message */ + self->rx.on_complete_fptr(self->rx.on_complete_inst, msg_ptr); + /* Fifo_RxReleaseCredit() is called when message is freed */ + } + } + } + else + { + TR_ERROR((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessData(): FIFO: %u, state: %u, discards Rx message with unexpected SID=0x%02X (warning)", 3U, self->config.fifo_id, self->sync_state, sid)); + } + + return free_msg; +} + +/*! \brief Processes an Rx status message + * \param self The instance + * \param msg_ptr The Rx status message + */ +static void Fifo_RxProcessStatus(CPmFifo *self, CMessage *msg_ptr) +{ + CPmh pm_header; + uint8_t current_sid; + uint8_t current_type; + uint8_t current_code; + uint8_t *header_ptr = Msg_GetHeader(msg_ptr); + + Pmh_DecodeHeader(&pm_header, header_ptr); + current_sid = pm_header.sid; + current_type = (uint8_t)Pmh_GetExtStatusType(&pm_header); + current_code = (uint8_t)Pmh_GetExtStatusCode(&pm_header); + + self->wd.request_started = false; /* status finishes a wd request */ + + switch ((Pmp_StatusType_t)current_type) + { + case PMP_STATUS_TYPE_FAILURE: + Fifo_TxUpdateCurrentStatus(self, current_sid, current_type, Fifo_RxCheckFailureCode(self, current_code)); /* just update status type FAILURE */ + break; + case PMP_STATUS_TYPE_FLOW: + Fifo_TxUpdateCurrentStatus(self, current_sid, current_type, current_code); /* just update status type FLOW (codes: BUSY, NACK, SUCCESS, CANCELED) */ + break; + case PMP_STATUS_TYPE_SYNCED: + Fifo_RxProcessSyncStatus(self, current_sid, current_type, current_code, header_ptr); + break; + case PMP_STATUS_TYPE_UNSYNCED_BSY: + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessStatus(): FIFO: %u, state: %u, received UNSYNCED_BSY", 2U, self->config.fifo_id, self->sync_state)); + if (self->sync_state != FIFO_S_SYNCING) + { + Fifo_Stop(self, FIFO_S_UNSYNCED_BUSY, true); + } + break; + case PMP_STATUS_TYPE_UNSYNCED_RDY: + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessStatus(): FIFO: %u, state: %u, received UNSYNCED_RDY", 2U, self->config.fifo_id, self->sync_state)); + if (self->sync_state == FIFO_S_SYNCING) + { + if (current_code == (uint8_t)PMP_UNSYNC_R_COMMAND) + { + Fifo_Synchronize(self); /* retry synchronization */ + } + } + else + { + Fifo_Stop(self, FIFO_S_UNSYNCED_READY, true); + } + break; + default: + /* ignore status */ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_RxProcessStatus(): FIFO: %u, state: %u, received UNKNOWN TYPE: %u", 3U, self->config.fifo_id, self->sync_state, current_type)); + break; + } +} + +/*! \brief Checks failure_code and sets invalid code to MNS_MSG_STAT_ERROR_UNKNOWN + * \param self The instance + * \param failure_code The INIC failure code + * \return Returns the checked failure code + */ +static uint8_t Fifo_RxCheckFailureCode(CPmFifo *self, uint8_t failure_code) +{ + uint8_t ret; + MISC_UNUSED(self); + + switch (failure_code) + { + case (uint8_t)MNS_MSG_STAT_ERROR_CFG_NO_RCVR: + case (uint8_t)MNS_MSG_STAT_ERROR_BF: + case (uint8_t)MNS_MSG_STAT_ERROR_CRC: + case (uint8_t)MNS_MSG_STAT_ERROR_ID: + case (uint8_t)MNS_MSG_STAT_ERROR_ACK: + case (uint8_t)MNS_MSG_STAT_ERROR_TIMEOUT: + case (uint8_t)MNS_MSG_STAT_ERROR_FATAL_WT: + case (uint8_t)MNS_MSG_STAT_ERROR_FATAL_OA: + case (uint8_t)MNS_MSG_STAT_ERROR_NA_TRANS: + case (uint8_t)MNS_MSG_STAT_ERROR_NA_OFF: + ret = failure_code; + break; + default: + ret = (uint8_t)MNS_MSG_STAT_ERROR_UNKNOWN; + break; + } + + return ret; +} + +/*! \brief Processes an Rx command message + * \param self The instance + * \param msg_ptr The Rx command message + */ +static void Fifo_RxProcessCommand(CPmFifo *self, CMessage *msg_ptr) +{ + MISC_UNUSED(msg_ptr); + /* be aware that PMHL might vary */ + Pmcmd_SetTrigger(&self->rx.status, true); /* just trigger latest Rx status now */ +} + +/*! \brief Processes a status SYNCED from the INIC + * \param self The instance + * \param sid The sid of the sync status + * \param type The type of the sync status + * \param code The code of the sync status + * \param header_ptr Pointer to the raw port message + * \return The current synchronization state + */ +static void Fifo_RxProcessSyncStatus(CPmFifo *self, uint8_t sid, uint8_t type, uint8_t code, uint8_t *header_ptr) +{ + bool check = false; + uint8_t tx_credits = 0U; + + TR_ASSERT(self->init.base_ptr->mns_inst_id, "[FIFO]", (type==(uint8_t)PMP_STATUS_TYPE_SYNCED)); + MISC_UNUSED(type); + MISC_UNUSED(code); + + if (Pmp_GetDataSize(header_ptr) == 4U) + { + tx_credits = Pmp_GetData(header_ptr, 0U) & (uint8_t)PMP_CREDITS_MASK; + + if ((tx_credits >= PMP_CREDITS_MIN) && + (Pmp_GetData(header_ptr, 1U) == self->sync_params[1]) && + (Pmp_GetData(header_ptr, 2U) == self->sync_params[2]) && + (Pmp_GetData(header_ptr, 3U) == self->sync_params[3]) && + (sid == (self->sync_cnt))) + { + check = true; /* the sync status parameters are correct */ + } + } + + if ((check != false) && (self->sync_state == FIFO_S_SYNCING)) + { + Fifo_InitCounters(self, sid, tx_credits); /* values are incremented on each sync attempt */ + self->sync_state = FIFO_S_SYNCED; /* sync status shall have 4 bytes message body */ + self->rx.wait_processing = false; + Fifo_TxStartWatchdog(self); + Sub_Notify(&self->sync_state_subject, &self->config.fifo_id); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Synchronization */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Synchronizes the FIFO + * \param self The instance + */ +void Fifo_Synchronize(CPmFifo *self) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Synchronize(): FIFO: %u, state: %u", 2U, self->config.fifo_id, self->sync_state)); + self->sync_state = FIFO_S_SYNCING; + Pmcmd_SetTrigger(&self->tx.sync_cmd, true); + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); +} + +/*! \brief Un-synchronizes the FIFO + * \param self The instance + */ +void Fifo_Unsynchronize(CPmFifo *self) +{ + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_Unsynchronize(): FIFO: %u, state: %u", 2U, self->config.fifo_id, self->sync_state)); + if ( (self->sync_state == FIFO_S_SYNCING) || (self->sync_state == FIFO_S_SYNCED) ) + { + self->sync_state = FIFO_S_UNSYNCING; + Pmcmd_SetTrigger(&self->tx.sync_cmd, true); + Srv_SetEvent(&self->service, FIFO_SE_TX_SERVICE); + } +} + +/*! \brief Retrieves the current synchronization state + * \param self The instance + * \return The current synchronization state + */ +Fifo_SyncState_t Fifo_GetState(CPmFifo *self) +{ + return self->sync_state; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Watchdog */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Starts the watchdog handling + * \param self The instance + */ +static void Fifo_TxStartWatchdog(CPmFifo *self) +{ + self->wd.request_started = false; + + TR_INFO((self->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxStartWatchdog(): fifo_id: %u, timeout: %u", 2U, self->config.fifo_id, self->wd.timer_value)); + + if (self->wd.timer_value != 0U) + { + Tm_SetTimer(&self->init.base_ptr->tm, &self->wd.timer, &Fifo_TxOnWatchdogTimer, + self, + self->wd.timer_value, + self->wd.timer_value + ); + } +} + +/*! \brief Callback function which is invoked if the watchdog timer expires + * \param self The instance + */ +static void Fifo_TxOnWatchdogTimer(void *self) +{ + CPmFifo *self_ = (CPmFifo*)self; + + TR_INFO((self_->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxOnWatchdogTimer(): FIFO: %u, state: %u", 2U, self_->config.fifo_id, self_->sync_state)); + + if (self_->wd.request_started == false) + { + if (Pmcmd_Reserve(&self_->wd.wd_cmd) != false) + { + self_->wd.request_started = true; /* indicate that a status is expected */ + Pmcmd_UpdateContent(&self_->wd.wd_cmd, self_->tx.sid_next_to_use - 1U, PMP_CMD_TYPE_REQ_STATUS, PMP_CMD_CODE_REQ_STATUS); + Pmch_Transmit(self_->init.channel_ptr, Pmcmd_GetLldTxObject(&self_->wd.wd_cmd)); + } + else + { + TR_ERROR((self_->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxOnWatchdogTimer(): Unable to reserve watchdog command ", 0U)); + Fifo_Stop(self_, FIFO_S_UNSYNCED_INIT, true); + } + } + else /* status not received in time - notify communication error */ + { + TR_ERROR((self_->init.base_ptr->mns_inst_id, "[FIFO]", "Fifo_TxOnWatchdogTimer(): Missing response on status request", 0U)); + Fifo_Stop(self_, FIFO_S_UNSYNCED_INIT, true); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifo.h b/mnsl/mns_pmfifo.h new file mode 100644 index 0000000..3c9962f --- /dev/null +++ b/mnsl/mns_pmfifo.h @@ -0,0 +1,232 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of Port Message FIFO + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMF + * @{ + */ + +#ifndef MNS_PMFIFO_H +#define MNS_PMFIFO_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_base.h" +#include "mns_lld_pb.h" +#include "mns_message.h" +#include "mns_encoder.h" +#include "mns_pmp.h" +#include "mns_lldpool.h" +#include "mns_pmchannel.h" +#include "mns_pmcmd.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Number of LLD Tx handles dedicated to each FIFO */ +#define FIFO_TX_HANDLES 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Callback function which is invoked when receiving an Rx message + * \param self The Instance (of the host) + * \param msg_ptr The Rx message + */ +typedef void (*Fifo_OnRxMsg_t)(void *self, CMessage *msg_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of class Port Message FIFO */ +typedef struct Fifo_InitData_ +{ + CBase *base_ptr; /*!< \brief Reference to base module */ + CPmChannel *channel_ptr; /*!< \brief Points to channel object which is needed to communicate with + * the driver */ + IEncoder *tx_encoder_ptr; /*!< \brief Encoder for Tx messages */ + IEncoder *rx_encoder_ptr; /*!< \brief Encoder for Rx messages */ + Fifo_OnRxMsg_t rx_cb_fptr; /*!< \brief Callback function invoked for Rx */ + void *rx_cb_inst; /*!< \brief Instance which is referred when invoking rx_cb_fptr */ + +} Fifo_InitData_t; + +/*! \brief Initialization structure of class Port Message FIFO */ +typedef struct Fifo_Config_ +{ + Pmp_FifoId_t fifo_id; /*!< \brief Identifier of message FIFO. + * \details It is required that the fifo_id has the same value as + * specified in PMP. + */ + uint8_t rx_credits; /*!< \brief Number of Rx credits, i.e. reserved Rx messages */ + uint8_t rx_threshold; /*!< \brief Number of Rx credits which are acknowledged in a single status. + * \details The value needs to be smaller or equal than \c rx_credits. + * Valid values are: + * - 0,1: Single message acknowledge + * - 2..rx_credits: Implicit acknowledge is triggered after + * the specified number of messages. + */ + uint8_t tx_wd_timeout; /*!< \brief Idle timeout in x100ms. Formerly known as watchdog timeout */ + uint16_t tx_wd_timer_value; /*!< \brief Timer value used to trigger the watchdog in ms */ + uint8_t rx_ack_timeout; /*!< \brief Rx status timeout in x100ms. */ + uint8_t rx_busy_allowed; /*!< \brief Number of allowed RxStatus busy responds. 0..14, or 0xF (infinite) */ + +} Fifo_Config_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The synchronization status */ +typedef enum Fifo_SyncState_ +{ + FIFO_S_UNSYNCED_INIT, + FIFO_S_SYNCING, + FIFO_S_UNSYNCED_BUSY, + FIFO_S_UNSYNCED_READY, + FIFO_S_SYNCED, + FIFO_S_UNSYNCING + +} Fifo_SyncState_t; + +/*! \brief The class CPmFifo*/ +typedef struct CPmFifo_ +{ + Fifo_InitData_t init; /*!< \brief Initialization data */ + Fifo_Config_t config; /*!< \brief Configuration data */ + + CService service; /*!< \brief Service object */ + Fifo_SyncState_t sync_state; /*!< \brief Synchronization state of the FIFO */ + CSubject sync_state_subject; /*!< \brief Notification of changed synchronization state */ + uint8_t sync_params[4]; /*!< \brief Synchronization parameters */ + uint8_t sync_cnt; /*!< \brief Counts the number of synchronization attempts */ + + struct CPmFifo_wd_ + { + CTimer timer; /*!< \brief The timer object */ + CPmCommand wd_cmd; /*!< \brief The watchdog command message */ + uint16_t timer_value; /*!< \brief The internal timer value used by PMC to trigger the watchdog */ + bool request_started; /*!< \brief Is used to check if the INIC responds with a status before the + * next Cmd.REQUEST_STATUS is triggered. + */ + } wd; + + struct CPmFifo_rx_ + { + CDlList queue; /*!< \brief Message queue containing all incoming messages */ + + IEncoder *encoder_ptr; /*!< \brief Encoder for Rx messages */ + Fifo_OnRxMsg_t on_complete_fptr; /*!< \brief Callback function invoked for Rx */ + void *on_complete_inst; /*!< \brief Instance which is referred when invoking rx_cb_fptr */ + + uint8_t ack_threshold; /*!< \brief Number of unacknowledged Rx credits */ + uint8_t ack_last_ok_sid; /*!< \brief Latest SID which was acknowledged with "success" */ + uint8_t expected_sid; /*!< \brief The next expected Rx message SeqId */ + uint8_t busy_num; /*!< \brief The number of currently processing data messages */ + + bool wait_processing; /*!< \brief If set: Wait until transmission of e.g. NACK has finished + * before continuing with further Rx message processing. + * The flag is used if a status must be sent explicitly. + */ + CPmCommand status; /*!< \brief Rx status channel control */ + + } rx; + + struct CPmFifo_tx_ + { + CDlList waiting_queue; /*!< \brief Queue containing all outgoing messages */ + CDlList pending_q; /*!< \brief Queue containing all messages waiting for Tx status */ + IEncoder *encoder_ptr; /*!< \brief Encoder for Tx messages */ + uint8_t credits; /*!< \brief Remaining Tx credits */ + + CLldPool lld_pool; /*!< \brief Pool of LLD Tx messages, used for data messages */ + + CPmh pm_header; /*!< \brief Temporary header which is used to build the FIFO data messages*/ + CPmCommand cancel_cmd; /*!< \brief Tx cancel command message */ + CPmCommand sync_cmd; /*!< \brief Sync command message */ + + uint8_t sid_next_to_use; /*!< \brief SID that shall be used for the next transmission */ + uint8_t sid_last_completed; /*!< \brief Latest SID that was acknowledged by the INIC */ + uint8_t current_sid; /*!< \brief Tracks the latest valid FIFO status SID received from the INIC */ + Pmp_StatusType_t current_type; /*!< \brief Tracks the latest valid FIFO status type received from the INIC */ + uint8_t current_code; /*!< \brief Tracks the latest valid FIFO status code received from the INIC */ + + bool status_waiting_release; /*!< \brief Is \c true if status notification wasn't completed due to messages + * which are not yet released by the LLD. + */ + bool cancel_all_running; /*!< \brief Is \c true during pending command CANCEL_ALL. This command is required + * if the front-most message is segmented which requires to discard all + * belonging segments (same \c cancel_id) after the CANCEL_ALL was completed. + */ + uint8_t failure_status; /*!< \brief Stores the Tx status until the message is canceled */ + uint8_t failure_sid; /*!< \brief Stores the SID of the last cancelled data message */ + } tx; + +} CPmFifo; + + +/*------------------------------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Fifo_Ctor(CPmFifo *self, const Fifo_InitData_t *init_ptr, const Fifo_Config_t *config_ptr); +extern void Fifo_Stop(CPmFifo *self, Fifo_SyncState_t new_state, bool allow_notification); +extern void Fifo_Cleanup(CPmFifo *self); + +extern void Fifo_Synchronize(CPmFifo *self); +extern void Fifo_Unsynchronize(CPmFifo *self); +extern Fifo_SyncState_t Fifo_GetState(CPmFifo *self); +extern void Fifo_AddStateObserver(CPmFifo *self, CObserver *obs_ptr); +extern void Fifo_RemoveStateObserver(CPmFifo *self, CObserver *obs_ptr); + +/* Rx interface */ +extern void Fifo_RxReleaseMsg(CPmFifo *self, CMessage *msg_ptr); + +/* Tx interface */ +extern void Fifo_Tx(CPmFifo *self, CMessage *msg_ptr, bool bypass); +extern void Fifo_TxOnRelease(void *self, Mns_Lld_TxMsg_t *handle_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMFIFO_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifos.c b/mnsl/mns_pmfifos.c new file mode 100644 index 0000000..e82b837 --- /dev/null +++ b/mnsl/mns_pmfifos.c @@ -0,0 +1,447 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of class CPmFifos + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMFIFOS + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmfifos.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal Constants */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The initialization value of sync_count. It is incremented for each sync or un-sync attempt. */ +static const uint8_t FIFOS_SYNC_CNT_INITIAL = 0xFFU; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal typedefs */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Fifos_Cleanup(CPmFifos *self); +static void Fifos_OnSyncTimeout(void *self); +static void Fifos_OnUnsyncTimeout(void *self); +static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr); + +static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id); +static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CPmFifos + * \param self The instance + * \param base_ptr Reference to basic services + * \param channel_ptr Reference to the port message channel + * \param icm_fifo_ptr Reference to ICM FIFO, or NULL. + * \param mcm_fifo_ptr Reference to MCM FIFO, or NULL. + * \param rcm_fifo_ptr Reference to RCM FIFO, or NULL. + * \details At least one FIFO (MCM or ICM) must be provided. + */ +void Fifos_Ctor(CPmFifos *self, CBase *base_ptr, CPmChannel *channel_ptr, CPmFifo *icm_fifo_ptr, CPmFifo *mcm_fifo_ptr, CPmFifo *rcm_fifo_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + + self->base_ptr = base_ptr; + self->channel_ptr = channel_ptr; + self->state = FIFOS_S_UNSYNCED; + + self->unsync_initial = false; + Fifos_ConfigureSyncParams(self, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); + + self->fifos[PMP_FIFO_ID_ICM] = icm_fifo_ptr; + self->fifos[PMP_FIFO_ID_RCM] = rcm_fifo_ptr; + self->fifos[PMP_FIFO_ID_MCM] = mcm_fifo_ptr; + + T_Ctor(&self->init_timer); + Sub_Ctor(&self->event_subject, self->base_ptr->mns_inst_id); + Obs_Ctor(&self->obs_icm, self, &Fifos_OnFifoEvent); + Obs_Ctor(&self->obs_rcm, self, &Fifos_OnFifoEvent); + Obs_Ctor(&self->obs_mcm, self, &Fifos_OnFifoEvent); + + Pmcmd_Ctor(&self->cmd, PMP_FIFO_ID_ALL, PMP_MSG_TYPE_CMD); + + TR_ASSERT(self->base_ptr->mns_inst_id, "[FIFOS]", (!((icm_fifo_ptr == NULL) && (mcm_fifo_ptr == NULL)))); + + if (icm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(icm_fifo_ptr, &self->obs_icm); + } + + if (rcm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(rcm_fifo_ptr, &self->obs_rcm); + } + + if (mcm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(mcm_fifo_ptr, &self->obs_mcm); + } + + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Ctor(): FIFOS created, state %d", 1U, self->state)); +} + +/*! \brief Adds an observer of synchronization events + * \param self The instance + * \param obs_ptr The observer. The notification result type is Fifos_Event_t. + */ +void Fifos_AddEventObserver(CPmFifos *self, CObserver *obs_ptr) +{ + TR_ASSERT(self->base_ptr->mns_inst_id, "[FIFOS]", (obs_ptr != 0)); + (void)Sub_AddObserver(&self->event_subject, obs_ptr); +} + +/*! \brief Removes an observer of synchronization events + * \param self The instance + * \param obs_ptr The observer. + */ +void Fifos_RemoveEventObserver(CPmFifos *self, CObserver *obs_ptr) +{ + TR_ASSERT(self->base_ptr->mns_inst_id, "[FIFOS]", (obs_ptr != 0)); + (void)Sub_RemoveObserver(&self->event_subject, obs_ptr); +} + +/*! \brief Forces all FIFOs to state UNSYNCED without waiting for INIC responses and + * without throwing events + * \details Stops the LLD interface and releases all pending message resources. + * This function shall be called if the MNS requires a un-normal termination + * which is not detected by port message protocol. + * \param self The instance + */ +void Fifos_ForceTermination(CPmFifos *self) +{ + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_ForceTermination(): Termination started, state: %d", 1U, self->state)); + Fifos_Cleanup(self); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_ForceTermination(): Termination done, state: %d", 1U, self->state)); +} + +/*! \brief Configures retries and timeout for synchronize or un-synchronize + * operation + * \details This method shall be called before starting a synchronization or un-synchronization + * or after it has finished. The current counter of synchronization attempts is reset. + * \param self The instance + * \param retries The number of retries until event FIFOS_EV_SYNC_FAILED or + * FIFOS_EV_UNSYNC_FAILED will be notified + * \param timeout The timeout in milliseconds when the retry is performed + */ +void Fifos_ConfigureSyncParams(CPmFifos *self, uint8_t retries, uint16_t timeout) +{ + self->cmd_retries = retries; + self->cmd_timeout = timeout; + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Synchronization */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initializes all port message FIFOs + * \details Possible results of the operation are the following events which are fired + * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t. + * - \ref FIFOS_EV_SYNC_ESTABLISHED + * - \ref FIFOS_EV_SYNC_FAILED + * \param self The instance + * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic + * retries will be done after the first synchronization timeout. + * \param force_sync If \c true the method will also trigger the synchronization of already + * synced \ref CPmFifo objects. + */ +void Fifos_Synchronize(CPmFifos *self, bool reset_cnt, bool force_sync) +{ + uint8_t cnt; + self->state = FIFOS_S_SYNCING; + self->unsync_initial = false; + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Synchronize(): Synchronization started, state: %d", 1U, self->state)); + + if (reset_cnt) + { + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; + } + + self->sync_cnt++; + Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */ + + for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++) + { + if (self->fifos[cnt] != NULL) + { + if (force_sync || (Fifo_GetState(self->fifos[cnt]) != FIFO_S_SYNCED)) + { + Fifo_Synchronize(self->fifos[cnt]); + } + } + } + + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnSyncTimeout, self, + self->cmd_timeout, 0U); +} + +/*! \brief Un-initializes all port message FIFOs + * \details Possible results of the operation are the following events which are fired + * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t. + * - \ref FIFOS_EV_UNSYNC_COMPLETE + * - \ref FIFOS_EV_UNSYNC_FAILED + * \param self The instance + * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic + * retries will be done after the first synchronization timeout. + * \param initial If the un-synchronization shall be executed prior to a initial synchronization + * it is recommended to set the argument to \c true. After notifying the event + * FIFOS_EV_UNSYNC_COMPLETE the LLD interface will not be stopped. The subsequent + * call of Fifos_Synchronize() will not start the LLD interface un-necessarily. + * To trigger a final un-synchronization \c initial shall be set to \c false. + * I.e., FIFOS_EV_UNSYNC_COMPLETE stops the LLD interface. + */ +void Fifos_Unsynchronize(CPmFifos *self, bool reset_cnt, bool initial) +{ + self->state = FIFOS_S_UNSYNCING; + self->unsync_initial = initial; + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Unsynchronize(): Un-synchronization started, state: %d", 1U, self->state)); + + if (reset_cnt) + { + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; + } + + self->sync_cnt++; + Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */ + + if (Pmcmd_Reserve(&self->cmd)) + { + Pmcmd_SetContent(&self->cmd, 0xFFU, PMP_CMD_TYPE_SYNCHRONIZATION, PMP_CMD_CODE_UNSYNC, NULL, 0U); + + Pmch_Transmit(self->channel_ptr, Pmcmd_GetLldTxObject(&self->cmd)); + } + + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnUnsyncTimeout, self, + self->cmd_timeout, 0U); +} + +/*! \brief Handles the synchronization timeout + * \param self The instance + */ +static void Fifos_OnSyncTimeout(void *self) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_Event_t the_event = FIFOS_EV_SYNC_FAILED; + + self_->state = FIFOS_S_UNSYNCED; + + TR_INFO((self_->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_OnSyncTimeout(): state: %d", 1U, self_->state)); + + if (self_->sync_cnt < self_->cmd_retries) + { + Fifos_Synchronize(self_, false, false); /* retry synchronization after first timeout */ + } + else + { + Fifos_Cleanup(self_); + Sub_Notify(&self_->event_subject, &the_event); + } +} + +/*! \brief Handles the un-synchronization timeout + * \param self The instance + */ +static void Fifos_OnUnsyncTimeout(void *self) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_Event_t the_event = FIFOS_EV_UNSYNC_FAILED; + + self_->state = FIFOS_S_UNSYNCED; + TR_INFO((self_->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_OnUnsyncTimeout(): state: %d", 1U, self_->state)); + + if (self_->sync_cnt < self_->cmd_retries) + { + Fifos_Unsynchronize(self_, false, self_->unsync_initial); /* retry synchronization after first timeout */ + } + else + { + self_->unsync_initial = false; /* un-sync timeout will lead to termination - stop LLD */ + Fifos_Cleanup(self_); + Sub_Notify(&self_->event_subject, &the_event); +} +} + +/*! \brief Performs a cleanup of the Port Message Channel and the dedicated FIFOs + * \details Releases all message objects which are currently in use. + * \param self The instance + */ +static void Fifos_Cleanup(CPmFifos *self) +{ + uint8_t count; + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup started", 0U)); + + if (self->unsync_initial == false) + { + Pmch_Uninitialize(self->channel_ptr); + } + + for (count = 0U; count < PMP_MAX_NUM_FIFOS; count++) /* stop & cleanup all FIFOs */ + { + if (self->fifos[count] != NULL) + { /* stop and avoid recursion */ + Fifo_Stop(self->fifos[count], FIFO_S_UNSYNCED_INIT, false); + Fifo_Cleanup(self->fifos[count]); + } + } + + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup completed", 0U)); + + /* notify external event after message objects were released */ + self->state = FIFOS_S_UNSYNCED; +} + +/*------------------------------------------------------------------------------------------------*/ +/* FIFO observation */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Notifies an event to the host class + * \param self The instance + * \param fifo_id_ptr Specific event identifier, pointer to "fifo_id" + */ +static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_HandleFifoStateChange(self_, *((Pmp_FifoId_t*)fifo_id_ptr)); +} + +/*! \brief Executes transition to new synchronization states + * \param self The instance + * \param fifo_id The FIFO identifier + */ +static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id) +{ + Fifos_Event_t the_event; + + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): FIFOs state: %d, FIFO: %d, FIFO State: %d", 3U, + self->state, fifo_id, Fifo_GetState(self->fifos[fifo_id]))); + + switch (self->state) + { + case FIFOS_S_SYNCING: + if (Fifos_AreAllFifosInState(self, FIFO_S_SYNCED)) + { + self->state = FIFOS_S_SYNCED; /* now the complete channel is synced */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + Fifos_ConfigureSyncParams(self, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT); + the_event = FIFOS_EV_SYNC_ESTABLISHED; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_UNSYNCING: + if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY)) + { + Fifos_Cleanup(self); + self->state = FIFOS_S_UNSYNCED; /* now the complete channel is un-synced */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + the_event = FIFOS_EV_UNSYNC_COMPLETE; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Un-synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_SYNCED: + if (!Fifos_AreAllFifosInState(self, FIFO_S_SYNCED)) + { + self->state = FIFOS_S_UNSYNCING; /* set state to 'unsyncing' and wait until all FIFOs are unsynced */ + self->sync_cnt = 0U; /* pretend having triggered an un-sync which starts the timer */ + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnUnsyncTimeout, self, + FIFOS_UNSYNC_TIMEOUT, 0U); + the_event = FIFOS_EV_SYNC_LOST; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Lost synchronization of Port Message channel", 0U)); + } + if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY)) + { + Fifos_Cleanup(self); + self->state = FIFOS_S_UNSYNCED; /* the complete channel suddenly goes unsynced_complete */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + the_event = FIFOS_EV_UNSYNC_COMPLETE; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Sudden un-synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_UNSYNCED: + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFO event in state unsynced", 0U)); + break; + + default: + TR_INFO((self->base_ptr->mns_inst_id, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFOs state", 0U)); + break; + } + + MISC_UNUSED(fifo_id); +} + +/*! \brief Helper function that evaluates if all configured FIFOs are in a given state + * \param self The instance + * \param target_state The required state that is evaluated for all FIFOs + * \return \c true if all FIFOs are in the given \c target_state, otherwise \c false. + */ +static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state) +{ + bool ret = true; + uint8_t cnt; + + for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++) + { + if (self->fifos[cnt] != NULL) + { + Fifo_SyncState_t state = Fifo_GetState(self->fifos[cnt]); + + if (state != target_state) + { + ret = false; + } + } + } + + return ret; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmfifos.h b/mnsl/mns_pmfifos.h new file mode 100644 index 0000000..814b057 --- /dev/null +++ b/mnsl/mns_pmfifos.h @@ -0,0 +1,131 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of class CPmFifos + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMFIFOS + * @{ + */ + +#ifndef MNS_PMFIFOS_H +#define MNS_PMFIFOS_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_base.h" +#include "mns_pmfifo.h" +#include "mns_pmchannel.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define FIFOS_SYNC_TIMEOUT 50U /*!< \brief Synchronization timeout in milliseconds */ +#define FIFOS_SYNC_RETRIES 40U /*!< \brief Maximum number of synchronization retries after timeout */ +#define FIFOS_UNSYNC_TIMEOUT 200U /*!< \brief Un-synchronization timeout in milliseconds */ +#define FIFOS_UNSYNC_RETRIES 0U /*!< \brief Un-synchronization retries */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The synchronization status of all FIFOs */ +typedef enum Fifos_SyncState_ +{ + FIFOS_S_UNSYNCED, /*!< \brief Not all FIFOs are synchronized */ + FIFOS_S_SYNCING, /*!< \brief FIFOs synchronization has started */ + FIFOS_S_SYNCED, /*!< \brief All FIFOs are synchronized */ + FIFOS_S_UNSYNCING /*!< \brief FIFOs un-synchronization has started */ + +} Fifos_SyncState_t; + +/*! \brief PMS Events */ +typedef enum Fifos_Event_ +{ + FIFOS_EV_SYNC_LOST = 0, /*!< \brief Synchronization of at least one FIFO is lost */ + FIFOS_EV_SYNC_ESTABLISHED = 1, /*!< \brief Synchronization of all FIFOs is established */ + FIFOS_EV_SYNC_FAILED = 2, /*!< \brief The initial synchronization of FIFOs failed */ + FIFOS_EV_UNSYNC_COMPLETE = 3, /*!< \brief Un-synchronization of all FIFOs has succeeded */ + FIFOS_EV_UNSYNC_FAILED = 4 /*!< \brief Un-synchronization of all FIFOs has failed */ + +} Fifos_Event_t; + +/*! \brief The class CPmFifos*/ +typedef struct CPmFifos_ +{ + CBase *base_ptr; /*!< \brief Reference to base object */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + CPmChannel *channel_ptr; /*!< \brief MOST NetServices instance ID */ + + CPmFifo *fifos[PMP_MAX_NUM_FIFOS]; /*!< \brief Reference to assigned FIFOs */ + + CObserver obs_icm; /*!< \brief Observes ICM synchronization state */ + CObserver obs_rcm; /*!< \brief Observes ICM synchronization state */ + CObserver obs_mcm; /*!< \brief Observes MCM synchronization state */ + CPmCommand cmd; /*!< \brief The UNSYNC command message */ + Fifos_SyncState_t state; /*!< \brief The Overall synchronization state */ + uint8_t sync_cnt; /*!< \brief The current count of synchronization command */ + + uint8_t cmd_retries; /*!< \brief The number of sync/un-sync retries */ + uint16_t cmd_timeout; /*!< \brief The the timeout to retry sync/un-sync */ + bool unsync_initial; /*!< \brief Specifies if un-sync complete shall un-initialize the channel */ + + CSubject event_subject; /*!< \brief Subject to report synchronization result */ + CTimer init_timer; /*!< \brief Timer elapses on synchronization timeout */ + +} CPmFifos; + +/*------------------------------------------------------------------------------------------------*/ +/* Function prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Fifos_Ctor(CPmFifos *self, CBase *base_ptr, CPmChannel *channel_ptr, CPmFifo *icm_fifo_ptr, CPmFifo *mcm_fifo_ptr, CPmFifo *rcm_fifo_ptr); +extern void Fifos_AddEventObserver(CPmFifos *self, CObserver *obs_ptr); +extern void Fifos_RemoveEventObserver(CPmFifos *self, CObserver *obs_ptr); +extern void Fifos_Synchronize(CPmFifos *self, bool reset_cnt, bool force_sync); +extern void Fifos_Unsynchronize(CPmFifos *self, bool reset_cnt, bool initial); +extern void Fifos_ForceTermination(CPmFifos *self); +extern void Fifos_ConfigureSyncParams(CPmFifos *self, uint8_t retries, uint16_t timeout/*, bool uninit_on_fail*/); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMFIFOS_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmp.c b/mnsl/mns_pmp.c new file mode 100644 index 0000000..765982d --- /dev/null +++ b/mnsl/mns_pmp.c @@ -0,0 +1,352 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of Port Message Protocol + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMH + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pmp.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* PMP Indexes */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_IDX_PML_H 0U +#define PMP_IDX_PML_L 1U +#define PMP_IDX_PMHL 2U +#define PMP_IDX_FPH 3U +#define PMP_IDX_SID 4U +#define PMP_IDX_EXT_TYPE 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Masks and shifts for bit fields */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_PMHL_MASK 0x1FU /* 0b00011111 */ +#define PMP_VERSION_MASK 0xE0U /* 0b11100000 */ +#define PMP_VERSION 0x40U /* Version: "2" */ +#define PMP_FPH_TYPE_POS 1U +#define PMP_FPH_TYPE_MASK 0x06U /* 0b00000110 */ +#define PMP_FPH_ID_POS 3U +#define PMP_FPH_ID_MASK 0x38U /* 0b00111000 */ +#define PMP_FPH_DIR_RX 0x01U /* RX: "1" */ +#define PMP_FPH_DIR_MASK 0x01U /* 0b00000001 */ +#define PMP_EXT_TYPE_POS 5U +#define PMP_EXT_TYPE_MASK 0xE0U /* 0b11100000 */ +#define PMP_EXT_CODE_MASK 0x1FU /* 0b00011111 */ + +/*------------------------------------------------------------------------------------------------*/ +/* PMP Verification */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_PML_MAX_SIZE_CTRL 69U +#define PMP_PMHL_MIN_SIZE 3U +#define PMP_PMHL_MAX_SIZE 5U + +/*------------------------------------------------------------------------------------------------*/ +/* Macro like functions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Sets the port message length within a given message header + * \param header The message header + * \param length The port message length + */ +void Pmp_SetPml(uint8_t header[], uint8_t length) +{ + header[PMP_IDX_PML_H] = 0U; + header[PMP_IDX_PML_L] = length; +} + +/*! \brief Sets the port message header length within a given message header + * \param header The message header + * \param length The port message header length. Valid values: 3..5. + * Invalid values will set the PMHL to \c 0. + */ +void Pmp_SetPmhl(uint8_t header[], uint8_t length) +{ + if ((length < PMP_PMHL_MIN_SIZE) || (length > PMP_PMHL_MAX_SIZE)) + { + length = 0U; + } + + header[PMP_IDX_PMHL] = length | PMP_VERSION; +} + +/*! \brief Sets the FIFO protocol header within a given message header + * \param header The message header + * \param id The FIFO id + * \param type The port message type + */ +void Pmp_SetFph(uint8_t header[], Pmp_FifoId_t id, Pmp_MsgType_t type) +{ + header[PMP_IDX_FPH] = (uint8_t)((uint8_t)type << PMP_FPH_TYPE_POS) | (uint8_t)((uint8_t)id << PMP_FPH_ID_POS) | (uint8_t)PMP_DIR_TX; +} + +/*! \brief Sets the field ExtType within a given message header + * \param header The message header + * \param type The command or status type + * \param code The command or status code + */ +void Pmp_SetExtType(uint8_t header[], uint8_t type, uint8_t code) +{ + header[PMP_IDX_EXT_TYPE] = (uint8_t)((type << PMP_EXT_TYPE_POS) & PMP_EXT_TYPE_MASK) | (uint8_t)(code & PMP_EXT_CODE_MASK); +} + +/*! \brief Sets the sequence id within a given message header + * \param header The message header + * \param sid The sequence id + */ +void Pmp_SetSid(uint8_t header[], uint8_t sid) +{ + header[PMP_IDX_SID] = sid; +} + +/*! \brief Retrieves the port message length of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The port message length in bytes or 0 if the PML is greater than 255. + */ +uint8_t Pmp_GetPml(uint8_t header[]) +{ + uint8_t pml; + if (header[PMP_IDX_PML_H] != 0U) + { + pml = 0U; + } + else + { + pml = header[PMP_IDX_PML_L]; + } + + return pml; +} + +/*! \brief Retrieves the port message header length of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The port message header length in bytes + */ +uint8_t Pmp_GetPmhl(uint8_t header[]) +{ + return ((uint8_t)(header[PMP_IDX_PMHL] & (uint8_t)PMP_PMHL_MASK)); +} + +/*! \brief Retrieves the FIFO number of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The FIFO number + */ +Pmp_FifoId_t Pmp_GetFifoId(uint8_t header[]) +{ + return (Pmp_FifoId_t)(((uint8_t)PMP_FPH_ID_MASK & (header)[PMP_IDX_FPH]) >> PMP_FPH_ID_POS); +} + +/*! \brief Retrieves the FIFO Type of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The FIFO type + */ +Pmp_MsgType_t Pmp_GetMsgType(uint8_t header[]) +{ + return ((Pmp_MsgType_t)((PMP_FPH_TYPE_MASK & (header)[PMP_IDX_FPH]) >> PMP_FPH_TYPE_POS)); +} + +/*! \brief Retrieves the SequenceID of a given port message buffer + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \return The SequenceID + */ +uint8_t Pmp_GetSid(uint8_t header[]) +{ + return ((header)[PMP_IDX_SID]); +} + +/*! \brief Retrieves payload data of a port message + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \param index The index of the payload byte starting with '0' + * \return The content of a payload data byte + */ +uint8_t Pmp_GetData(uint8_t header[], uint8_t index) +{ + return header[Pmp_GetPmhl(header) + 3U + index]; +} + +/*! \brief Retrieves the payload size of the port message + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \details The function Pmp_VerifyHeader() must be called before + * to verify that the port message fields are consistent. + * \return The payload size of a port message + */ +uint8_t Pmp_GetDataSize(uint8_t header[]) +{ + return Pmp_GetPml(header) - (Pmp_GetPmhl(header) + 1U); +} + +/*! \brief Checks if header length fields are set to valid values + * \param header Data buffer containing the port message. + * The required size of this buffer is 6 bytes. + * \param buf_len Length of the complete port message in bytes + * \return Returns \c true if the header was checked successfully, + * otherwise \c false. + */ +bool Pmp_VerifyHeader(uint8_t header[], uint8_t buf_len) +{ + uint8_t pml = Pmp_GetPml(header); + uint8_t pmhl = Pmp_GetPmhl(header); + bool ok = true; + + ok = ((pmhl >= 3U)&&(pmhl <= 5U)) ? ok : false; + ok = ((header[PMP_IDX_PMHL] & PMP_VERSION_MASK) == PMP_VERSION) ? ok : false; + ok = (pml >= (pmhl + 1U)) ? ok : false; + ok = ((pml + 2U) <= buf_len) ? ok : false; + ok = (pml <= PMP_PML_MAX_SIZE_CTRL) ? ok : false; + ok = ((header[PMP_IDX_FPH] & PMP_FPH_DIR_MASK) == PMP_FPH_DIR_RX) ? ok : false; + + return ok; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Creates a Port Message Header instance + * \param self The instance + */ +void Pmh_Ctor(CPmh *self) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); +} + +/*! \brief Inserts a port message header of the specified size into a given buffer + * \param self Header content to be written to the buffer (source) + * \param data Data buffer the header shall be written to (target) + */ +void Pmh_BuildHeader(CPmh *self, uint8_t data[]) +{ + uint8_t cnt; + + data[PMP_IDX_PML_H] = 0U; + data[PMP_IDX_PML_L] = (uint8_t)self->pml; + data[PMP_IDX_PMHL] = (uint8_t)PMP_VERSION | self->pmhl; + data[PMP_IDX_FPH] = (uint8_t)PMP_DIR_TX | ((uint8_t)((self->fifo_id) << PMP_FPH_ID_POS)) | + ((uint8_t)((self->msg_type) << PMP_FPH_TYPE_POS)); + + data[PMP_IDX_SID] = self->sid; + data[PMP_IDX_EXT_TYPE]= self->ext_type; + + for (cnt=3U; cnt < self->pmhl; cnt++) + { + data[3U + cnt] = 0U; /* add stuffing bytes */ + } +} + +/*! \brief Decodes a given data buffer into a provided port message header structure + * \param self Header content structure (target) + * \param data Data buffer containing the port message with a minimum size + * of 6 bytes (source) + */ +void Pmh_DecodeHeader(CPmh *self, uint8_t data[]) +{ + self->pml = Pmp_GetPml(data); + self->pmhl = data[PMP_IDX_PMHL] & PMP_PMHL_MASK; /* ignore version */ + + self->fifo_id = Pmp_GetFifoId(data); + self->msg_type = Pmp_GetMsgType(data); + self->sid = data[PMP_IDX_SID]; + self->ext_type = data[PMP_IDX_EXT_TYPE]; +} + +/*! \brief Setter function for FIFO protocol header which contains several subfields + * \details The "retransmitted" flag is currently not supported (always Tx) + * \param self Reference to the PM content structure that shall be modified + * \param fifo_id Id of the PM FIFO + * \param msg_type PM type + */ +void Pmh_SetFph(CPmh *self, Pmp_FifoId_t fifo_id, Pmp_MsgType_t msg_type) +{ + self->msg_type = msg_type; + self->fifo_id = fifo_id; +} + +/*! \brief Retrieves the ExtType StatusType + * \param self The instance + * \return Returns The Status Type + */ +Pmp_StatusType_t Pmh_GetExtStatusType(CPmh *self) +{ + return ((Pmp_StatusType_t)((uint8_t)(PMP_EXT_TYPE_MASK & self->ext_type) >> PMP_EXT_TYPE_POS)); +} + +/*! \brief Retrieves the ExtType StatusCode + * \param self The instance + * \return Returns The Status Code + */ +Pmp_CommandCode_t Pmh_GetExtCommandCode(CPmh *self) +{ + return ((Pmp_CommandCode_t)(uint8_t)(PMP_EXT_CODE_MASK & self->ext_type)); +} + +/*! \brief Retrieves the ExtType StatusType + * \param self The instance + * \return Returns The Status Type + */ +Pmp_CommandType_t Pmh_GetExtCommandType(CPmh *self) +{ + return ((Pmp_CommandType_t)((uint8_t)(PMP_EXT_TYPE_MASK & self->ext_type) >> PMP_EXT_TYPE_POS)); +} + +/*! \brief Retrieves the ExtType StatusCode + * \param self The instance + * \return Returns The Status Code + */ +Pmp_StatusCode_t Pmh_GetExtStatusCode(CPmh *self) +{ + return ((Pmp_StatusCode_t)(uint8_t)(PMP_EXT_CODE_MASK & self->ext_type)); +} + +/*! \brief Sets the ExtType field by passing the values for type and code + * \details The function is applicable for status and command + * \param self The Instance + * \param type The status or command type + * \param code The status or command code + */ +void Pmh_SetExtType(CPmh *self, uint8_t type, uint8_t code) +{ + self->ext_type = (uint8_t)((type << PMP_EXT_TYPE_POS) & PMP_EXT_TYPE_MASK) | (uint8_t)(code & PMP_EXT_CODE_MASK); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pmp.h b/mnsl/mns_pmp.h new file mode 100644 index 0000000..96b22b0 --- /dev/null +++ b/mnsl/mns_pmp.h @@ -0,0 +1,211 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of Port Message Protocol + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_PMH + * @{ + */ + +#ifndef MNS_PMP_H +#define MNS_PMP_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define PMP_PM_MAX_SIZE_HEADER 8U /*!< \brief Maximum size of a port message header */ +#define PMP_PM_MIN_SIZE_HEADER 6U /*!< \brief Minimum size of a port message header */ +#define PMP_MAX_NUM_FIFOS 7U /*!< \brief Maximum number if FIFOs an an array + * \details Means "3" if FIFO "0" and "2" is used + */ +#define PMP_CREDITS_MASK 0x3FU /*!< \brief Valid bits for credits: 5..0 */ +#define PMP_CREDITS_MIN 1U /*!< \brief Minimum value for credits: 1 */ +#define PMP_CREDITS_MAX 63U /*!< \brief Maximum value for credits: 63 */ + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Specifies the FIFO */ +typedef enum Pmp_FifoId_ +{ + PMP_FIFO_ID_MCM = 0U, /*!< \brief FIFO dedicated to MOST Control Messages (MCM) */ + /* PMP_FIFO_ID_MDP = 1U, < (reserved identifier) */ + PMP_FIFO_ID_ICM = 2U, /*!< \brief FIFO dedicated to INIC Control Messages (ICM) */ + PMP_FIFO_ID_ALL = 3U, /*!< \brief All FIFOs (ICM, MCM) */ + /* PMP_FIFO_ID_MEP = 4U < (reserved identifier) */ + /* PMP_FIFO_ID_IOCM = 5U < (reserved identifier) */ + PMP_FIFO_ID_RCM = 6U /*!< \brief FIFO dedicated to Remote Control Messages (RCM) */ + /* PMP_FIFO_ID_RSVD = 7U < (reserved identifier) */ + +} Pmp_FifoId_t; + +/*! \brief Specifies the messages type */ +typedef enum Pmp_MsgType_ +{ + PMP_MSG_TYPE_CMD = 0U, /*!< \brief FIFO command message */ + PMP_MSG_TYPE_STATUS = 1U, /*!< \brief FIFO status message */ + PMP_MSG_TYPE_DATA = 2U /*!< \brief FIFO data message */ + +} Pmp_MsgType_t; + +/*! \brief Specifies the direction of the Port Message */ +typedef enum Pmp_Direction_ +{ + PMP_DIR_TX = 0, /*!< \brief Direction Tx (EHC -> INIC) */ + PMP_DIR_RX = 1 /*!< \brief Direction Rx (INIC -> EHC) */ + +} Pmp_Direction_t; + +/*! \brief Specifies FIFO status types */ +typedef enum Pmp_StatusType_ +{ + PMP_STATUS_TYPE_FAILURE = 0U, /*!< \brief PMP status type "failure" */ + PMP_STATUS_TYPE_FLOW = 1U, /*!< \brief PMP status type "flow" */ + PMP_STATUS_TYPE_SYNCED = 4U, /*!< \brief PMP status type "synced" */ + PMP_STATUS_TYPE_UNSYNCED_BSY = 5U, /*!< \brief PMP status type "unsynced_busy" */ + PMP_STATUS_TYPE_UNSYNCED_RDY = 6U /*!< \brief PMP status type "unsynced_ready" */ + +} Pmp_StatusType_t; + +/*! \brief Specifies FIFO status codes */ +typedef enum Pmp_StatusCode_ +{ + PMP_STATUS_CODE_BUSY = 0U, /*!< \brief PMP status code "busy" */ + PMP_STATUS_CODE_SUCCESS = 1U, /*!< \brief PMP status code "success" */ + PMP_STATUS_CODE_CANCELED = 3U, /*!< \brief PMP status code "canceled" */ + PMP_STATUS_CODE_NACK = 8U /*!< \brief PMP status code "not_acknowledge" */ + +} Pmp_StatusCode_t; + +/*! \brief Specifies FIFO status codes */ +typedef enum Pmp_UnsyncReason_ +{ + PMP_UNSYNC_R_STARTUP = 1U, /*!< \brief PMP status code, UnsyncReason "INIC Startup" */ + PMP_UNSYNC_R_REINIT = 2U, /*!< \brief PMP status code, UnsyncReason "Re-init of another FIFO" */ + PMP_UNSYNC_R_COMMAND = 3U, /*!< \brief PMP status code, UnsyncReason "By sync or un-sync command" */ + PMP_UNSYNC_R_ACK_TIMEOUT = 4U, /*!< \brief PMP status code, UnsyncReason "Missing EHC Rx acknowledge" */ + PMP_UNSYNC_R_WD_TIMEOUT = 5U, /*!< \brief PMP status code, UnsyncReason "Missing EHC status request" */ + PMP_UNSYNC_R_TX_TIMEOUT = 6U /*!< \brief PMP status code, UnsyncReason "Missing EHC read, or blocked communication" */ + +} Pmp_UnsyncReason_t; + +/*! \brief Specifies FIFO command types */ +typedef enum Pmp_CommandType_ +{ + PMP_CMD_TYPE_REQ_STATUS = 0U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_TYPE_MSG_ACTION = 1U, /*!< \brief PMP command type "message_action" */ + /* PMP_CMD_TYPE_TRIGGER_NAOMDP = 2U, < (reserved identifier) */ + PMP_CMD_TYPE_SYNCHRONIZATION = 4U /*!< \brief PMP command type "synchronization" */ + +} Pmp_CommandType_t; + +/*! \brief Specifies FIFO command codes */ +typedef enum Pmp_CommandCode_ +{ + PMP_CMD_CODE_REQ_STATUS = 0U, /*!< \brief PMP command code "request_status" */ + /* PMP_CMD_CODE_TRIGGER_NAOMDP = 0U, < (reserved identifier)*/ + PMP_CMD_CODE_ACTION_RETRY = 1U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_ACTION_CANCEL = 2U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_ACTION_CANCEL_ALL = 3U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_UNSYNC = 10U, /*!< \brief PMP command type "request_status" */ + PMP_CMD_CODE_SYNC = 21U /*!< \brief PMP command type "request_status" */ + +} Pmp_CommandCode_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Header buffer operations */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pmp_SetPml(uint8_t header[], uint8_t length); +extern void Pmp_SetPmhl(uint8_t header[], uint8_t length); +extern void Pmp_SetFph(uint8_t header[], Pmp_FifoId_t id, Pmp_MsgType_t type); +extern void Pmp_SetExtType(uint8_t header[], uint8_t type, uint8_t code); +extern void Pmp_SetSid(uint8_t header[], uint8_t sid); + +extern uint8_t Pmp_GetPml(uint8_t header[]); +extern uint8_t Pmp_GetPmhl(uint8_t header[]); +extern Pmp_FifoId_t Pmp_GetFifoId(uint8_t header[]); +extern Pmp_MsgType_t Pmp_GetMsgType(uint8_t header[]); +extern uint8_t Pmp_GetSid(uint8_t header[]); +extern uint8_t Pmp_GetDataSize(uint8_t header[]); +extern uint8_t Pmp_GetData(uint8_t header[], uint8_t index); +extern bool Pmp_VerifyHeader(uint8_t header[], uint8_t buf_len); + +/*------------------------------------------------------------------------------------------------*/ +/* Class CPmh */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines the content of a Port Message Header + * \details The current structure does not support "direction" and "retransmitted" flag. + */ +typedef struct CPmh_ +{ + uint8_t pml; /*!< \brief Port Message length */ + uint8_t pmhl; /*!< \brief Port Message Header length */ + Pmp_MsgType_t msg_type; /*!< \brief Port Message type */ + Pmp_FifoId_t fifo_id; /*!< \brief FIFO identifier */ + uint8_t sid; /*!< \brief The SequenceId */ + uint8_t ext_type; /*!< \brief status or content type */ + +} CPmh; + +/*------------------------------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pmh_Ctor(CPmh *self); +extern void Pmh_BuildHeader(CPmh *self, uint8_t data[]); +extern void Pmh_DecodeHeader(CPmh *self, uint8_t data[]); +extern void Pmh_SetFph(CPmh *self, Pmp_FifoId_t fifo_id, Pmp_MsgType_t msg_type); +extern Pmp_StatusType_t Pmh_GetExtStatusType(CPmh *self); +extern Pmp_StatusCode_t Pmh_GetExtStatusCode(CPmh *self); +extern Pmp_CommandCode_t Pmh_GetExtCommandCode(CPmh *self); +extern Pmp_CommandType_t Pmh_GetExtCommandType(CPmh *self); +extern void Pmh_SetExtType(CPmh *self, uint8_t type, uint8_t code); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_PMP_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_pool.c b/mnsl/mns_pool.c new file mode 100644 index 0000000..870efd3 --- /dev/null +++ b/mnsl/mns_pool.c @@ -0,0 +1,127 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of message pool class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_POOL + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_pool.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of message pool class + * \param self The instance + * \param messages Reference to an array of message objects + * \param size Number of message objects the \c messages array is comprising. + * \param mns_inst_id MOST NetServices instance ID + */ +void Pool_Ctor(CPool *self, CMessage messages[], uint16_t size, uint8_t mns_inst_id) +{ + uint16_t index; + + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = mns_inst_id; + self->initial_size = size; + self->messages = messages; + + Dl_Ctor(&self->message_list, self->mns_inst_id); + + for (index = 0U; index < size; index++) + { + Msg_Ctor(&messages[index]); + Msg_SetPoolReference(&messages[index], self); + Dl_InsertTail(&self->message_list, Msg_GetNode(&messages[index])); + } +} + +/*! \brief Retrieves a message object from the pool + * \param self The instance + * \return Reference to the CMessage structure if a message is available. + * Otherwise \c NULL. + */ +CMessage* Pool_GetMsg(CPool *self) +{ + CMessage *msg = NULL; + CDlNode *node = Dl_PopHead(&self->message_list); + + if (node != NULL) + { + msg = (CMessage*)node->data_ptr; + } + + return msg; +} + +/*! \brief Returns a message object to the pool pre-assigned pool + * \param msg_ptr Reference to the message object which needs + * to be returned to the pool. + */ +void Pool_ReturnMsg(CMessage *msg_ptr) +{ + CPool *pool_ptr = (CPool*)Msg_GetPoolReference(msg_ptr); + + if (pool_ptr != NULL) + { + TR_ASSERT(pool_ptr->mns_inst_id, "[POOL]", (Pool_GetCurrentSize(pool_ptr) < pool_ptr->initial_size)); + Dl_InsertTail(&pool_ptr->message_list, Msg_GetNode(msg_ptr)); + } + else + { + TR_ERROR((pool_ptr->mns_inst_id, "[POOL]", "Pool_ReturnMsg(): released msg_ptr=0x%p without pool reference", 1U, msg_ptr)); + } +} + +/*! \brief Retrieves the current number of available message objects in the pool + * \param self The instance + * \return The current number of available message objects in the pool + */ +uint16_t Pool_GetCurrentSize(CPool *self) +{ + uint16_t list_size = Dl_GetSize(&self->message_list); + + return list_size; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ diff --git a/mnsl/mns_pool.h b/mnsl/mns_pool.h new file mode 100644 index 0000000..c9b6edd --- /dev/null +++ b/mnsl/mns_pool.h @@ -0,0 +1,82 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of message pool class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_POOL + * @{ + */ +#ifndef MNS_POOL_H +#define MNS_POOL_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_dl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif +/*------------------------------------------------------------------------------------------------*/ +/* Class CPool */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CMessage + * \details Common internal message class which embeds a list of MOST telegrams + */ +typedef struct CPool_ +{ + uint16_t initial_size; /*! \brief The size of a provided message array */ + CMessage *messages; /*! \brief Reference to a message array provided by another module */ + CDlList message_list; /*! \brief Doubly linked list required providing available messages */ + uint8_t mns_inst_id; /*! \brief MOST NetServices instance ID */ + +} CPool; + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Pool_Ctor(CPool *self, CMessage messages[], uint16_t size, uint8_t mns_inst_id); +extern CMessage* Pool_GetMsg(CPool *self); +extern void Pool_ReturnMsg(CMessage *msg_ptr); +extern uint16_t Pool_GetCurrentSize(CPool *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_POOL_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_ret.h b/mnsl/mns_ret.h new file mode 100644 index 0000000..50305bf --- /dev/null +++ b/mnsl/mns_ret.h @@ -0,0 +1,115 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief This header file contains standard return values used by MOST NetServices functions + * and methods. + * \addtogroup G_MNS_MISC_RET_RES + * @{ + */ + +#ifndef MNS_RET_H +#define MNS_RET_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerations */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return codes used for synchronous response */ +typedef enum Mns_Return_ +{ + MNS_RET_SUCCESS = 0x00, /*!< \brief Operation successfully completed */ + MNS_RET_ERR_PARAM = 0x01, /*!< \brief At least one parameter exceeds its + admissible range */ + MNS_RET_ERR_BUFFER_OVERFLOW = 0x02, /*!< \brief Buffer overflow or service busy */ + MNS_RET_ERR_NOT_AVAILABLE = 0x03, /*!< \brief Functionality not available */ + MNS_RET_ERR_NOT_SUPPORTED = 0x04, /*!< \brief This function is not supported by this + derivative of INIC / physical layer / MOST + speed */ + MNS_RET_ERR_INVALID_SHADOW = 0x05, /*!< \brief The requested information is not yet + available */ + MNS_RET_ERR_ALREADY_SET = 0x06, /*!< \brief The value to be set is already set. The + application can therefore be aware that no + message will be send to INIC and no + callback will be called */ + MNS_RET_ERR_API_LOCKED = 0x07, /*!< \brief INIC performs already requested function. */ + MNS_RET_ERR_NOT_INITIALIZED = 0x08 /*!< \brief MOST NetServices is not initialized */ + +} Mns_Return_t; + +/*! \brief Result codes used for asynchronous response */ +typedef enum Mns_Result_ +{ + MNS_RES_SUCCESS = 0x00, /*!< \brief Operation successfully completed */ + MNS_RES_ERR_MOST_STANDARD = 0x01, /*!< \brief MOST standard error occurred */ + MNS_RES_ERR_BUSY = 0x02, /*!< \brief Function currently busy */ + MNS_RES_ERR_PROCESSING = 0x03, /*!< \brief Processing error occurred */ + MNS_RES_ERR_CONFIGURATION = 0x04, /*!< \brief Configuration error occurred */ + MNS_RES_ERR_SYSTEM = 0x05, /*!< \brief System error occurred */ + MNS_RES_ERR_TIMEOUT = 0x06, /*!< \brief Timeout occurred */ + MNS_RES_ERR_TRANSMISSION = 0x07 /*!< \brief Transmission error occurred */ + +} Mns_Result_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard result structure which provides fields for detailed status and + * error information + * \details \mns_ic_started{ The \ref P_UM_SYNC_AND_ASYNC_RESULTS section in \c Getting \c Started will provide you with more detailed information concerning the info pointer and the error code. } + */ +typedef struct Mns_StdResult_ +{ + Mns_Result_t code; /*!< \brief Result/Error code */ + uint8_t *info_ptr; /*!< \brief INIC error data */ + uint8_t info_size; /*!< \brief Size of the INIC error data in bytes */ + +} Mns_StdResult_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for MOST NetServices standard result callbacks + * \param result Result of the callback + */ +typedef void (*Mns_StdResultCb_t)(Mns_StdResult_t result); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_RET_H */ + +/*! + * @} + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_scheduler.c b/mnsl/mns_scheduler.c new file mode 100644 index 0000000..1fc5273 --- /dev/null +++ b/mnsl/mns_scheduler.c @@ -0,0 +1,259 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the scheduler module. The module consists of the two classes + * CScheduler and CService. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_SCHEDULER + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_scheduler.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Constants */ +/*------------------------------------------------------------------------------------------------*/ +const Srv_Event_t SRV_EMPTY_EVENT_MASK = (Srv_Event_t)0x00000000; /*!< \brief Empty event mask */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static bool Scd_SearchSlot(void *current_prio_ptr, void *new_prio_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CScheduler */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the scheduler class. + * \param self Instance pointer + * \param init_ptr Reference to the initialization data + */ +void Scd_Ctor(CScheduler *self, Scd_InitData_t *init_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = init_ptr->mns_inst_id; + Dl_Ctor(&self->srv_list, self->mns_inst_id); + Ssub_Ctor(&self->service_request_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->service_request_subject, + init_ptr->service_request_obs_ptr); + self->scd_srv_is_running = false; +} + +/*! \brief Add the given service to the scheduler. All services are arranged in priority order. + * A service with a higher priority will execute before a service with a lower priority. + * \param self Instance pointer + * \param srv_ptr Reference of the service which shall be added + * \return SCD_OK: Service added + * \return SCD_SRV_ALREADY_LISTED: Services already listed + */ +Scd_Ret_t Scd_AddService(CScheduler *self, CService *srv_ptr) +{ + Scd_Ret_t ret_val; + + /* Check that service is not already part of scheduler */ + if(false == Dl_IsNodeInList(&self->srv_list, &srv_ptr->list_node)) + { + /* Search slot where the service must be inserted depending on the priority value. */ + CDlNode *result_ptr = Dl_Foreach(&self->srv_list, &Scd_SearchSlot, &srv_ptr->priority); + + if(NULL != result_ptr) /* Slot found? */ + { + Dl_InsertBefore(&self->srv_list, result_ptr, &srv_ptr->list_node); + } + else /* No slot found -> Insert as last node */ + { + Dl_InsertTail(&self->srv_list, &srv_ptr->list_node); + } + /* Create back link service -> scheduler */ + srv_ptr->scd_ptr = self; + Dln_SetData(&srv_ptr->list_node, &srv_ptr->priority); + ret_val = SCD_OK; + } + else /* Service is already part of schedulers list */ + { + ret_val = SCD_SRV_ALREADY_LISTED; + } + + return ret_val; +} + +/*! \brief Remove the given service from the schedulers list. + * \param self Instance pointer + * \param srv_ptr Reference of the service which shall be removed + * \return SCD_OK: Service removed + * \return SCD_UNKNOWN_SRV: Unknown service can not be removed + */ +Scd_Ret_t Scd_RemoveService(CScheduler *self, CService *srv_ptr) +{ + Scd_Ret_t ret_val = SCD_OK; + + /* Error occurred? */ + if(DL_UNKNOWN_NODE == Dl_Remove(&self->srv_list, &srv_ptr->list_node)) + { + ret_val = SCD_UNKNOWN_SRV; + } + + return ret_val; +} + +/*! \brief Service function of the scheduler module. + * \param self Instance pointer + */ +void Scd_Service(CScheduler *self) +{ + CService *current_srv_ptr = (CService *)(void*)self->srv_list.head; + + /* Scheduler service is running. Important for event handling */ + self->scd_srv_is_running = true; + + while(NULL != current_srv_ptr) /* Process registered services */ + { + if(NULL != current_srv_ptr->service_fptr) + { + /* Are events pending for the current service */ + if(SRV_EMPTY_EVENT_MASK != current_srv_ptr->event_mask) + { + /* Execute service callback function */ + current_srv_ptr->service_fptr(current_srv_ptr->instance_ptr); + /* Was the current service removed from the schedulers list? */ + if((current_srv_ptr->list_node.prev == NULL) && (current_srv_ptr->list_node.next == NULL)) + { + break; /* Abort scheduler service */ + } + } + } + current_srv_ptr = (CService *)(void*)current_srv_ptr->list_node.next; + } + /* Scheduler services finished */ + self->scd_srv_is_running = false; +} + +/*! \brief Searches for pending events. + * \param self Instance pointer + * \return true: At least one event is active + * \return false: No event is pending + */ +bool Scd_AreEventsPending(CScheduler *self) +{ + bool ret_val = false; + CService *current_srv_ptr = (CService *)(void*)self->srv_list.head; + + while(NULL != current_srv_ptr) + { + if(SRV_EMPTY_EVENT_MASK != current_srv_ptr->event_mask) + { + ret_val = true; + break; + } + current_srv_ptr = (CService *)(void*)current_srv_ptr->list_node.next; + } + + return ret_val; +} + +/*! \brief Searches the slot where the new service has to be inserted. The position depends on + * the given priority. If a the priority of the new service is higher than the priority + * of the current service \c true is returned which stops the search. + * \param current_prio_ptr Current service which is analyzed + * \param new_prio_ptr Priority of the new service + * \return false: The priority of the current service is greater than the new priority + * \return true: The priority of the current service is less than or equal to the new priority + */ +static bool Scd_SearchSlot(void *current_prio_ptr, void *new_prio_ptr) +{ + uint8_t current_prio_ptr_ = *((uint8_t *)current_prio_ptr); + uint8_t new_prio_ = *((uint8_t*)new_prio_ptr); + bool ret_val = false; + + if(current_prio_ptr_ <= new_prio_) + { + ret_val = true; + } + + return ret_val; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CService */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Parameter constructor of the service class. + * \param self Instance pointer + * \param instance_ptr Reference to object which contains the corresponding service + * \param priority Priority of the service + * \param service_fptr Service callback + */ +void Srv_Ctor(CService *self, uint8_t priority, void *instance_ptr, Srv_Cb_t service_fptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + Dln_Ctor(&self->list_node, NULL); + self->priority = priority; + self->instance_ptr = instance_ptr; + self->service_fptr = service_fptr; +} + +/*! \brief Sets events for the given service according to the given event mask. + * \param self Instance pointer + * \param event_mask Mask of the events to be set + */ +void Srv_SetEvent(CService *self, Srv_Event_t event_mask) +{ + self->event_mask |= event_mask; + if(false == self->scd_ptr->scd_srv_is_running) + { + Ssub_Notify(&self->scd_ptr->service_request_subject, NULL, false); + } +} + +/*! \brief The function returns the current state of all event bits of the service. + * \param self Instance pointer + * \param event_mask_ptr Reference to the memory of the returned event mask + */ +void Srv_GetEvent(CService *self, Srv_Event_t *event_mask_ptr) +{ + *event_mask_ptr = self->event_mask; +} + +/*! \brief Clears events for the given service according to the given event mask. + * \param self Instance pointer + * \param event_mask Mask of the events to be clear + */ +void Srv_ClearEvent(CService *self, Srv_Event_t event_mask) +{ + self->event_mask &= ~event_mask; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_scheduler.h b/mnsl/mns_scheduler.h new file mode 100644 index 0000000..624d0ad --- /dev/null +++ b/mnsl/mns_scheduler.h @@ -0,0 +1,148 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the scheduler module. The module consists of the two classes + * CScheduler and CService. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_SCHEDULER + * @{ + */ + +#ifndef MNS_SCHEDULER_H +#define MNS_SCHEDULER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_dl.h" +#include "mns_obs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Type definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for MNS request callback function */ +typedef void (*Scd_MnsServiceRequest_t)(void); +/*! \brief Function signature used for service callback functions + * \param self Instance pointer + */ +typedef void (*Srv_Cb_t)(void *self); +/*! \brief Data type of event masks */ +typedef uint32_t Srv_Event_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Definitions */ +/*------------------------------------------------------------------------------------------------*/ +extern const Srv_Event_t SRV_EMPTY_EVENT_MASK; /*!< \brief Empty event mask */ + +/*------------------------------------------------------------------------------------------------*/ +/* Enumerators */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Standard return values of scheduler module. */ +typedef enum Scd_Ret_ +{ + SCD_OK, /*!< \brief No error */ + SCD_UNKNOWN_SRV, /*!< \brief Service is unknown */ + SCD_SRV_ALREADY_LISTED /*!< \brief Service is already part of the schedulers list */ + +} Scd_Ret_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of the scheduler module. */ +typedef struct Scd_InitData_ +{ + /*! \brief Observer to request a MNS service call */ + CSingleObserver *service_request_obs_ptr; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} Scd_InitData_t; + +/*! \brief Class structure of the scheduler. */ +typedef struct CScheduler_ +{ + /*! \brief Subject to request a MNS service call */ + CSingleSubject service_request_subject; + /*! \brief Service list of the scheduler */ + CDlList srv_list; + /*! \brief Indicates if the scheduler services is running */ + bool scd_srv_is_running; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} CScheduler; + +/*! \brief Class structure of services used by the scheduler. */ +typedef struct CService_ +{ + CDlNode list_node; /*!< \brief Administration area for the linked list */ + CScheduler *scd_ptr; /*!< \brief Back link to scheduler */ + void *instance_ptr; /*!< \brief Reference of instance passed to service_fptr() */ + Srv_Cb_t service_fptr; /*!< \brief Reference of the service callback function */ + Srv_Event_t event_mask; /*!< \brief Event mask of the service */ + uint8_t priority; /*!< \brief Priority of the service */ + +} CService; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CScheduler */ +/*------------------------------------------------------------------------------------------------*/ +extern void Scd_Ctor(CScheduler *self, Scd_InitData_t *init_ptr); +extern void Scd_Service(CScheduler *self); +extern Scd_Ret_t Scd_AddService(CScheduler *self, CService *srv_ptr); +extern Scd_Ret_t Scd_RemoveService(CScheduler *self, CService *srv_ptr); +extern bool Scd_AreEventsPending(CScheduler *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CService */ +/*------------------------------------------------------------------------------------------------*/ +extern void Srv_Ctor(CService *self, uint8_t priority, void *instance_ptr, Srv_Cb_t service_fptr); +extern void Srv_SetEvent(CService *self, Srv_Event_t event_mask); +extern void Srv_GetEvent(CService *self, Srv_Event_t *event_mask_ptr); +extern void Srv_ClearEvent(CService *self, Srv_Event_t event_mask); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_SCHEDULER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_segmentation.c b/mnsl/mns_segmentation.c new file mode 100644 index 0000000..7bfa2ff --- /dev/null +++ b/mnsl/mns_segmentation.c @@ -0,0 +1,555 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of AMS Segmentation Class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSSEGM + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_segmentation.h" +#include "mns_ams.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + /*!\brief Timeout for garbage collector */ +static const uint16_t SEGM_GC_TIMEOUT = 5000U; /* parasoft-suppress MISRA2004-8_7 "intended usage as configuration parameter" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal typedefs */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static Mns_AmsRx_Msg_t *Segm_RxRetrieveProcessingHandle(CSegmentation *self, Msg_MostTel_t *tel_ptr); +static void Segm_RxStoreProcessingHandle(CSegmentation *self, Mns_AmsRx_Msg_t *msg_ptr); +static bool Segm_RxSearchProcessingHandle(void *current_data, void *search_data); +static bool Segm_RxGcSetLabel(void *current_data, void *search_data); +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId0(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); +static void Segm_RxProcessTelId1(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); +static void Segm_RxProcessTelId2(CSegmentation *self, Msg_MostTel_t *tel_ptr); +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId3(CSegmentation *self, Msg_MostTel_t *tel_ptr); +static void Segm_RxProcessTelId4(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization methods */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CSegmentation + * \param self The instance + * \param base_ptr Reference to base services + * \param pool_ptr Reference to the (Rx) message pool + * \param rx_def_payload_sz Default memory size that is allocated when receiving segmented messages + * without size prefix + */ +void Segm_Ctor(CSegmentation *self, CBase *base_ptr, CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->base_ptr = base_ptr; /* initialize members */ + self->pool_ptr = pool_ptr; + + self->rx_default_payload_sz = rx_def_payload_sz; + + Dl_Ctor(&self->processing_list, self->base_ptr->mns_inst_id); + T_Ctor(&self->gc_timer); + Tm_SetTimer(&self->base_ptr->tm, /* start garbage collector timer */ + &self->gc_timer, + &Segm_RxGcScanProcessingHandles, + self, + SEGM_GC_TIMEOUT, + SEGM_GC_TIMEOUT + ); +} + +/*! \brief Constructor of class CSegmentation + * \param self The instance + * \param error_fptr Reference to segmentation error callback function + * \param error_inst Reference to segmentation error instance + */ +void Segm_AssignRxErrorHandler(CSegmentation *self, Segm_OnError_t error_fptr, void *error_inst) +{ + self->error_fptr = error_fptr; + self->error_inst = error_inst; +} + +/*! \brief Performs cleanup of pending messages + * \param self The instance + */ +void Segm_Cleanup(CSegmentation *self) +{ + CDlNode *node_ptr = NULL; + /* cleanup Tx queue */ + for (node_ptr = Dl_PopHead(&self->processing_list); node_ptr != NULL; node_ptr = Dl_PopHead(&self->processing_list)) + { + Mns_AmsRx_Msg_t *rx_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + + Amsp_FreeRxPayload(self->pool_ptr, rx_ptr); + Amsp_FreeRxObj(self->pool_ptr, rx_ptr); + } +} + +/*------------------------------------------------------------------------------------------------*/ +/* Tx segmentation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Builds next MOST telegram according to given Application Messages + * \param self The instance + * \param msg_ptr Reference to the Application Message Tx handle + * \param tel_ptr Reference to the MOST Telegram handle + * \return Returns \c True if segmentation was completed for the Application Message. Otherwise \c false. + */ +bool Segm_TxBuildSegment(CSegmentation *self, Mns_AmsTx_Msg_t* msg_ptr, Msg_MostTel_t *tel_ptr) +{ + bool finished = false; + MISC_UNUSED(self); + + tel_ptr->destination_addr = msg_ptr->destination_address; + tel_ptr->id.fblock_id = msg_ptr->fblock_id; + tel_ptr->id.instance_id = msg_ptr->instance_id; + tel_ptr->id.function_id = msg_ptr->function_id; + tel_ptr->id.op_type = msg_ptr->op_type; + tel_ptr->opts.llrbc = msg_ptr->llrbc; + tel_ptr->info_ptr = msg_ptr; /* info_ptr must carry the reference to AMS Tx message object */ + tel_ptr->opts.cancel_id = Amsg_TxGetFollowerId(msg_ptr); + + if (msg_ptr->data_size <= SEGM_MAX_SIZE_TEL) /* is single transfer? */ + { + Msg_SetExtPayload((CMessage*)(void*)tel_ptr, msg_ptr->data_ptr, (uint8_t)msg_ptr->data_size, NULL); + finished = true; + } + else /* is segmented transfer? */ + { + uint16_t next_segm_cnt = Amsg_TxGetNextSegmCnt(msg_ptr); + + if (next_segm_cnt == 0xFFFFU) /* first segment: size prefixed segmented message TelId = "4" */ + { + tel_ptr->tel.tel_id = 4U; + tel_ptr->tel.tel_data_ptr[0] = MISC_HB(msg_ptr->data_size); + tel_ptr->tel.tel_data_ptr[1] = MISC_LB(msg_ptr->data_size); + tel_ptr->tel.tel_len = 2U; + } + else /* further segments: TelId = "1,2,3" */ + { + uint16_t index = next_segm_cnt * ((uint16_t)SEGM_MAX_SIZE_TEL - 1U); + uint16_t remaining_sz = msg_ptr->data_size - index; + uint8_t tel_sz = SEGM_MAX_SIZE_TEL - 1U; + + if (remaining_sz < SEGM_MAX_SIZE_TEL) + { + tel_ptr->tel.tel_id = 3U; /* is last segment */ + tel_sz = (uint8_t)remaining_sz; + finished = true; + } + else + { + if (0U == index) + { + tel_ptr->tel.tel_id = 1U; /* is first segment */ + } + else + { + tel_ptr->tel.tel_id = 2U; /* is subsequent segment */ + } + } + + tel_ptr->tel.tel_cnt = (uint8_t)next_segm_cnt; + Msg_SetExtPayload((CMessage*)(void*)tel_ptr, &msg_ptr->data_ptr[index], tel_sz, NULL); + } + + Amsg_TxIncrementNextSegmCnt(msg_ptr); + } + + return finished; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx pools */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Retrieves a processing Rx Application message object to a corresponding MOST telegram + * \param self The instance + * \param tel_ptr Reference to the MOST telegram + * \return The reference to the corresponding Rx Application Message or \c NULL if no appropriate + * Rx Application Message is available. + */ +static Mns_AmsRx_Msg_t* Segm_RxRetrieveProcessingHandle(CSegmentation *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + CDlNode *result_node_ptr = Dl_Foreach(&self->processing_list, &Segm_RxSearchProcessingHandle, tel_ptr); + + if (result_node_ptr != NULL) + { + Dl_Ret_t ret = Dl_Remove(&self->processing_list, result_node_ptr); + TR_ASSERT(self->base_ptr->mns_inst_id, "[SEGM]", (ret == DL_OK)); + msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(result_node_ptr); + MISC_UNUSED(ret); + } + + return msg_ptr; +} + +/*! \brief Stores a processing Rx Application message object into a dedicated list + * \param self The instance + * \param msg_ptr Reference to the Rx Application Message + */ +static void Segm_RxStoreProcessingHandle(CSegmentation *self, Mns_AmsRx_Msg_t *msg_ptr) +{ + Amsg_RxSetGcMarker(msg_ptr, false); + Amsg_RxEnqueue(msg_ptr, &self->processing_list); /* insert at tail, since garbage collector starts at head */ +} + +/*! \brief Performs garbage collection of outdated message objects + * \param self The instance + */ +void Segm_RxGcScanProcessingHandles(void *self) +{ + CSegmentation *self_ = (CSegmentation*)self; + /* first remove outdated messages */ + CDlNode *node_ptr = Dl_PeekHead(&self_->processing_list); /* get first candidate from head */ + + while (node_ptr != NULL) + { + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)Dln_GetData(node_ptr); + + if (Amsg_RxGetGcMarker(msg_ptr) != false) + { + Msg_MostTel_t tel; + + Amsg_RxCopySignatureToTel(msg_ptr, &tel); + self_->error_fptr(self_->error_inst, &tel, SEGM_ERR_5); + + (void)Dl_Remove(&self_->processing_list, node_ptr); + + Amsp_FreeRxPayload(self_->pool_ptr, msg_ptr); + Amsp_FreeRxObj(self_->pool_ptr, msg_ptr); + + node_ptr = Dl_PeekHead(&self_->processing_list); /* get next candidate from head */ + } + else + { + break; + } + } + + (void)Dl_Foreach(&self_->processing_list, &Segm_RxGcSetLabel, NULL); /* set label of all remaining messages */ +} + +/*! \brief Sets garbage collector flags for all list members + * \param current_data The Application message object present in list + * \param search_data unused (\c NULL) + * \return Returns always false in order to handle all list members */ +static bool Segm_RxGcSetLabel(void *current_data, void *search_data) +{ + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)current_data; + Amsg_RxSetGcMarker(msg_ptr, true); + MISC_UNUSED(search_data); + return false; +} + +/*! \brief Search routine to identify message objects with the same signature + * than a given MOST telegram + * \param current_data The Application message object present in list + * \param search_data The MOST Telegram object + * \return Returns \c true if both handles have the same functional signature, + * otherwise \c false. */ +static bool Segm_RxSearchProcessingHandle(void *current_data, void *search_data) +{ + Mns_AmsRx_Msg_t *msg_ptr = (Mns_AmsRx_Msg_t*)current_data; + Msg_MostTel_t *tel_ptr = (Msg_MostTel_t*)search_data; + + return Amsg_RxHandleIsIdentical(msg_ptr, tel_ptr); +} + +/*------------------------------------------------------------------------------------------------*/ +/* Rx segmentation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Processes segmentation for a received MOST telegram + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr The result of the segmentation process + * \return The completed Rx Application Message or \c NULL if segmentation process is still + * ongoing. + */ +Mns_AmsRx_Msg_t* Segm_RxExecuteSegmentation(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = NULL; + *result_ptr = SEGM_RES_OK; + + switch (tel_ptr->tel.tel_id) /* parasoft-suppress MISRA2004-15_3 "ignore unexpected TelIds" */ + { + case 0U: + msg_ptr = Segm_RxProcessTelId0(self, tel_ptr, result_ptr); + break; + case 1U: + Segm_RxProcessTelId1(self, tel_ptr, result_ptr); + break; + case 2U: + Segm_RxProcessTelId2(self, tel_ptr); + break; + case 3U: + msg_ptr = Segm_RxProcessTelId3(self, tel_ptr); + break; + case 4U: + Segm_RxProcessTelId4(self, tel_ptr, result_ptr); + break; + default: + break; + } + + return msg_ptr; /* return completed message */ +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="0" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr Result of segmentation process + * \return The completed Rx Application Message or \c NULL if segmentation process + * does not finish successfully. + */ +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId0(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + *result_ptr = SEGM_RES_OK; + + if (msg_ptr != NULL) /* treat error: segmentation process is ongoing */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_7); + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr);/* free assigned user payload and throw segmentation error */ + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); + msg_ptr = NULL; + } + /* try to allocate handle, memory is NetServices provided (payload <= 45 bytes) */ + msg_ptr = Amsp_AllocRxObj(self->pool_ptr, (uint16_t)tel_ptr->tel.tel_len); + + if (msg_ptr == NULL) + { + msg_ptr = Amsp_AllocRxRsvd(self->pool_ptr); + } + + if (msg_ptr != NULL) /* handle available: setup Rx Application Message */ + { + Amsg_RxCopySignatureFromTel(msg_ptr, tel_ptr); + + if (tel_ptr->tel.tel_len > 0U) + { /* copy payload to message */ + Amsg_RxCopyToPayload(msg_ptr, tel_ptr->tel.tel_data_ptr, tel_ptr->tel.tel_len); + } + else + { /* set payload length to zero */ + msg_ptr->data_ptr = NULL; + msg_ptr->data_size = 0U; + } + } + else + { + *result_ptr = SEGM_RES_RETRY; /* retry when next Rx object is released */ + } + + return msg_ptr; +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="1" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr Result of segmentation process + */ +static void Segm_RxProcessTelId1(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + *result_ptr = SEGM_RES_OK; + + if (tel_ptr->tel.tel_cnt != 0U) /* handle incorrect tel_cnt */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_3); + } + else /* tel_cnt is correct -> continue segmentation */ + { + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + bool is_size_prefixed = false; + + if (msg_ptr != NULL) /* has previous message */ + { + if ((Amsg_RxGetExpTelCnt(msg_ptr) != 0U) || (msg_ptr->data_size > 0U)) + { /* error: previous message already contains segments */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_7); + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); + Amsg_RxHandleSetup(msg_ptr); /* initialize message for re-use */ + } + else /* message and payload had been allocated by TelId '4' */ + { + is_size_prefixed = true; + } + } + else /* allocate message object if pre-allocation was not initiated by TelId "4" */ + { + msg_ptr = Amsp_AllocRxObj(self->pool_ptr, 0U); + } + + if (msg_ptr != NULL) /* now allocate payload */ + { + if (is_size_prefixed == false) + { + Amsg_RxCopySignatureFromTel(msg_ptr, tel_ptr); /* save signature and try to allocate */ + (void)Amsp_AllocRxPayload(self->pool_ptr, self->rx_default_payload_sz, msg_ptr); + } + + if (!Amsg_RxHasExternalPayload(msg_ptr)) /* allocation of payload failed */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_2); + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); + msg_ptr = NULL; + } + else /* allocation of payload succeeded */ + { + (void)Amsg_RxAppendPayload(msg_ptr, tel_ptr); + Segm_RxStoreProcessingHandle(self, msg_ptr); + msg_ptr = NULL; + } + } + else /* no message object allocated */ + { /* send segmentation error */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_4); + } + } +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="2" + * \param self The instance + * \param tel_ptr The received MOST telegram + */ +static void Segm_RxProcessTelId2(CSegmentation *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxProcessTelId3(self, tel_ptr); /* pretend having TelId '2' but store the */ + /* assembled message again */ + if (msg_ptr != NULL) + { + Segm_RxStoreProcessingHandle(self, msg_ptr); + } +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="3" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \return The assembled Rx Application Message or \c NULL if segmentation process + * did not process successfully. + */ +static Mns_AmsRx_Msg_t* Segm_RxProcessTelId3(CSegmentation *self, Msg_MostTel_t *tel_ptr) +{ + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + + if (msg_ptr == NULL) /* is first segment missing */ + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_1); + } + else + { + uint8_t exp_tel_cnt = Amsg_RxGetExpTelCnt(msg_ptr); + + if ((exp_tel_cnt == 0U) && (msg_ptr->data_size == 0U)) + { /* error: did not receive first segment */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_1); + Segm_RxStoreProcessingHandle(self, msg_ptr); + msg_ptr = NULL; + } + else if (exp_tel_cnt != tel_ptr->tel.tel_cnt) + { /* has wrong TelCnt */ + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_3); + Segm_RxStoreProcessingHandle(self, msg_ptr); + msg_ptr = NULL; + } + + if (msg_ptr != NULL) + { + bool succ = Amsg_RxAppendPayload(msg_ptr, tel_ptr); + + if (succ == false) + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_2); + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); + Amsp_FreeRxObj(self->pool_ptr, msg_ptr); + msg_ptr = NULL; + } + } + } + + return msg_ptr; +} + +/*! \brief Processes segmentation for a received MOST telegram with \c TelId="4" + * \param self The instance + * \param tel_ptr The received MOST telegram + * \param result_ptr Result of segmentation process + */ +static void Segm_RxProcessTelId4(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr) +{ + *result_ptr = SEGM_RES_OK; + + if (tel_ptr->tel.tel_len >= 2U) /* telegrams has necessary length */ + { + uint16_t msg_size; + MISC_DECODE_WORD(&msg_size, tel_ptr->tel.tel_data_ptr); + + if (msg_size > SEGM_MAX_SIZE_TEL) /* application message has correct size */ + { + Mns_AmsRx_Msg_t *msg_ptr = Segm_RxRetrieveProcessingHandle(self, tel_ptr); + + if (msg_ptr != NULL) /* treat error: segmentation process is ongoing */ + { + Amsp_FreeRxPayload(self->pool_ptr, msg_ptr); + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_7); + Amsg_RxHandleSetup(msg_ptr); /* initialize message for re-use */ + } + else + { /* try to allocate handle, memory is NetServices provided (payload <= 45 bytes) */ + msg_ptr = Amsp_AllocRxObj(self->pool_ptr, 0U); + } + + if (msg_ptr != NULL) /* allocation succeeded: decode length and allocate payload */ + { + Amsg_RxCopySignatureFromTel(msg_ptr, tel_ptr); + (void)Amsp_AllocRxPayload(self->pool_ptr, msg_size, msg_ptr); + Segm_RxStoreProcessingHandle(self, msg_ptr);/* store handle and don't care if payload was allocated or not */ + msg_ptr = NULL; /* segmentation error 2 is treated by TelId 1 */ + } + else + { + self->error_fptr(self->error_inst, tel_ptr, SEGM_ERR_4); + } + } + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_segmentation.h b/mnsl/mns_segmentation.h new file mode 100644 index 0000000..1295be3 --- /dev/null +++ b/mnsl/mns_segmentation.h @@ -0,0 +1,146 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of AMS Segmentation Class + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_AMSSEGM + * @{ + */ + +#ifndef MNS_SEGMENTATION_H +#define MNS_SEGMENTATION_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_amsmessage.h" +#include "mns_amspool.h" +#include "mns_base.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines the maximum payload size of a single transfer in bytes */ +#define SEGM_MAX_SIZE_TEL 45U + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Collection of possible segmentation errors */ +typedef enum Segm_Error_ +{ + SEGM_ERR_1 = 1, /*!< \brief The first segment is missing. */ + + SEGM_ERR_2 = 2, /*!< \brief The device is not able to receive a message of this size. + * MNS specific: The allocation of user provided payload failed. + */ + SEGM_ERR_3 = 3, /*!< \brief Unexpected segment number. */ + + SEGM_ERR_4 = 4, /*!< \brief Too many unfinished segmentation messages were pending. */ + + SEGM_ERR_5 = 5, /*!< \brief A timeout occurred while waiting for the next segment. */ + + SEGM_ERR_6 = 6, /*!< \brief The Device is not capable to handle segmented messages. + * MNS specific: The application did not assign the payload allocation + * function in Mns_Ams_InitData_t prior calling Mns_Init(). + */ + SEGM_ERR_7 = 7 /*!< \brief Segmented message has not been finished before the arrival of + * another message with the identical FBlockID, InstID, FktID, and + * OPType sent by the same node. + */ +} Segm_Error_t; + +/*! \brief Segmentation result */ +typedef enum Segm_Result_ +{ + SEGM_RES_OK, /*!< \brief Telegram was processed */ + SEGM_RES_RETRY /*!< \brief Telegram shall be processed again as soon as messages are freed to the Rx pool */ + +} Segm_Result_t; + +/*! \brief Callback function to notify that a segmentation error has occurred + * \param self The instance + * \param tel_ptr The affected telegram + * \param error The segmentation error code (1..7) + */ +typedef void (*Segm_OnError_t)(void *self, Msg_MostTel_t *tel_ptr, Segm_Error_t error); + +/*------------------------------------------------------------------------------------------------*/ +/* Class */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief AMS Segmentation Class + * \details Performs Tx and Rx Segmentation + */ +typedef struct CSegmentation_ +{ + CBase *base_ptr; /*!< \brief Reference to base services */ + CAmsMsgPool *pool_ptr; /*!< \brief Reference to object/payload pool */ + + Segm_OnError_t error_fptr; /*!< \brief Callback function to notify segmentation errors */ + void *error_inst; /*!< \brief Instance which is notified on segmentation errors */ + + CDlList processing_list; /*!< \brief Segmented and un-finished Rx messages */ + CTimer gc_timer; /*!< \brief Timer to trigger the garbage collector */ + uint16_t rx_default_payload_sz; /*!< \brief Payload size that shall be allocated if size-prefixes + * segmentation message is missing */ + +} CSegmentation; + +/*------------------------------------------------------------------------------------------------*/ +/* Initialization Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Segm_Ctor(CSegmentation *self, CBase *base_ptr, CAmsMsgPool *pool_ptr, uint16_t rx_def_payload_sz); +extern void Segm_AssignRxErrorHandler(CSegmentation *self, Segm_OnError_t error_fptr, void *error_inst); +extern void Segm_Cleanup(CSegmentation *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Public method prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern bool Segm_TxBuildSegment(CSegmentation *self, Mns_AmsTx_Msg_t *msg_ptr, Msg_MostTel_t *tel_ptr); +extern Mns_AmsRx_Msg_t* Segm_RxExecuteSegmentation(CSegmentation *self, Msg_MostTel_t *tel_ptr, Segm_Result_t *result_ptr); +extern void Segm_RxGcScanProcessingHandles(void *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MNS_SEGMENTATION_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_telqueue.c b/mnsl/mns_telqueue.c new file mode 100644 index 0000000..c250507 --- /dev/null +++ b/mnsl/mns_telqueue.c @@ -0,0 +1,119 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of class CTelQueue + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MSG_QUEUE + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_telqueue.h" +#include "mns_misc.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal constants */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CTelQueue + * \param self The instance + * \param mns_inst_id MOST NetServices instance id + */ +void Telq_Ctor(CTelQueue *self, uint8_t mns_inst_id) +{ + self->mns_inst_id = mns_inst_id; + Dl_Ctor(&self->list, self->mns_inst_id); +} + +/*! \brief Retrieves the head object of the telegram queue + * \param self The instance + * \return Reference to the telegram if a telegram object is available. + * Otherwise \c NULL. + */ +Msg_MostTel_t* Telq_Dequeue(CTelQueue *self) +{ + Msg_MostTel_t *tel_ptr = NULL; + CDlNode *node_ptr = Dl_PopHead(&self->list); + + if (node_ptr != NULL) + { + tel_ptr = (Msg_MostTel_t*)Dln_GetData(node_ptr); + } + + return tel_ptr; +} + +/*! \brief Retrieves a reference to the head object + * without removing it from the telegram queue + * \param self The instance + * \return Reference to the telegram if a telegram object is available. + * Otherwise \c NULL. + */ +Msg_MostTel_t* Telq_Peek(CTelQueue *self) +{ + Msg_MostTel_t *tel_ptr = NULL; + CDlNode *node_ptr = Dl_PeekHead(&self->list); + + if (node_ptr != NULL) + { + tel_ptr = (Msg_MostTel_t*)Dln_GetData(node_ptr); + } + + return tel_ptr; +} + +/*! \brief Adds a telegram to the tail of the queue + * \param self The instance + * \param tel_ptr Reference to the telegram + */ +void Telq_Enqueue(CTelQueue *self, Msg_MostTel_t *tel_ptr) +{ + Dl_InsertTail(&self->list, Msg_GetNode((CMessage*)(void*)tel_ptr)); +} + +/*! \brief Retrieves the current number of objects in the telegram queue + * \param self The instance + * \return The current number of available telegram objects in the pool + */ +uint8_t Telq_GetSize(CTelQueue *self) +{ + return (uint8_t)Dl_GetSize(&self->list); +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_telqueue.h b/mnsl/mns_telqueue.h new file mode 100644 index 0000000..2a86387 --- /dev/null +++ b/mnsl/mns_telqueue.h @@ -0,0 +1,93 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of class CTelQueue + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_MSG_QUEUE + * @{ + */ + +#ifndef MNS_MSGQUEUE_H +#define MNS_MSGQUEUE_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_dl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Class CTelQueue */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CTelQueue + * \details Internal class to queue MOST telegrams + */ +typedef struct CTelQueue_ +{ + CDlList list; /*! \brief Doubly linked list */ + uint8_t mns_inst_id; /*! \brief MOST NetServices instance ID */ + +} CTelQueue; + + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +extern void Telq_Ctor(CTelQueue *self, uint8_t mns_inst_id); +extern Msg_MostTel_t* Telq_Dequeue(CTelQueue *self); +extern Msg_MostTel_t* Telq_Peek(CTelQueue *self); +extern void Telq_Enqueue(CTelQueue *self, Msg_MostTel_t *tel_ptr); +extern uint8_t Telq_GetSize(CTelQueue *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_MSGQUEUE_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_timer.c b/mnsl/mns_timer.c new file mode 100644 index 0000000..c28d32b --- /dev/null +++ b/mnsl/mns_timer.c @@ -0,0 +1,454 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the timer management module. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TIMER + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_timer.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Service parameters */ +/*------------------------------------------------------------------------------------------------*/ +/*! Priority of the TM service used by scheduler */ +static const uint8_t TM_SRV_PRIO = 255U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */ +/*! Main event for the TM service */ +static const Srv_Event_t TM_EVENT_UPDATE_TIMERS = 1U; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Tm_Service(void *self); +static void Tm_UpdateTimers(CTimerManagement *self); +static bool Tm_HandleElapsedTimer(CTimerManagement *self); +static bool Tm_UpdateTimersAdd(void *c_timer_ptr, void *n_timer_ptr); +static void Tm_SetTimerInternal(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CTimerManagement */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the timer management class. + * \param self Instance pointer + * \param scd Scheduler instance + * \param init_ptr Reference to the initialization data + */ +void Tm_Ctor(CTimerManagement *self, CScheduler *scd, const Tm_InitData_t *init_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = init_ptr->mns_inst_id; + /* Initialize subjects and add observers */ + Ssub_Ctor(&self->get_tick_count_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->get_tick_count_subject, + init_ptr->get_tick_count_obs_ptr); + if(init_ptr->set_application_timer_obs_ptr != NULL) + { + self->delayed_tm_service_enabled = true; + Ssub_Ctor(&self->set_application_timer_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->set_application_timer_subject, + init_ptr->set_application_timer_obs_ptr); + } + /* Initialize timer management service */ + Srv_Ctor(&self->tm_srv, TM_SRV_PRIO, self, &Tm_Service); + /* Add timer management service to scheduler */ + (void)Scd_AddService(scd, &self->tm_srv); +} + +/*! \brief Service function of the timer management. + * \param self Instance pointer + */ +static void Tm_Service(void *self) +{ + CTimerManagement *self_ = (CTimerManagement *)self; + Srv_Event_t event_mask; + + Srv_GetEvent(&self_->tm_srv, &event_mask); + + if(TM_EVENT_UPDATE_TIMERS == (event_mask & TM_EVENT_UPDATE_TIMERS)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->tm_srv, TM_EVENT_UPDATE_TIMERS); + Tm_UpdateTimers(self_); + } +} + +/*! \brief If event TM_EVENT_UPDATE_TIMERS is set this function is called. Handles the update + * of the timer list. If a timer has expired the corresponding callback function is + * executed. If the expired timer is a periodic timer, the timer will be set again. + * \param self Instance pointer + */ +static void Tm_UpdateTimers(CTimerManagement *self) +{ + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + + if(self->timer_list.head != NULL) /* At least one timer is running? */ + { + bool continue_loop = true; + /* Calculate time difference between the current and the last TM service run */ + uint16_t tick_count_diff = (uint16_t)(current_tick_count - self->last_tick_count); + /* Save current tick count for next service run */ + self->last_tick_count = current_tick_count; + + /* Loop while timer list is not empty */ + while((self->timer_list.head != NULL) && (continue_loop!= false)) + { + /* Is not first timer in list elapsed yet? */ + if(tick_count_diff <= ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Update delta of first timer in list */ + ((CTimer *)self->timer_list.head->data_ptr)->delta -= tick_count_diff; + tick_count_diff = 0U; + } + else /* At least first timer in list elapsed */ + { + /* Update tick count difference for next timer in list */ + tick_count_diff -= ((CTimer *)self->timer_list.head->data_ptr)->delta; + /* First timer elapsed */ + ((CTimer *)self->timer_list.head->data_ptr)->delta = 0U; + } + + /* First timer in list elapsed? */ + if(0U == ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Handle elapsed timer */ + continue_loop = Tm_HandleElapsedTimer(self); + } + else /* No elapsed timer in list. */ + { + /* First timer in list updated! Set trigger to inform application (see + Tm_CheckForNextService()) and stop TM service. */ + self->set_service_timer = true; + continue_loop = false; + } + } + } +} + +/*! \brief This function is called if the first timer in list is elapsed. The timer handler + * callback function is invoked. If the timer is a periodic timer it is wound up again. + * \param self Instance pointer + * \return \c true if the next timer must be check. + * \return \c false if the wound up timer (periodic timer) is new head of timer list + */ +static bool Tm_HandleElapsedTimer(CTimerManagement *self) +{ + bool ret_val = true; + + CDlNode *node = self->timer_list.head; + /* Reset flag to be able to check if timer object has changed within handler + callback function */ + ((CTimer *)node->data_ptr)->changed = false; + /* Call timer handler callback function */ + ((CTimer *)node->data_ptr)->handler_fptr(((CTimer *)node->data_ptr)->args_ptr); + + /* Timer object hasn't changed within handler callback function? */ + if(false == ((CTimer *)node->data_ptr)->changed) + { + /* Remove current timer from list */ + (void)Dl_Remove(&self->timer_list, node); + /* Mark timer as unused */ + ((CTimer *)node->data_ptr)->in_use = false; + /* Is current timer a periodic timer? */ + if(((CTimer *)node->data_ptr)->period > 0U) + { + /* Reload current timer */ + Tm_SetTimerInternal(self, + ((CTimer *)node->data_ptr), + ((CTimer *)node->data_ptr)->handler_fptr, + ((CTimer *)node->data_ptr)->args_ptr, + ((CTimer *)node->data_ptr)->period, + ((CTimer *)node->data_ptr)->period); + + if(node == self->timer_list.head) /* Is current timer new head of list? */ + { + /* Set trigger to inform application (see Tm_CheckForNextService()) and + stop TM service. */ + self->set_service_timer = true; + ret_val = false; + } + } + } + + return ret_val; +} + +/*! \brief Calls an application callback function to inform the application that the MNS must be + * serviced not later than the passed time period. If the timer list is empty a possible + * running application timer will be stopped. This function is called at the end of + * Mns_Service(). + * \param self Instance pointer + */ +void Tm_CheckForNextService(CTimerManagement *self) +{ + if(self->delayed_tm_service_enabled != false) + { + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + /* Has head of timer list changed? */ + if(self->set_service_timer != false) + { + uint16_t new_time; + uint16_t diff = current_tick_count - self->last_tick_count; + self->set_service_timer = false; + /* Timer expired since last TM service? */ + if(diff >= ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + new_time = 1U; /* Return minimum value */ + } + else + { + /* Calculate new timeout */ + new_time = (uint16_t)(((CTimer *)self->timer_list.head->data_ptr)->delta - diff); + } + /* Inform the application that the MNS must be serviced not later than the passed + time period. */ + Ssub_Notify(&self->set_application_timer_subject, &new_time, false); + } + } + else + { + Tm_TriggerService(self); /* Application timer not implemented -> Retrigger TM */ + } +} + +/*! \brief Helper function to set the TM service event. + * \details This function is used by the application to trigger a service call of the Timer + * Management if the application timer has expired. + * \param self Instance pointer + */ +void Tm_TriggerService(CTimerManagement *self) +{ + if(self->timer_list.head != NULL) /* At least one timer is running? */ + { + Srv_SetEvent(&self->tm_srv, TM_EVENT_UPDATE_TIMERS); + } +} + +/*! \brief Helper function to stop the TM service. + * \param self Instance pointer + */ +void Tm_StopService(CTimerManagement *self) +{ + uint16_t new_time = 0U; + + /* Clear probable running application timer */ + Ssub_Notify(&self->set_application_timer_subject, &new_time, false); + + /* Reset the service timer. Not necessary ? */ + self->set_service_timer = false; + + /* Clear the timer head queue to prevent any event to be set */ + self->timer_list.head = NULL; +} + +/*! \brief Creates a new timer. The timer expires at the specified elapse time and then after + * every specified period. When the timer expires the specified callback function is + * called. + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \param handler_fptr Callback function which is called when the timer expires + * \param args_ptr Reference to an optional parameter which is passed to the specified + * callback function + * \param elapse The elapse value before the timer expires for the first time, in + * milliseconds + * \param period The period of the timer, in milliseconds. If this parameter is zero, the + * timer is signaled once. If the parameter is greater than zero, the timer + * is periodic. + */ +void Tm_SetTimer(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period) +{ + (void)Tm_ClearTimer(self, timer_ptr); /* Clear timer if running */ + /* Call the internal method to set the new timer (-> does not trigger TM service!) */ + Tm_SetTimerInternal(self, timer_ptr, handler_fptr, args_ptr, elapse, period); + Tm_TriggerService(self); /* New timer added -> trigger timer list update */ +} + +/*! \brief This function contains the internal part when adding a new timer. The function is + * called within Tm_SetTimer() and within Tm_UpdateTimers(). + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \param handler_fptr Callback function which is called when the timer expires + * \param args_ptr Reference to an optional parameter which is passed to the specified + * callback function + * \param elapse The elapse value before the timer expires for the first time, in + * milliseconds + * \param period The period of the timer, in milliseconds. If this parameter is zero, the + * timer is signaled once. If the parameter is greater than zero, the timer + * is periodic. + */ +static void Tm_SetTimerInternal(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period) +{ + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + + /* Save timer specific values */ + timer_ptr->changed = true; /* Flag is needed by Tm_UpdateTimers() */ + timer_ptr->in_use = true; + timer_ptr->handler_fptr = handler_fptr; + timer_ptr->args_ptr = args_ptr; + timer_ptr->elapse = elapse; + timer_ptr->period = period; + timer_ptr->delta = elapse; + + /* Create back link to be able to point from node to timer object */ + timer_ptr->node.data_ptr = (void *)timer_ptr; + + if(NULL == self->timer_list.head) /* Is timer list empty? */ + { + Dl_InsertHead(&self->timer_list, &timer_ptr->node); /* Add first timer to list */ + /* Save current tick count */ + Ssub_Notify(&self->get_tick_count_subject, &self->last_tick_count, false); + } + else /* Timer list is not empty */ + { + CDlNode *result_ptr = NULL; + + /* Set delta value in relation to last saved tick count (last TM service) */ + timer_ptr->delta += (uint16_t)(current_tick_count - self->last_tick_count); + + /* Search slot where new timer must be inserted. Update delta of new timer + and delta of the following timer in the list. */ + result_ptr = Dl_Foreach(&self->timer_list, &Tm_UpdateTimersAdd, (void *)timer_ptr); + + if(NULL != result_ptr) /* Slot found? */ + { + /* Insert new timer at found position */ + Dl_InsertBefore(&self->timer_list, result_ptr, &timer_ptr->node); + } + else /* No slot found -> Insert as last node */ + { + /* Add new timer to end of list */ + Dl_InsertTail(&self->timer_list, &timer_ptr->node); + } + } +} + +/*! \brief Removes the specified timer from the timer list. + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \attention Make sure that for a timer object Tm_SetTimer() is called before Tm_ClearTimer() + * is called! + */ +void Tm_ClearTimer(CTimerManagement *self, CTimer *timer_ptr) +{ + if(timer_ptr->in_use != false) /* Is timer currently in use? */ + { + timer_ptr->changed = true; /* Flag is needed by Tm_UpdateTimers() */ + + if(NULL != timer_ptr->node.next) /* Has deleted timer a follower? */ + { + /* Adjust delta of following timer */ + ((CTimer *)timer_ptr->node.next->data_ptr)->delta += timer_ptr->delta; + } + + (void)Dl_Remove(&self->timer_list, &timer_ptr->node); + timer_ptr->in_use = false; + + Tm_TriggerService(self); /* Timer removed -> trigger timer list update */ + } +} + +/*! \brief Used by Tm_SetTimer() to find the slot where the new timer must be inserted. + * \param c_timer_ptr Reference to current timer processed by foreach loop + * \param n_timer_ptr Reference to new timer + * \return \c true: Slot found, stop foreach loop + * \return \c false: Slot not found, continue foreach loop + */ +static bool Tm_UpdateTimersAdd(void *c_timer_ptr, void *n_timer_ptr) +{ + CTimer *current_timer_ptr = (CTimer *)c_timer_ptr; + CTimer *new_timer_ptr = (CTimer *)n_timer_ptr; + bool ret_val; + + /* Is current timer lesser than new timer? */ + if(current_timer_ptr->delta <= new_timer_ptr->delta) + { + /* Update delta of new timer and continue foreach loop */ + new_timer_ptr->delta -= current_timer_ptr->delta; + ret_val = false; + } + else /* Slot found! */ + { + /* Correct delta of current timer and stop foreach loop */ + current_timer_ptr->delta -= new_timer_ptr->delta; + ret_val = true; + } + + return ret_val; +} + + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CTimer */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the Timer class. + * \param self Instance pointer + */ +void T_Ctor(CTimer *self) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); +} + +/*! \brief Returns the status of the given timer. + * \param self Instance pointer + * \return \c true if the timer is currently in use + * \return \c false if the timer is not currently in use + */ +bool T_IsTimerInUse(CTimer *self) +{ + return self->in_use; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_timer.h b/mnsl/mns_timer.h new file mode 100644 index 0000000..9e5c02a --- /dev/null +++ b/mnsl/mns_timer.h @@ -0,0 +1,146 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the timer management module. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TIMER + * @{ + */ + +#ifndef MNS_TIMER_H +#define MNS_TIMER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_cfg.h" +#include "mns_dl.h" +#include "mns_scheduler.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Function signature used for timer handler + * \param args Void pointer to optional data + */ +typedef void (*Tm_Handler_t)(void *args); + +/*------------------------------------------------------------------------------------------------*/ +/* Structures */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initialization structure of the timer management module. */ +typedef struct Tm_InitData_ +{ + /*! \brief Observer used to request current tick count value */ + CSingleObserver *get_tick_count_obs_ptr; + /*! \brief Observer used to start application timer for delayed TM service calls */ + CSingleObserver *set_application_timer_obs_ptr; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} Tm_InitData_t; + +/*! \brief Class structure of the a timer object. */ +typedef struct CTimer_ +{ + /*! \brief Node of the doubly linked (timer-) list */ + CDlNode node; + /*! \brief Handler function which is invoked when the timer expires */ + Tm_Handler_t handler_fptr; + /*! \brief Reference to optional parameter */ + void *args_ptr; + /*! \brief The Timeout value before the timer expires for the first time, in milliseconds */ + uint16_t elapse; + /*! \brief The period of the timer, in milliseconds */ + uint16_t period; + /*! \brief Delta time related to next timer in list */ + uint16_t delta; + /*! \brief Flag which signals that the timer is in use */ + bool in_use; + /*! \brief Flag to check if timer object has changed within timer handler callback function */ + bool changed; + +} CTimer; + +/*! \brief Class structure of the timer management */ +typedef struct CTimerManagement_ +{ + /*! \brief Doubly linked list to manage the active timers */ + CDlList timer_list; + /*! \brief Subject to request current tick count */ + CSingleSubject get_tick_count_subject; + /*! \brief Subject to start the application timer which triggers a MNS service call */ + CSingleSubject set_application_timer_subject; + /*! \brief Service instance to add the timer management to the scheduler */ + CService tm_srv; + /*! \brief Last tick count value (saved at TM service) */ + uint16_t last_tick_count; + /*! \brief Signals that the application timer callbacks are used */ + bool delayed_tm_service_enabled; + /*! \brief Indicates that the application timer must be started */ + bool set_service_timer; + /*! \brief MOST NetServices instance ID */ + uint8_t mns_inst_id; + +} CTimerManagement; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CTimerManagement */ +/*------------------------------------------------------------------------------------------------*/ +extern void Tm_Ctor(CTimerManagement *self, CScheduler *scd, const Tm_InitData_t *init_ptr); +extern void Tm_SetTimer(CTimerManagement *self, CTimer *timer_ptr, Tm_Handler_t handler_fptr, + void *args_ptr, uint16_t elapse, uint16_t period); +extern void Tm_ClearTimer(CTimerManagement *self, CTimer *timer_ptr); +extern void Tm_CheckForNextService(CTimerManagement *self); +extern void Tm_TriggerService(CTimerManagement *self); +extern void Tm_StopService(CTimerManagement *self); + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes of class CTimer */ +/*------------------------------------------------------------------------------------------------*/ +extern void T_Ctor(CTimer *self); +extern bool T_IsTimerInUse(CTimer *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_TIMER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_trace.h b/mnsl/mns_trace.h new file mode 100644 index 0000000..7ef4758 --- /dev/null +++ b/mnsl/mns_trace.h @@ -0,0 +1,87 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Internal header file of the trace interface + */ + +#ifndef MNSL_TRACE_H +#define MNSL_TRACE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Unit and entry ids */ +/*------------------------------------------------------------------------------------------------*/ +#define TR_MNSL_ASSERT "ASSERT failed in line %d" +#define TR_MNSL_INIC_RESULT_ID_1 "INIC error data:" +#define TR_MNSL_INIC_RESULT_ID_2 "--> Data[%u]: 0x%02X" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal macros */ +/*------------------------------------------------------------------------------------------------*/ + +/* parasoft suppress MISRA2004-19_7 MISRA2004-19_4 reason "function-like macros are allowed for tracing" */ +#ifdef MNS_TR_ERROR +# define TR_ERROR(args) MNS_TR_ERROR args; +# define TR_FAILED_ASSERT(mns_inst_id, unit) TR_ERROR(((mns_inst_id), (unit), TR_MNSL_ASSERT, 1U, __LINE__)) +# define TR_ASSERT(mns_inst_id, unit, expr) if (!(expr)) {TR_FAILED_ASSERT((mns_inst_id), (unit));} +# define TR_ERROR_INIC_RESULT(mns_inst_id, unit, info_ptr, info_size) \ + { \ + uint8_t i; \ + TR_ERROR(((mns_inst_id), (unit), TR_MNSL_INIC_RESULT_ID_1, 0U)); \ + for(i=0U; i. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of class CTransceiver + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TRCV + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_transceiver.h" +#include "mns_misc.h" +#include "mns_pmp.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Trcv_OnTxStatusInternal(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CTransceiver + * \param self The instance + * \param fifo_ptr Reference to the dedicated port message FIFO + * \param def_src_addr Source address that is preset in Tx message object + * \param mns_inst_id MOST NetServices instance ID + * \param trace_id ID specifies FIFO in traces if multiple transceivers are running + */ +void Trcv_Ctor(CTransceiver *self, CPmFifo *fifo_ptr, uint16_t def_src_addr, uint8_t mns_inst_id, uint8_t trace_id) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->fifo_ptr = fifo_ptr; + self->tx_def_src = def_src_addr; + self->mns_inst_id = mns_inst_id; + self->own_id = trace_id; + Pool_Ctor(&self->tx_msg_pool, self->tx_msgs, TRCV_SIZE_TX_POOL, mns_inst_id); + TR_ASSERT(self->mns_inst_id, "[TRCV]", (fifo_ptr != NULL)); +} + +/*! \brief Assigns a function of another class to receive messages + * \details The assigned function is responsible to call Trcv_RxReleaseMsg() it has finished to process it + * \param self The instance + * \param callback_fptr Callback function + * \param inst_ptr The instance of the receiver class + */ +void Trcv_RxAssignReceiver(CTransceiver *self, Trcv_RxCompleteCb_t callback_fptr, void *inst_ptr) +{ + self->rx_complete_fptr = callback_fptr; + self->rx_complete_inst = inst_ptr; +} + +/*! \brief Assigns a function of another class to filter Rx messages + * \details The assigned function is responsible to discard or pass Rx messages + * \param self The instance + * \param callback_fptr Callback function + * \param inst_ptr The instance of the filter class + */ +void Trcv_RxAssignFilter(CTransceiver *self, Trcv_RxFilterCb_t callback_fptr, void *inst_ptr) +{ + self->rx_filter_fptr = callback_fptr; + self->rx_filter_inst = inst_ptr; +} + +/*! \brief Releases an Rx message which was received by the assigned receiver + * \param self The instance + * \param tel_ptr Reference to the received message + */ +void Trcv_RxReleaseMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr) +{ + CMessage *msg_ptr = (CMessage*)(void*)tel_ptr; + bool check_ok = !Dln_IsNodePartOfAList(Msg_GetNode(msg_ptr)); /* message object shall not be part of a list */ + /* because it was provided in an earlier step */ + TR_ASSERT(self->mns_inst_id, "[TRCV]", check_ok); + if (check_ok) + { + Fifo_RxReleaseMsg(self->fifo_ptr, msg_ptr); + } +} + +/*! \brief Retrieves a message object from the pool + * \param self The instance + * \param size Size of the message in bytes. Valid range: 0..45. + * \return Reference to the Msg_MostTel_t structure if a message is available. + * Otherwise \c NULL. + */ +extern Msg_MostTel_t* Trcv_TxAllocateMsg(CTransceiver *self, uint8_t size) +{ + const uint8_t TRCV_CTRL_MAX_SIZE = 45U; /* replace by PMS constant in future */ + CMessage *handle = NULL; + Msg_MostTel_t *tel_ptr = NULL; + + if (size <= TRCV_CTRL_MAX_SIZE) + { + handle = Pool_GetMsg(&self->tx_msg_pool); + + if (handle != NULL) + { + Msg_Cleanup(handle); /* reset headers and fields */ + Msg_ReserveHeader(handle, PMP_PM_MAX_SIZE_HEADER + ENC_MAX_SIZE_CONTENT); + tel_ptr = Msg_GetMostTel(handle); /* return public struct of the message object */ + tel_ptr->tel.tel_id = 0U; + tel_ptr->tel.tel_len = size; + tel_ptr->tel.tel_cnt = 0U; + tel_ptr->source_addr = self->tx_def_src; + } + } + + return tel_ptr; +} + +/*! \brief Returns a message object to the transceiver pool a message was allocated from + * \param tel_ptr Reference to the message object which needs to be returned. + */ +void Trcv_TxReleaseMsg(Msg_MostTel_t *tel_ptr) +{ + CMessage* msg_ptr = (CMessage*)(void*)tel_ptr; /* avoid MISRA-C warning by converting to "void*" */ + bool check_ok = !Dln_IsNodePartOfAList(Msg_GetNode(msg_ptr)); /* message object shall not be part of a list */ + TR_ASSERT(0U, "[TRCV]", check_ok); /* because it was provided in an earlier step */ + + if (check_ok) + { + Pool_ReturnMsg(msg_ptr); + } +} + +/*! \brief Prepares a message object for re-transmission + * \param tel_ptr Reference to the Tx message object which needs + * to be reused. + */ +void Trcv_TxReuseMsg(Msg_MostTel_t *tel_ptr) +{ + CMessage* msg_ptr = (CMessage*)(void*)tel_ptr; + TR_ASSERT(0U, "[TRCV]", (!Dln_IsNodePartOfAList(Msg_GetNode(msg_ptr)))); /* message object shall not be part of a list */ + /* because it was provided in an earlier step */ + Msg_Cleanup(msg_ptr); /* reset headers and fields */ + Msg_ReserveHeader(msg_ptr, PMP_PM_MAX_SIZE_HEADER + ENC_MAX_SIZE_CONTENT); +} + +/*! \brief Transmits a given message object to the INIC + * \details After completed transmission the message object is released automatically + * \param self The instance + * \param tel_ptr Reference to the message object + */ +void Trcv_TxSendMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr) +{ + CMessage *msg_ptr; + + TR_ASSERT(self->mns_inst_id, "[TRCV]", (tel_ptr != NULL)); + msg_ptr = (CMessage*)(void*)tel_ptr; + + TR_INFO((self->mns_inst_id, "[TRCV]", "Trcv_TxSendMsg(): FIFO: %u, MSG(tgt:0x%04X, id:%02X.%01X.%04X.%01X)", 6U, self->own_id, tel_ptr->destination_addr, tel_ptr->id.fblock_id, tel_ptr->id.instance_id, tel_ptr->id.function_id, tel_ptr->id.op_type)); + Msg_SetTxStatusHandler(msg_ptr, &Trcv_OnTxStatusInternal, self); /* just release the message */ + Fifo_Tx(self->fifo_ptr, msg_ptr, false); +} + +/*! \brief Transmits a given message object to the INIC with a dedicated result callback + * \param self The instance + * \param tel_ptr Reference to the message object + * \param callback_fptr Callback function which is invoked after message transmission has finished. + * Must be \c NULL to avoid that a callback function is invoked. In this case + * the message object is freed internally. Hence, the message object must + * not provide external payload. + * \param inst_ptr Reference to the instance which is invoked with callback_fptr. Has to be \c + * NULL if callback_fptr is \c NULL. + * \note The provided callback function is responsible to free the message object by calling + * Trcv_TxReleaseMsg() or to reuse the message object by calling Trcv_TxReuseMsg() before + * passing it to one of the transmit functions again. + */ +void Trcv_TxSendMsgExt(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr) +{ + CMessage *msg_ptr; + + TR_ASSERT(self->mns_inst_id, "[TRCV]", (tel_ptr != NULL)); + msg_ptr = (CMessage*)(void*)tel_ptr; + + if (callback_fptr == NULL) + { + TR_ASSERT(self->mns_inst_id, "[TRCV]", (inst_ptr == NULL)); + callback_fptr = &Trcv_OnTxStatusInternal; + inst_ptr = self; + } + + TR_INFO((self->mns_inst_id, "[TRCV]", "Trcv_TxSendMsgExt(): FIFO: %u, MSG(tgt:0x%04X, id:%02X.%01X.%04X.%01X)", 6U, self->own_id, tel_ptr->destination_addr, tel_ptr->id.fblock_id, tel_ptr->id.instance_id, tel_ptr->id.function_id, tel_ptr->id.op_type)); + Msg_SetTxStatusHandler(msg_ptr, callback_fptr, inst_ptr); + Fifo_Tx(self->fifo_ptr, msg_ptr, false); +} + +/*! \brief Transmits a given message object to the INIC bypassing all other messages in the FIFO + * \param self The instance + * \param tel_ptr Reference to the message object + * \param callback_fptr Callback function which is invoked after message transmission has finished. + * Must be \c NULL to avoid that a callback function is invoked. In this case + * the message object is freed internally. Hence, the message object must + * not provide external payload. + * \param inst_ptr Reference to the instance which is invoked + * \note The provided callback function is responsible to free the message object by calling + * Trcv_TxReleaseMsg() or to reuse the message object by calling Trcv_TxReuseMsg() before + * passing it to one of the transmit functions again. + */ +void Trcv_TxSendMsgBypass(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr) +{ + CMessage *msg_ptr; + + TR_ASSERT(self->mns_inst_id, "[TRCV]", (tel_ptr != NULL)); + msg_ptr = (CMessage*)(void*)tel_ptr; + + if (callback_fptr == NULL) + { + TR_ASSERT(self->mns_inst_id, "[TRCV]", (inst_ptr == NULL)); + callback_fptr = &Trcv_OnTxStatusInternal; + inst_ptr = self; + } + + Msg_SetTxStatusHandler(msg_ptr, callback_fptr, inst_ptr); + Fifo_Tx(self->fifo_ptr, msg_ptr, true); +} + +/*! \brief Callback function which is invoked instead of an external callback + * as soon as channel transmission was finished in PMS. + * \param self The instance + * \param tel_ptr Reference to the message object + * \param status Transmission status + */ +static void Trcv_OnTxStatusInternal(void *self, Msg_MostTel_t *tel_ptr, Mns_MsgTxStatus_t status) +{ + Trcv_TxReleaseMsg(tel_ptr); + MISC_UNUSED(self); + MISC_UNUSED(status); +} + +/*! \brief Internal callback function which is intended to be + * invoked by the port message channel on completed reception. + * \param self The instance + * \param tel_ptr Reference to the message object + */ +void Trcv_RxOnMsgComplete(void *self, CMessage *tel_ptr) +{ + CTransceiver *self_ = (CTransceiver*)self; + bool discard = false; + + TR_INFO((self_->mns_inst_id, "[TRCV]", "Trcv_RxOnMsgComplete(): FIFO: %u, MSG(src:0x%04X, id:%02X.%01X.%04X.%01X)", 6U, self_->own_id, tel_ptr->pb_msg.source_addr, tel_ptr->pb_msg.id.fblock_id, tel_ptr->pb_msg.id.instance_id, tel_ptr->pb_msg.id.function_id, tel_ptr->pb_msg.id.op_type)); + if (self_->rx_filter_fptr != NULL) + { + discard = self_->rx_filter_fptr(self_->rx_filter_inst, Msg_GetMostTel(tel_ptr)); + } + + if ((self_->rx_complete_fptr != NULL) && (discard == false)) + { + /* the assigned Rx function is responsible to release the message */ + self_->rx_complete_fptr(self_->rx_complete_inst, Msg_GetMostTel(tel_ptr)); + } + else + { + Fifo_RxReleaseMsg(self_->fifo_ptr, tel_ptr); + } +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_transceiver.h b/mnsl/mns_transceiver.h new file mode 100644 index 0000000..b1701c5 --- /dev/null +++ b/mnsl/mns_transceiver.h @@ -0,0 +1,135 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Declaration of class CTransceiver + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TRCV + * @{ + */ + +#ifndef MNS_TRANSCEIVER_H +#define MNS_TRANSCEIVER_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_message.h" +#include "mns_pool.h" +#include "mns_pmfifo.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Assignable callback function which is invoked for message reception + * \param self The instance + * \param tel_ptr Reference to the message object + */ +typedef void (*Trcv_RxCompleteCb_t)(void *self, Msg_MostTel_t *tel_ptr); + +/*! \brief Assignable callback function which is invoked to filter Rx messages + * \details Filtering is a synchronous operation. Hence, it is not possible to keep a message + * object for delayed processing. The invoked function has to decide whether a + * message shall be discarded and freed to the Rx pool. Therefore, it has to return + * \c true. By returning \ false, the message will be received in the usual way. + * \param self The instance + * \param tel_ptr Reference to the message object + * \return Returns \c true to discard the message and free it to the pool (no-pass). Otherwise, returns + * \c false (pass). + */ +typedef bool (*Trcv_RxFilterCb_t)(void *self, Msg_MostTel_t *tel_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define TRCV_SIZE_TX_POOL 100U /*!< \brief Number of messages in the message pool */ + +/*------------------------------------------------------------------------------------------------*/ +/* Class CTransceiver */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Class CTransceiver + * \details Provides MOST message objects and communication methods to further classes + */ +typedef struct CTransceiver_ +{ + CMessage tx_msgs[TRCV_SIZE_TX_POOL];/*!< \brief Messages in message pool */ + CPool tx_msg_pool; /*!< \brief The message pool */ + uint16_t tx_def_src; /*!< \brief Default source address for Tx message object */ + uint8_t mns_inst_id; /*!< \brief MOST NetServices instance ID */ + uint8_t own_id; /*!< \brief ID of the transceiver required for tracing */ + CPmFifo *fifo_ptr; /*!< \brief Reference to dedicated port message FIFO */ + + Trcv_RxCompleteCb_t rx_complete_fptr; /*!< \brief Callback function which is invoked on + * message reception + */ + void *rx_complete_inst; /*!< \brief Instance which is notified on + * message reception + */ + Trcv_RxFilterCb_t rx_filter_fptr; /*!< \brief Callback function which is invoked + * to filter Rx messages + */ + void *rx_filter_inst; /*!< \brief Instance which is notified to + * filter Rx messages + */ +} CTransceiver; + +/*------------------------------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------------------------------*/ +/* Constructor */ +extern void Trcv_Ctor(CTransceiver *self, CPmFifo *fifo_ptr, uint16_t def_src_addr, uint8_t mns_inst_id, uint8_t trace_id); +/* Tx */ +extern Msg_MostTel_t* Trcv_TxAllocateMsg(CTransceiver *self, uint8_t size); +extern void Trcv_TxSendMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr); +extern void Trcv_TxSendMsgExt(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr); +extern void Trcv_TxSendMsgBypass(CTransceiver *self, Msg_MostTel_t *tel_ptr, Msg_TxStatusCb_t callback_fptr, void *inst_ptr); +extern void Trcv_TxReleaseMsg(Msg_MostTel_t *tel_ptr); +extern void Trcv_TxReuseMsg(Msg_MostTel_t *tel_ptr); +/* Rx */ +extern void Trcv_RxAssignReceiver(CTransceiver *self, Trcv_RxCompleteCb_t callback_fptr, void *inst_ptr); +extern void Trcv_RxAssignFilter(CTransceiver *self, Trcv_RxFilterCb_t callback_fptr, void *inst_ptr); +extern void Trcv_RxReleaseMsg(CTransceiver *self, Msg_MostTel_t *tel_ptr); +extern void Trcv_RxOnMsgComplete(void *self, CMessage *tel_ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_TRANSCEIVER_H */ + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mns_types_cfg.h b/mnsl/mns_types_cfg.h new file mode 100644 index 0000000..32ce84e --- /dev/null +++ b/mnsl/mns_types_cfg.h @@ -0,0 +1,70 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief MOST NetServices data types. + */ + +#ifndef MNS_TYPES_H +#define MNS_TYPES_H + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Data Types */ +/*------------------------------------------------------------------------------------------------*/ +/* Definition of standard integer typed, typically defined in */ +/* typedef signed char int8_t; */ +/* typedef short int16_t; */ +/* typedef int int32_t; */ +/* typedef unsigned char uint8_t; */ +/* typedef unsigned short uint16_t; */ +/* typedef unsigned int uint32_t; */ + +/* Definition of size_t, typically defined in */ +/* typedef uint32_t size_t; */ + +/* Definition of character type */ + typedef char char_t; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNS_TYPES_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/mnsl/mnsl.c b/mnsl/mnsl.c new file mode 100644 index 0000000..2b59746 --- /dev/null +++ b/mnsl/mnsl.c @@ -0,0 +1,435 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/* parasoft suppress item * reason "not part of MOST NetServices delivery" */ + +/*! + * \file + * \brief Implementation of MOST NetServices Light + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_types_cfg.h" +#include "mnsl.h" +#include "mns_base.h" +#include "mns_misc.h" +#include "mns_pmchannel.h" +#include "mns_ams.h" +#include "mns_transceiver.h" +#include "mns_pmfifos.h" + +#include //TKU + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Mnsl_OnPmsEvent(void *self, void *event_code); +static void Mnsl_OnServiceRequest(void *self, void *data_ptr); +static void Mnsl_OnGetTickCount(void *self, void *tick_count_value_ptr); +static void Mnsl_OnSetApplicationTimer(void *self, void *new_time_value_ptr); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CMnsl */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Assigns default values to a provided MNSL init structure + * \param init_ptr Reference to MNSL init structure. + * \param enable_watchdog Set to \c true for normal use. Set to \c false to disable the watchdog + * supervision for debugging purpose only. + */ +extern void Mnsl_SetDefaultConfig(Mnsl_InitData_t *init_ptr, bool enable_watchdog) +{ + MISC_MEM_SET(init_ptr, 0, sizeof(Mnsl_InitData_t)); + + init_ptr->pms.active_fifos = MNSL_FIFOS_MCM_ICM; + init_ptr->pms.compressed = false; + + init_ptr->pms.icm_config.fifo_id = PMP_FIFO_ID_ICM; + init_ptr->pms.icm_config.tx_wd_timeout = 0U; + init_ptr->pms.icm_config.tx_wd_timer_value = 0U; + init_ptr->pms.icm_config.rx_ack_timeout = 10U; + init_ptr->pms.icm_config.rx_busy_allowed = 0xFU; + init_ptr->pms.icm_config.rx_credits = PMCH_FIFO_CREDITS; + init_ptr->pms.icm_config.rx_threshold = PMCH_FIFO_THRESHOLD; + + init_ptr->pms.rcm_config.fifo_id = PMP_FIFO_ID_RCM; + init_ptr->pms.rcm_config.tx_wd_timeout = 10U; /* watchdog timeout: 1s */ + init_ptr->pms.rcm_config.tx_wd_timer_value = 600U; /* watchdog trigger every 600 ms */ + init_ptr->pms.rcm_config.rx_ack_timeout = 10U; + init_ptr->pms.rcm_config.rx_busy_allowed = 0xFU; + init_ptr->pms.rcm_config.rx_credits = PMCH_FIFO_CREDITS; + init_ptr->pms.rcm_config.rx_threshold = PMCH_FIFO_THRESHOLD; + + init_ptr->pms.mcm_config.fifo_id = PMP_FIFO_ID_MCM; + init_ptr->pms.mcm_config.tx_wd_timeout = 10U; /* watchdog timeout: 1s */ + init_ptr->pms.mcm_config.tx_wd_timer_value = 600U; /* watchdog trigger every 600 ms */ + init_ptr->pms.mcm_config.rx_ack_timeout = 10U; + init_ptr->pms.mcm_config.rx_busy_allowed = 0xFU; + init_ptr->pms.mcm_config.rx_credits = PMCH_MCM_CREDITS; + init_ptr->pms.mcm_config.rx_threshold = PMCH_MCM_THRESHOLD; + + if (enable_watchdog == false) + { + init_ptr->pms.icm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */ + init_ptr->pms.rcm_config.tx_wd_timeout = 0U; /* watchdog timeout: 0 -> infinite */ + init_ptr->pms.rcm_config.tx_wd_timer_value = 0U;/* watchdog timer: 0 -> no timer */ + init_ptr->pms.rcm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */ + init_ptr->pms.mcm_config.tx_wd_timeout = 0U; /* watchdog timeout: 0 -> infinite */ + init_ptr->pms.mcm_config.tx_wd_timer_value = 0U;/* watchdog timer: 0 -> no timer */ + init_ptr->pms.mcm_config.rx_ack_timeout = 0U; /* acknowledge timeout: 0 -> infinite */ + } +} + +/*! \brief Initialize MNS Light class + * \param init_ptr Reference to the MNSL init structure. + * The memory of the init structure can be freed after the function returns. + */ +void Mnsl_Init(CMnsl *mnsl, Mnsl_InitData_t *init_ptr, void *inst_ptr) +{ + assert(NULL != mnsl); + CSingleObserver *srv_request_obs_ptr = NULL; + CSingleObserver *app_timer_obs_ptr = NULL; + Base_InitData_t base_init_data; + Pmch_InitData_t pmch_init_data; + CPmFifo *icm_ptr = NULL; + CPmFifo *rcm_ptr = NULL; + CPmFifo *mcm_ptr = NULL; + CTransceiver *trcv_mcm_ptr = NULL; + CTransceiver *trcv_rcm_ptr = NULL; + + MISC_MEM_SET(mnsl, 0, sizeof(mnsl)); + MISC_MEM_SET(&base_init_data, 0, sizeof(base_init_data)); + + mnsl->inst_ptr = inst_ptr; //TKU + mnsl->inst_id = 1U; + mnsl->get_tick_count_fptr = init_ptr->general.get_tickcount_fptr; + mnsl->srv_request_fptr = init_ptr->general.request_service_fptr; + mnsl->set_app_timer_fptr = init_ptr->general.set_app_timer_fptr; + + if(mnsl->srv_request_fptr != NULL) + { + Sobs_Ctor(&mnsl->srv_request_obs, mnsl, &Mnsl_OnServiceRequest); + srv_request_obs_ptr = &mnsl->srv_request_obs; + } + if (mnsl->set_app_timer_fptr != NULL) + { + Sobs_Ctor(&mnsl->set_app_timer_obs, mnsl, &Mnsl_OnSetApplicationTimer); + app_timer_obs_ptr = &mnsl->set_app_timer_obs; + } + + base_init_data.mns_inst_id = 1U; + base_init_data.tm.mns_inst_id = 1U; + base_init_data.scd.mns_inst_id = 1U; + + Sobs_Ctor(&mnsl->get_tick_count_obs, mnsl, &Mnsl_OnGetTickCount); + base_init_data.tm.get_tick_count_obs_ptr = &mnsl->get_tick_count_obs; + base_init_data.scd.service_request_obs_ptr = srv_request_obs_ptr; + base_init_data.tm.set_application_timer_obs_ptr = app_timer_obs_ptr; + + Base_Ctor(&mnsl->base, &base_init_data); + + /* Initialize port message service */ + pmch_init_data.mns_inst_id = 1U; + pmch_init_data.tx_release_fptr = &Fifo_TxOnRelease; + pmch_init_data.lld_iface = init_ptr->lld; + Pmch_Ctor(&mnsl->pm_channel, &pmch_init_data, inst_ptr); + + if((init_ptr->pms.active_fifos & MNSL_FIFOS_ICM) == MNSL_FIFOS_ICM) + { + Fifo_InitData_t icm_init; + + icm_init.base_ptr = &mnsl->base; + icm_init.channel_ptr = &mnsl->pm_channel; + icm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete; + icm_init.rx_cb_inst = &mnsl->icm_transceiver; + + if (init_ptr->pms.compressed) + { + icm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80); + icm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80); + } + else + { + icm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + icm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + } + + Fifo_Ctor(&mnsl->icm_fifo, &icm_init, &init_ptr->pms.icm_config); + Trcv_Ctor(&mnsl->icm_transceiver, &mnsl->icm_fifo, MSG_ADDR_EHC_CFG, base_init_data.mns_inst_id, PMP_FIFO_ID_ICM);/* initialize ICM transceiver and set link to PMS instance */ + + icm_ptr = &mnsl->icm_fifo; + } + + if((init_ptr->pms.active_fifos & MNSL_FIFOS_RCM) == MNSL_FIFOS_RCM) + { + Fifo_InitData_t rcm_init; + + rcm_init.base_ptr = &mnsl->base; + rcm_init.channel_ptr = &mnsl->pm_channel; + rcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete; + rcm_init.rx_cb_inst = &mnsl->rcm_transceiver; + rcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + rcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + /* Note: RCM compressed data format is not supported */ + + Fifo_Ctor(&mnsl->rcm_fifo, &rcm_init, &init_ptr->pms.rcm_config); + Trcv_Ctor(&mnsl->rcm_transceiver, &mnsl->rcm_fifo, MSG_ADDR_EHC_CFG, base_init_data.mns_inst_id, PMP_FIFO_ID_RCM);/* initialize ICM transceiver and set link to PMS instance */ + + rcm_ptr = &mnsl->rcm_fifo; + trcv_rcm_ptr = &mnsl->rcm_transceiver; + } + + if((init_ptr->pms.active_fifos & MNSL_FIFOS_MCM) == MNSL_FIFOS_MCM) + { + Fifo_InitData_t mcm_init; + + mcm_init.base_ptr = &mnsl->base; + mcm_init.channel_ptr = &mnsl->pm_channel; + mcm_init.rx_cb_fptr = &Trcv_RxOnMsgComplete; + mcm_init.rx_cb_inst = &mnsl->mcm_transceiver; + + if (init_ptr->pms.compressed) + { + mcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_81); + mcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_80); + } + else + { + mcm_init.tx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + mcm_init.rx_encoder_ptr = Enc_GetEncoder(ENC_CONTENT_00); + } + + Fifo_Ctor(&mnsl->mcm_fifo, &mcm_init, &init_ptr->pms.mcm_config); + Trcv_Ctor(&mnsl->mcm_transceiver, &mnsl->mcm_fifo, MSG_ADDR_EHC_APP, base_init_data.mns_inst_id, PMP_FIFO_ID_MCM);/* initialize ICM transceiver and set link to PMS instance */ + + mcm_ptr = &mnsl->mcm_fifo; + trcv_mcm_ptr = &mnsl->mcm_transceiver; + } + + if ((trcv_mcm_ptr != NULL) || (trcv_rcm_ptr != NULL)) /* initialize AMS if MCM or RCM is active */ + { + mnsl->ams_allocator.alloc_fptr = init_ptr->ams.rx_alloc_mem_fptr; + mnsl->ams_allocator.free_fptr = init_ptr->ams.rx_free_mem_fptr; + Amsp_Ctor(&mnsl->ams_pool, &mnsl->ams_allocator, mnsl->base.mns_inst_id); + Ams_Ctor(&mnsl->ams, &mnsl->base, trcv_mcm_ptr, trcv_rcm_ptr, &mnsl->ams_pool, AMS_RX_DEF_SIZE_PAYLOAD); + } + + /* initialize FIFO handler */ + Fifos_Ctor(&mnsl->fifos, &mnsl->base, &mnsl->pm_channel, icm_ptr, mcm_ptr, rcm_ptr); + + /* register event callback */ + mnsl->event_fptr = init_ptr->general.event_fptr; + Obs_Ctor(&mnsl->pms_observer, mnsl, &Mnsl_OnPmsEvent); + Fifos_AddEventObserver(&mnsl->fifos, &mnsl->pms_observer); +} + +/*! \brief Synchronizes the PMS + * \details Accordingly MNSL_EVENT_SYNC_COMPLETE or MNSL_EVENT_SYNC_FAILED will be notified. + */ +void Mnsl_Synchronize(CMnsl *mnsl) +{ + assert(NULL != mnsl); + /* initializes the port message service & LLD interface */ + Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); + Fifos_Synchronize(&mnsl->fifos, true, true); +} + +/*! \brief Un-synchronizes the PMS + * \details Accordingly MNSL_EVENT_UNSYNC_COMPLETE or MNSL_EVENT_UNSYNC_FAILED will be notified. + * Calling this function if MNSL is already un-synced will report MNSL_EVENT_UNSYNC_FAILED, + * since the low-level driver interface is not active. + * \param initial MNSL is able to trigger un-synchronization in advance of the initial + * synchronization. In this case, timeouts and retries behave like a synchronization + * and on MNSL_EVENT_UNSYNC_COMPLETE the LLD interface will stay running. + * I.e. if set to \c true the un-synchronization behaves like MNS initial un-synchronization. + * If set to \c false, the un-synchronization behaves like MNS final un-synchronization. + */ +void Mnsl_Unsynchronize(CMnsl *mnsl, bool initial) +{ + assert(NULL != mnsl); + if (initial == false) + { + Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT); /* final sync */ + } + else + { + Fifos_ConfigureSyncParams(&mnsl->fifos, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); /* initial sync */ + } + + Fifos_Unsynchronize(&mnsl->fifos, true, initial); +} + +/*! \brief Handles PMS event and notifies MNSL registered event callback + * \param self Reference to instance + * \param event_code Event notified by the PMS + */ +static void Mnsl_OnPmsEvent(void *self, void *event_code) +{ + assert(NULL != self); + Fifos_Event_t *code = (Fifos_Event_t*)event_code; + CMnsl *mnsl = (CMnsl *)self; + + if (mnsl->event_fptr != NULL) + { + switch (*code) + { + case FIFOS_EV_SYNC_LOST: + Eh_ReportEvent(&mnsl->base.eh, EH_E_SYNC_LOST); /* event is necessary for AMS cleanup */ + mnsl->event_fptr(MNSL_EVENT_SYNC_LOST, mnsl->inst_ptr); + break; + case FIFOS_EV_SYNC_ESTABLISHED: + mnsl->event_fptr(MNSL_EVENT_SYNC_COMPLETE, mnsl->inst_ptr); + break; + case FIFOS_EV_SYNC_FAILED: + mnsl->event_fptr(MNSL_EVENT_SYNC_FAILED, mnsl->inst_ptr); + break; + case FIFOS_EV_UNSYNC_COMPLETE: + mnsl->event_fptr(MNSL_EVENT_UNSYNC_COMPLETE, mnsl->inst_ptr); + Eh_ReportEvent(&mnsl->base.eh, EH_E_UNSYNC_COMPLETE); /* event is necessary for AMS cleanup */ + break; + case FIFOS_EV_UNSYNC_FAILED: + mnsl->event_fptr(MNSL_EVENT_UNSYNC_FAILED, mnsl->inst_ptr); + Eh_ReportEvent(&mnsl->base.eh, EH_E_UNSYNC_FAILED); /* event is necessary for AMS cleanup */ + break; + default: + break; + } + } +} + +/*! \brief Service function of MNS Light + */ +void Mnsl_Service(CMnsl *mnsl) +{ + assert(NULL != mnsl); + bool pending_events; + + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service()", 0U)); + Scd_Service(&mnsl->base.scd); /* Run the scheduler */ + pending_events = Scd_AreEventsPending(&mnsl->base.scd); /* Check if events are still pending? */ + + if (pending_events != false) /* At least one event is pending? */ + { + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service(): events still pending", 0U)); + Mnsl_OnServiceRequest(mnsl, NULL); + } + else /* No event is pending */ + { + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_Service(): calling Tm_CheckForNextService()", 0U)); + mnsl->base.scd.scd_srv_is_running = true; /* prevent continuous service requests if no app timer is provided */ + Tm_CheckForNextService(&mnsl->base.tm); /* If MNS timers are running: What is the next time that the timer management must be */ + mnsl->base.scd.scd_srv_is_running = false; /* serviced again? */ + } +} + +/*! \brief Reports that the application provided timer has expired. + */ +void Mnsl_ReportTimeout(CMnsl *mnsl) +{ + assert(NULL != mnsl); + TR_INFO((mnsl->inst_id, "[API]", "Mns_ReportTimeout()", 0U)); + Tm_TriggerService(&mnsl->base.tm); /* Trigger TM service call */ +} + +/*! \brief Callback function which is invoked by the scheduler + * \param self Parameter not used with single instance API + * \param data_ptr Currently unused + */ +static void Mnsl_OnServiceRequest(void *self, void *data_ptr) +{ + assert(NULL != self); + CMnsl *mnsl = (CMnsl *)self; + MISC_UNUSED(data_ptr); + + if (mnsl->srv_request_fptr != NULL) + { + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_OnServiceRequest(): calling srv_request_fptr()", 0U)); + mnsl->srv_request_fptr(mnsl->inst_ptr); + } +} + +/*! \brief This function is used in combination with the observer \c mns.get_tick_count_obs + * which is used to request the current tick count value form the application. + * \param self Parameter not used with single instance API + * \param tick_count_value_ptr Reference to the requested tick count value. The pointer must + * be casted into data type uint16_t. + */ +static void Mnsl_OnGetTickCount(void *self, void *tick_count_value_ptr) +{ + assert(NULL != self); + CMnsl *mnsl = (CMnsl *)self; + *((uint16_t *)tick_count_value_ptr) = mnsl->get_tick_count_fptr(); +} + +/*! \brief Callback function which is invoked to start the application timer when the MNSL service + * is implemented event driven + * \param self The instance + * \param new_time_value_ptr Reference to the new timer value. The pointer must be casted into + * data type uint16_t. + */ +static void Mnsl_OnSetApplicationTimer(void *self, void *new_time_value_ptr) +{ + CMnsl *self_ = (CMnsl*)self; + TR_INFO((mnsl->inst_id, "[API]", "Mnsl_OnSetApplicationTimer(): set_app_timer_fptr(%d)", 1U, *((uint16_t *)new_time_value_ptr))); + self_->set_app_timer_fptr(*((uint16_t *)new_time_value_ptr), self_->inst_ptr); +} + +/*! \brief Returns the ICM transceiver object + * \return Reference to the ICM transceiver + */ +CTransceiver * Mnsl_GetIcmTransceiver(CMnsl *mnsl) +{ + return &mnsl->icm_transceiver; +} + +/*! \brief Returns the RCM transceiver object + * \return Reference to the RCM transceiver + */ +CTransceiver * Mnsl_GetRcmTransceiver(CMnsl *mnsl) +{ + return &mnsl->rcm_transceiver; +} + +/*! \brief Returns the MCM transceiver object + * \return Reference to the MCM transceiver + */ +CTransceiver * Mnsl_GetMcmTransceiver(CMnsl *mnsl) +{ + return &mnsl->mcm_transceiver; +} + +/*! \brief Returns the AMS transceiver object + * \details It is important not make usage of competing transceivers + * AMS, MCM and RCM. Either use AMS exclusively or MCM and RCM. + * Important: Do not use AMS in mode \c MNSL_FIFOS_ICM. + * \return Reference to the AMS + */ +CAms * Mnsl_GetAmsTransceiver(CMnsl *mnsl) +{ + return &mnsl->ams; +} + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ diff --git a/mnsl/mnsl.h b/mnsl/mnsl.h new file mode 100644 index 0000000..7288f50 --- /dev/null +++ b/mnsl/mnsl.h @@ -0,0 +1,195 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/* parasoft suppress item * reason "not part of MOST NetServices delivery" */ + +/*! + * \file + * \brief Header file of MOST NetServices Light + */ + +#ifndef MNSL_H +#define MNSL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_transceiver.h" +#include "mns_ams.h" +#include "mns_pmfifos.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Definitions */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief MNS Major Version Number */ +#define MNSL_VERSION_MAJOR 3 +/*! \brief MNS Minor Version Number */ +#define MNSL_VERSION_MINOR 2 +/*! \brief MNS Release Version Number */ +#define MNSL_VERSION_RELEASE 7 +/*! \brief MNS Service Release Number */ +#define MNSL_VERSION_SR 0 +/*! \brief MNS Build Number */ +#define MNSL_VERSION_BUILD 1796 + +/*------------------------------------------------------------------------------------------------*/ +/* Types */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Defines which FIFOs shall be built on the Port Message Channel */ +typedef enum Mnsl_ActiveFifos_ +{ + MNSL_FIFOS_MCM = 0x01U, /*!< \brief Initialize PM channel with MCM FIFO only */ + MNSL_FIFOS_ICM = 0x02U, /*!< \brief Initialize PM channel with ICM FIFO only */ + MNSL_FIFOS_MCM_ICM = 0x03U, /*!< \brief Initialize PM channel with MCM and ICM FIFO */ + MNSL_FIFOS_RCM = 0x04U, /*!< \brief Initialize PM channel with RCM FIFO only */ + MNSL_FIFOS_MCM_RCM = 0x05U, /*!< \brief Initialize PM channel with MCM and RCM FIFO */ + MNSL_FIFOS_ICM_RCM = 0x06U, /*!< \brief Initialize PM channel with ICM and RCM FIFO */ + MNSL_FIFOS_MCM_ICM_RCM = 0x07U /*!< \brief Initialize PM channel with MCM, ICM and RCM FIFO */ + +} Mnsl_ActiveFifos_t; + +/*! \brief Possible MNSL events */ +typedef enum Mnsl_Event_ +{ + MNSL_EVENT_SYNC_COMPLETE = 0, /*!< \brief The initial synchronization completed successfully */ + MNSL_EVENT_SYNC_FAILED = 1, /*!< \brief The initial synchronization failed */ + MNSL_EVENT_SYNC_LOST = 2, /*!< \brief The synchronization to the INIC was lost irrecoverably */ + MNSL_EVENT_UNSYNC_COMPLETE = 3, /*!< \brief The un-synchronization was completed successfully */ + MNSL_EVENT_UNSYNC_FAILED = 4 /*!< \brief The un-synchronization failed */ + +} Mnsl_Event_t; + +typedef uint16_t (*Mnsl_GetTickCountCb_t)(void); +typedef void (*Mnsl_EventCb_t)(Mnsl_Event_t event_code, void *inst_ptr); +typedef void (*Mnsl_ServiceRequestCb_t)(void *inst_ptr); +typedef void (*Mnsl_SetAppTimerCb_t)(uint16_t timeout, void *inst_ptr); + +/*! \brief General initialization settings */ +typedef struct Mnsl_General_InitData_ +{ + Mnsl_EventCb_t event_fptr; /*!< \brief Reports an MNSL event (mandatory callback) */ + Mnsl_GetTickCountCb_t get_tickcount_fptr; /*!< \brief Requests the current tick count in milliseconds (mandatory callback) */ + Mnsl_ServiceRequestCb_t request_service_fptr;/*!< \brief Reports that the application shall call Mnsl_Service() (optional callback) */ + Mnsl_SetAppTimerCb_t set_app_timer_fptr; /*!< \brief Triggers the application to call Mnsl_ReportTimeout() after a specific time (mandatory callback) */ + +} Mnsl_General_InitData_t; + +/*! \brief Port Message Service (PMS) settings */ +typedef struct Mnsl_Pms_InitData_ +{ + Mnsl_ActiveFifos_t active_fifos; /*!< \brief Defines which FIFOs shall be initialized */ + Fifo_Config_t icm_config; /*!< \brief Setup of ICM FIFO */ + Fifo_Config_t mcm_config; /*!< \brief Setup of MCM FIFO */ + Fifo_Config_t rcm_config; /*!< \brief Setup of RCM FIFO */ + bool compressed; /*!< \brief Set to \c true in order to use OS81118 Rev.C */ + +} Mnsl_Pms_InitData_t; + +/*! \brief Application Message Service (AMS) settings */ +typedef struct Mnsl_Ams_InitData_ +{ + Mns_Ams_AllocMemCb_t rx_alloc_mem_fptr; /*!< \brief Required to allocate memory for an AMS Rx message */ + Mns_Ams_FreeMemCb_t rx_free_mem_fptr; /*!< \brief Required to free memory for an AMS Rx message */ + +} Mnsl_Ams_InitData_t; + +/*! \brief Initialization settings */ +typedef struct Mnsl_InitData_ +{ + Mnsl_General_InitData_t general; /*!< \brief Basic settings to drive MNSL */ + Mns_Lld_Callbacks_t lld; /*!< \brief Mandatory callback functions of the driver */ + Mnsl_Pms_InitData_t pms; /*!< \brief Port Message Service settings */ + Mnsl_Ams_InitData_t ams; /*!< \brief Application Message Service settings */ + +} Mnsl_InitData_t; + +/*------------------------------------------------------------------------------------------------*/ +/* Helper Macros */ +/*------------------------------------------------------------------------------------------------*/ +#define MNSL_UNUSED(p) ((p) = (p)) /* parasoft-suppress MISRA2004-19_7 "suppress warning" */ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal structures */ +/*------------------------------------------------------------------------------------------------*/ +typedef struct CMnsl_ +{ + uint8_t inst_id; + CBase base; + CPmChannel pm_channel; + CPmFifos fifos; + CPmFifo icm_fifo; + CPmFifo rcm_fifo; + CPmFifo mcm_fifo; + + Mnsl_GetTickCountCb_t get_tick_count_fptr; + CSingleObserver get_tick_count_obs; + + Mnsl_ServiceRequestCb_t srv_request_fptr; + CSingleObserver srv_request_obs; + + Mnsl_SetAppTimerCb_t set_app_timer_fptr; + CSingleObserver set_app_timer_obs; + + CTransceiver icm_transceiver; + CTransceiver rcm_transceiver; + CTransceiver mcm_transceiver; + CAms ams; + CAmsMsgPool ams_pool; + Ams_MemAllocator_t ams_allocator; + + Mnsl_EventCb_t event_fptr; + CObserver pms_observer; + + void *inst_ptr; //TKU: added + +} CMnsl; + +/*------------------------------------------------------------------------------------------------*/ +/* Prototypes */ +/*------------------------------------------------------------------------------------------------*/ +extern void Mnsl_SetDefaultConfig(Mnsl_InitData_t *init_ptr, bool enable_watchdog); +extern void Mnsl_Init(CMnsl *mnsl, Mnsl_InitData_t *init_ptr, void *inst_ptr); +extern void Mnsl_Synchronize(CMnsl *mnsl); +extern void Mnsl_Unsynchronize(CMnsl *mnsl, bool initial); +extern void Mnsl_Service(CMnsl *mnsl); +extern void Mnsl_ReportTimeout(CMnsl *mnsl); +extern CTransceiver * Mnsl_GetIcmTransceiver(CMnsl *mnsl); +extern CTransceiver * Mnsl_GetRcmTransceiver(CMnsl *mnsl); +extern CTransceiver * Mnsl_GetMcmTransceiver(CMnsl *mnsl); +extern CAms * Mnsl_GetAmsTransceiver(CMnsl *mnsl); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef MNSL_H */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + diff --git a/nbproject/private/Makefile-variables.mk b/nbproject/private/Makefile-variables.mk new file mode 100644 index 0000000..e69de29 diff --git a/scripts/camera-os88122-ts.script b/scripts/camera-os88122-ts.script new file mode 100644 index 0000000..5131f04 --- /dev/null +++ b/scripts/camera-os88122-ts.script @@ -0,0 +1,1542 @@ + diff --git a/scripts/camera-os88133-ap0100-flash.script b/scripts/camera-os88133-ap0100-flash.script new file mode 100644 index 0000000..1bfacb3 --- /dev/null +++ b/scripts/camera-os88133-ap0100-flash.script @@ -0,0 +1,17020 @@ + diff --git a/scripts/camera-os88133-ap0100.script b/scripts/camera-os88133-ap0100.script new file mode 100644 index 0000000..baee2a2 --- /dev/null +++ b/scripts/camera-os88133-ap0100.script @@ -0,0 +1,928 @@ + diff --git a/scripts/config-agl.xml b/scripts/config-agl.xml new file mode 100644 index 0000000..8824aee --- /dev/null +++ b/scripts/config-agl.xml @@ -0,0 +1,419 @@ + + + + + 3 + USB + 1 + 212 + + CTRL + IN + 8f + + + CTRL + OUT + f + + + + + + 311 + 3 + + + 1 + + USB + SYNC + IN + 1 + 16 + 5040 + 42 + 12 + 0 + AUDIO + + + MOST + SYNC + OUT + 4 + + + + 2 + + USB + SYNC + IN + 1 + 16 + 5040 + 42 + 12 + 4 + AUDIO + + + MOST + SYNC + OUT + 4 + + + + 3 + + USB + SYNC + IN + 1 + 16 + 5040 + 42 + 12 + 8 + AUDIO + + + MOST + SYNC + OUT + 4 + + + + 4 + + USB + ISOC + IN + 2 + 188 + 32 + 7520 + 2 + + + MOST + ISOC + OUT + 80 + 188 + + + + 5 + + USB + ISOC + OUT + 81 + 188 + 32 + 7520 + 2 + V4L + + + MOST + ISOC + IN + 60 + + + + + + + 370 + 3 + + + 1 + + MOST + SYNC + IN + 4 + + + I2S + SYNC + OUT + 4 + DualOut + Output + 1 + 64 + Left16 + SRXA0 + + + + + + + 371 + 3 + + + 1 + + MOST + SYNC + IN + 4 + + + I2S + SYNC + OUT + 4 + DualOut + Output + 1 + 64 + Left16 + SRXA0 + + + + + + + 372 + 3 + + + 1 + + MOST + SYNC + IN + 4 + + + I2S + SYNC + OUT + 4 + DualOut + Output + 1 + 64 + Left16 + SRXA0 + + + + + + + 332 + 3 + + + 1 + + MOST + ISOC + IN + 80 + 188 + + + USB + ISOC + OUT + 7520 + 2 + 83 + 188 + + + + + 2 + + USB + SYNC + IN + 4 + 16 + 7520 + 128 + 4 + + + MOST + SYNC + OUT + 4 + + + + 3 + + USB + ISOC + OUT + 81 + 188 + 32 + 7520 + 2 + + + MOST + ISOC + IN + 60 + + + + + + + 340 + 3 + + + 1 + + MLB + ISOC + IN + 60 + a + + + MOST + ISOC + OUT + 100 + + + + + + + 321 + 1 + 3072 + + + 1 + + MLB + ISOC + IN + 60 + a + + + MOST + ISOC + OUT + 60 + + + + + + + + 311 + 0 + 1 + + + 370 + 0 + 1 + + + + + + + 311 + 0 + 2 + + + 371 + 0 + 1 + + + + + + + 311 + 0 + 3 + + + 372 + 0 + 1 + + + + + + + 311 + 0 + 4 + + + 332 + 0 + 1 + + + 332 + 1 + 1 + + + + + + + 321 + 0 + 1 + + + 340 + 0 + 1 + + + 311 + 0 + 5 + + + 332 + 0 + 3 + + + 332 + 1 + 3 + + + + + + diff --git a/scripts/i2c-slim-amplifier-v2.3.script b/scripts/i2c-slim-amplifier-v2.3.script new file mode 100644 index 0000000..af57e67 --- /dev/null +++ b/scripts/i2c-slim-amplifier-v2.3.script @@ -0,0 +1,92 @@ + diff --git a/scripts/loadDriver.sh b/scripts/loadDriver.sh new file mode 100755 index 0000000..2993614 --- /dev/null +++ b/scripts/loadDriver.sh @@ -0,0 +1,174 @@ +#!/bin/bash + +if [ ! "$(whoami)" = "root" ] +then + echo Warning you are not root, expect problems! +fi + +if [ -d /home/pi/appsteam/trunk/LinuxDriver ] +then + cd /home/pi/appsteam/trunk/LinuxDriver + echo Switched to folder:$(pwd) +else + echo Searching driver in folder:$(pwd) +fi + +#unload MOST Kernel modules +lsmod | grep "^mxc_mlb\>" > /dev/null && + rmmod mxc_mlb && echo "unloaded mxc_mlb" + +lsmod | grep "^hdm_dim2_mx6q\>" > /dev/null && + rmmod hdm_dim2_mx6q && echo "unloaded hdm_dim2_mx6q" + +lsmod | grep "^hdm_dim2\>" > /dev/null && + rmmod hdm_dim2 && echo "unloaded hdm_dim2" + +lsmod | grep "^hdm_usb\>" > /dev/null && + rmmod hdm_usb && echo "unloaded hdm_usb" + +lsmod | grep "^aim_sound\>" > /dev/null && + rmmod aim_sound && echo "unloaded aim_sound" + +lsmod | grep "^aim_v4l2\>" > /dev/null && + rmmod aim_v4l2 && echo "unloaded aim_v4l2" + +lsmod | grep "^aim_cdev\>" > /dev/null && + rmmod aim_cdev && echo "unloaded aim_cdev" + +lsmod | grep "^aim_network\>" > /dev/null && + rmmod aim_network && echo "unloaded aim_network" + +lsmod | grep "^mostcore\>" > /dev/null && + rmmod mostcore && echo "unloaded mostcore" + +echo +#load V4L support +lsmod | grep "^media\>" || + insmod /lib/modules/$(uname -r)/kernel/drivers/media/media.ko + +lsmod | grep "^videodev\>" || + insmod /lib/modules/$(uname -r)/kernel/drivers/media/v4l2-core/videodev.ko + +#load MOST Kernel modules +insmod mostcore.ko && echo "loaded mostcore" +insmod aim_cdev.ko && echo "loaded aim_cdev" + +[ -e aim_network.ko ] && + insmod aim_network.ko && echo "loaded aim_network" + +[ -e aim_sound.ko ] && + insmod aim_sound.ko && echo "loaded sound" + +[ -e aim_v4l2.ko ] && + insmod aim_v4l2.ko && echo "loaded v4l2" + +[ -e hdm_usb.ko ] && + insmod hdm_usb.ko && echo "loaded hdm_usb" + +[ -e hdm_dim2.ko ] && + insmod hdm_dim2.ko clock_speed=4096fs fcnt=3 && echo "loaded hdm_dim2" + +[ -e hdm_dim2_mx6q.ko ] && + insmod hdm_dim2_mx6q.ko && echo "loaded hdm_dim2_mx6q" + +DEVS=/sys/devices/virtual/most/mostcore/devices +AIMS=/sys/devices/virtual/most/mostcore/aims + +xset() +{ + VAL=$1 + FILE=$2 + + #echo "$VAL > $FILE" + echo "$VAL" > $FILE +} + +SetAsyncChannel() +{ + MDEV=mdev$1 + CH_NAME=$2 + DIR=dir_$3 + BUFSIZE=$4 + + CHANNEL=$DEVS/$MDEV/$CH_NAME + + [ -e $CHANNEL/set_buffer_size ] || return 1 + + [ "$(cat $CHANNEL/set_buffer_size)" = "0" ] || return 2 + + echo "Set Ethernet $MDEV channel: $CH_NAME" + + xset async $CHANNEL/set_datatype + xset $DIR $CHANNEL/set_direction + xset 32 $CHANNEL/set_number_of_buffers + xset $BUFSIZE $CHANNEL/set_buffer_size + + xset $MDEV:$CH_NAME: $AIMS/networking/add_link + return 0 +} + +StartupEth() +{ + METH=meth$1 + IFC_ALL=$2 + IFC_UP=$3 + + #if device not exist return + echo "$IFC_ALL" | grep -q $METH || return 1 + + #if the interface is down, start it + if [ -z "`echo "$IFC_UP" | grep $METH`" ] + then + echo Starting up $METH + ifconfig $METH up + return 2 + fi + + #if the IP is already set, we have nothing to do anymore + ifconfig $METH |grep -q "inet addr:" && return 3 + + MAC=`ifconfig -a $METH | awk '/HWaddr/{ print $5}'` + #if MAC address has not been set yet return + [ -z "$MAC" ] && return 4 + [ $MAC = "FF:FF:FF:FF:FF:FF" ] && return 5 + [ $MAC = "ff:ff:ff:ff:ff:ff" ] && return 5 + [ $MAC = "00:00:00:00:00:00" ] && return 6 + + m0=$(echo $((16#${MAC:0:2}))) + m1=$(echo $((16#${MAC:3:2}))) + m2=$(echo $((16#${MAC:6:2}))) + m3=$(echo $((16#${MAC:9:2}))) + m4=$(echo $((16#${MAC:12:2}))) + m5=$(echo $((16#${MAC:15:2}))) + + IP=10.0.$m2.$((1+$m5)) + echo Setting IP address $IP for device $METH MAC:$m0-$m1-$m2-$m3-$m4-$m5 + ifconfig $METH $IP netmask 255.255.255.0 + return 0 +} + +########################## +## USER ADJUSTABLE AREA ## +########################## +while : +do + for i in 0 1 2 3 4 5 6 7 8 9 + do +#USB attached INICs: +#MEP + SetAsyncChannel $i ep8e rx 1548 && + SetAsyncChannel $i ep0e tx 1548 +#MLB attached INICs: +#MEP + SetAsyncChannel $i ca6 rx 1548 && + SetAsyncChannel $i ca8 tx 1548 + done + IFC_ALL="$(ifconfig -a)" + IFC_UP="$(ifconfig)" + for i in 0 1 2 3 4 5 6 7 8 9 + do + StartupEth $i "$IFC_ALL" "$IFC_UP" + done + sleep 3 +done + -- cgit 1.2.3-korg