blob: 8b82d195b75ba8ef40425dc2c004656e3708bc55 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 - 2015 ACIN, fortiss GmbH
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alois Zoitl - initial API and implementation and/or initial documentation
*******************************************************************************/
#include "muxedsercommlayer.h"
#include "commfb.h"
#include "../devlog.h"
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <criticalregion.h>
using namespace forte::com_infra;
CMuxedSerCommLayer::CMuxedSerPortsManager CMuxedSerCommLayer::smMuxedSerPortsManager;
CMuxedSerCommLayer::CMuxedSerCommLayer(CComLayer* paUpperLayer, CBaseCommFB * paFB) :
CComLayer(paUpperLayer, paFB), mBufFillSize(0), mFD(scmInvalidFileDescriptor),
mInterruptResp(forte::com_infra::EComResponse::e_Nothing), mSerMuxId(0){
}
CMuxedSerCommLayer::~CMuxedSerCommLayer(){
closeConnection();
}
EComResponse CMuxedSerCommLayer::sendData(void *paData, unsigned int paSize){
if(CFDSelectHandler::scmInvalidFileDescriptor != mFD){
//first send id
mRecvBuffer[0] = mSerMuxId;
memcpy(&(mRecvBuffer[1]), paData, paSize);
paSize++;
ssize_t nToSend = paSize;
while(0 < nToSend){
ssize_t nSentBytes = write(mFD, &(mRecvBuffer[0]), nToSend);
if(nSentBytes <= 0){
DEVLOG_ERROR("CMuxedSerCommLayer: Send failed: %s\n", strerror(errno));
return e_ProcessDataSendFailed;
}
nToSend -= nSentBytes;
paData = static_cast<char*>(paData) + nSentBytes;
}
}
return e_ProcessDataOk;
}
EComResponse CMuxedSerCommLayer::recvData(const void *, unsigned int){
ssize_t nReadCount = read(mFD, &mRecvBuffer[mBufFillSize], cg_unIPLayerRecvBufferSize - mBufFillSize);
switch (nReadCount){
case 0:
DEVLOG_INFO("CMuxedSerCommLayer: Connection closed by peer\n");
mInterruptResp = e_InitTerminated;
closeConnection();
break;
case -1:
DEVLOG_ERROR("CMuxedSerCommLayer: read failed: %s\n", strerror(errno));
mInterruptResp = e_ProcessDataRecvFaild;
break;
default:
//we successfully received data
mBufFillSize += nReadCount;
mInterruptResp = e_ProcessDataOk;
break;
}
m_poFb->interruptCommFB(this);
return mInterruptResp;
}
EComResponse CMuxedSerCommLayer::processInterrupt(){
if(e_ProcessDataOk == mInterruptResp){
mInterruptResp = m_poTopLayer->recvData(mRecvBuffer, mBufFillSize);
mBufFillSize = 0;
}
return mInterruptResp;
}
EComResponse CMuxedSerCommLayer::openConnection(char *paLayerParameter){
EComResponse eRetVal = e_InitInvalidId;
char *acPort = strchr(paLayerParameter, ':');
if(0 != acPort){
*acPort = '\0';
++acPort;
mSerMuxId = static_cast<TForteUInt8>(forte::core::util::strtoul(acPort, 0, 10));
mFD = smMuxedSerPortsManager.addMuxedSerLayer(paLayerParameter, this);
if(CFDSelectHandler::scmInvalidFileDescriptor != mFD){
eRetVal = e_InitOk;
}
}
return eRetVal;
}
void CMuxedSerCommLayer::closeConnection(){
if(CFDSelectHandler::scmInvalidFileDescriptor != mFD){
smMuxedSerPortsManager.removeMuxedSerLayer(mFD, this);
mFD = CFDSelectHandler::scmInvalidFileDescriptor;
}
}
//**************************************************************************************************************************
CMuxedSerCommLayer::CMuxedSerPortsManager::CMuxedSerPortsManager(){
}
CFDSelectHandler::TFileDescriptor CMuxedSerCommLayer::CMuxedSerPortsManager::addMuxedSerLayer(char* paSerPort, CMuxedSerCommLayer *paComCallBack){
CCriticalRegion criticalRegion(mSync);
CFDSelectHandler::TFileDescriptor nRetVal = CFDSelectHandler::scmInvalidFileDescriptor;
SSerPortEntry *mSerPortEntry = getSerPortEntry(paSerPort);
if(0 != mSerPortEntry){
mSerPortEntry->mConnectionsList.pushBack(paComCallBack);
nRetVal = mSerPortEntry->mFD;
}
return nRetVal;
}
void CMuxedSerCommLayer::CMuxedSerPortsManager::removeMuxedSerLayer(CFDSelectHandler::TFileDescriptor paFD, CMuxedSerCommLayer *paComCallBack){
CCriticalRegion criticalRegion(mSync);
SSerPortEntry *mSerPortEntry = getOpendSerPortEntry(paFD);
if(0 != mSerPortEntry){
TConnectionContainer::Iterator itRunner(mSerPortEntry->mConnectionsList.begin());
TConnectionContainer::Iterator itRevNode(mSerPortEntry->mConnectionsList.end());
TConnectionContainer::Iterator itEnd(mSerPortEntry->mConnectionsList.end());
while(itRunner != itEnd){
if((*itRunner) == paComCallBack){
if(itRevNode == itEnd){
mSerPortEntry->mConnectionsList.popFront();
}else{
mSerPortEntry->mConnectionsList.eraseAfter(itRevNode);
}
break;
}
itRevNode = itRunner;
++itRunner;
}
if(mSerPortEntry->mConnectionsList.isEmpty()){
closePort(mSerPortEntry);
}
}
}
EComResponse CMuxedSerCommLayer::CMuxedSerPortsManager::SSerPortEntry::recvData(const void *, unsigned int){
EComResponse eRetVal = e_Nothing;
char nID;
ssize_t nReadCount = read(mFD, &nID, 1);
if(1 == nReadCount){
TConnectionContainer::Iterator itEnd(mConnectionsList.end());
for(TConnectionContainer::Iterator itRunner = mConnectionsList.begin(), itCurrent = mConnectionsList.begin(); itRunner != itEnd;){
itCurrent = itRunner;
++itRunner;
if(nID == itCurrent->getSerMuxId()){
eRetVal = itCurrent->recvData(0,0);
if(e_Nothing != eRetVal){
m_poFb = itCurrent->getCommFB();
}
break;
}
}
}
else{
//FIXME handle error close connections and inform FBs
}
return eRetVal;
}
EComResponse CMuxedSerCommLayer::CMuxedSerPortsManager::SSerPortEntry::processInterrupt(){
return e_Nothing;
}
CMuxedSerCommLayer::CMuxedSerPortsManager::SSerPortEntry *CMuxedSerCommLayer::CMuxedSerPortsManager::getSerPortEntry(char* paSerPort){
for(TSerPortList::Iterator itRunner = mPortList.begin(); itRunner != mPortList.end(); ++itRunner){
if(0 == strcmp(itRunner->mSerPort, paSerPort)){
return &(*itRunner);
}
}
TSerPortList::Iterator itRef(mPortList.back());
//this port is not yet managed by us
mPortList.pushBack(SSerPortEntry());
TSerPortList::Iterator itCurrent(mPortList.back());
SSerPortEntry *pstRetVal = &(*itCurrent);
openPort(paSerPort, pstRetVal);
if(CFDSelectHandler::scmInvalidFileDescriptor == pstRetVal->mFD){
if(itCurrent == mPortList.begin()){
mPortList.popFront();
}
else{
mPortList.eraseAfter(itRef);
}
pstRetVal = 0;
}
else{
pstRetVal->mSerPort = new char[strlen(paSerPort) + 1];
strcpy(pstRetVal->mSerPort, paSerPort);
}
return pstRetVal;
}
CMuxedSerCommLayer::CMuxedSerPortsManager::SSerPortEntry *CMuxedSerCommLayer::CMuxedSerPortsManager::getOpendSerPortEntry(CFDSelectHandler::TFileDescriptor paFD){
SSerPortEntry *pstRetVal = 0;
for(TSerPortList::Iterator itRunner = mPortList.begin(); itRunner != mPortList.end(); ++itRunner){
if(paFD == itRunner->mFD){
pstRetVal = &(*itRunner);
break;
}
}
return pstRetVal;
}
void CMuxedSerCommLayer::CMuxedSerPortsManager::openPort(char* paSerPort, SSerPortEntry *paPortEntry){
paPortEntry->mFD = open(paSerPort, O_RDWR | O_NOCTTY);
if(CFDSelectHandler::scmInvalidFileDescriptor != paPortEntry->mFD){
getExtEvHandler<CFDSelectHandler>().addComCallback(paPortEntry->mFD, paPortEntry);
}
else{
DEVLOG_ERROR("CSerCommLayer: open failed: %s\n", strerror(errno));
}
}
void CMuxedSerCommLayer::CMuxedSerPortsManager::closePort(SSerPortEntry *paSerPortEntry){
getExtEvHandler<CFDSelectHandler>().removeComCallback(paSerPortEntry->mFD);
close(paSerPortEntry->mFD);
delete[] paSerPortEntry->mSerPort;
TSerPortList::Iterator itRunner(mPortList.begin());
TSerPortList::Iterator itRefNode(mPortList.end());
while(itRunner != mPortList.end()){
if(itRunner->mFD == paSerPortEntry->mFD){
if(itRefNode == mPortList.end()){
mPortList.popFront();
}
else{
mPortList.eraseAfter(itRefNode);
}
break;
}
itRefNode = itRunner;
++itRunner;
}
}