From 0a28683a1ff9f3975c8b68b15017cf342bab5f3d Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Thu, 8 Dec 2016 16:46:07 +0100 Subject: src: vod-server: import sources This patch adds the source tree of the Video-On-Demand server and its configuration scripts. Change-Id: I6db8c43d46c7450baf117a541bd7153496dbcd4c Signed-off-by: Christian Gromm --- Src/ConnectionInfo.cpp | 712 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 712 insertions(+) create mode 100644 Src/ConnectionInfo.cpp (limited to 'Src/ConnectionInfo.cpp') 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; +} -- cgit 1.2.3-korg