diff -r 14460bf2a402 -r f50f4094acd7 cbsref/csyrefplugins/csy27010/src/PortFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cbsref/csyrefplugins/csy27010/src/PortFactory.cpp Tue Jul 06 15:36:38 2010 +0300 @@ -0,0 +1,982 @@ +// +// * Copyright 2004 Neusoft America Inc. +// * All rights reserved. +// * This component 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 the URL "http://www.eclipse.org/legal/epl-v10.html". +// * +// * Contributors: +// * Keith Collins (Neusoft America Inc.) original software development and additional code and modifications. +// * Thomas Gahagen (Neusoft America Inc.) additional code and modifications. +// * Zhen Yuan (Neusoft America Inc.) additional code and modifications. +// * +// * Description: This file contains the implementation for the class CPortFactory. +// * This class is used to create C32 port interface instances. +// + +// PortFactory.cpp + +/** @file PortFactory.cpp + * + */ + +/*! \mainpage CSY 27.010 Documentation + * + * \section intro Introduction + * + * This CSY has been designed and implemented by TapRoot Systems. + * This CSY is part of the TapRoot Systems Communications Platform (CommP) + * product family for Symbian OS. Please contact TapRoot Systems for + * information on CommP. + *

+ * A CSY is a specialized type of DLL that implements API interfaces + * required by the C32 Serial Communications Server. "CSY" is the file + * name extension used by CSY modules as in "ecuart.csy", which is the + * name of Symbian's generic CSY for non-multiplexed serial ports. + * TapRoot Systems 27.010 CSY (gsm0701.csy) is compatible with the + * 3GPP 27.010 v5.0.0 specification. + *

+ * The CSY has been designed and tested with the Intel Lubbock application + * processor, TI Condat baseband processor and Symbian OS 8.0a. + * The CSY is not guaranteed to work with other AP's, BP's or Symbian OS versions. + * The CSY has been integrated with Symbian OS supplied TSY and NIF IP. + * The CSY is not guaranteed to work with other TSY's and NIF's. + * + * \section feat Features + * + * The CSY supports the 27.010 Basic Option. The 3GPP 27.010 specification + * is derived from the GSM 07.10 specification. 3GPP 27.010 defines a mechanism + * whereby multiple commands and data streams can be carried over the same + * physical link. This specification defines a multiplexing protocol between + * a Terminal Equipment (TE) and a Mobile Station (MS) allowing multiple + * simultaneous sessions over a single start-stop, framed, serial link. + * Each session (channel) can carry different types of data including voice, + * fax, data, SMS, CBS, phonebook maintenance, battery status, GPRS, USSD, etc. + *

+ * The multiplexer has three operating options, basic, advanced without error + * recovery and advanced with error recovery. CSY 27.010 supports the + * basic option. The basic option is recommended on highly reliable links. + * Advanced options are not supported by the CSY. The characteristics of the + * basic option are: + *

  • Length indicator used instead of the HDLC transparency mechanism + *
  • Different flag octet from that used by HDLC + *
  • Cannot be used on links that use XON/XOFF flow control + *
  • May have longer recovery procedure from loss of synchronization + *

    + * Each channel between the TE and MS is called a Data Link Connection (DLC). + * A special DLC called "control channel" is used to exchange management + * information (e.g. parameter negotiation), power saving control information, + * testing, flow control, close down, etc. between the TE and MS. + *

    + * The multiplexer is activated with the 3GPP 27.007 AT+CMUX command. + *

    + * + * \section install Installation + * + * Refer to the readme.txt file for installation and build instructions. + *
    + * Please contact TapRoot Systems for information on product updates and + * additional support. + */ + +/** @file PortFactory.cpp + * This file contains the implementation for the class CPortFactory. + * This class is used to create C32 port interface instances. + */ + +#include +#include +#include +#include +using namespace CommsDat; +#include "Portfactory.h" +#include "CsyGlobals.h" +#include "Mux0710Protocol.h" +#include "ChannelMgrCtrl.h" +#include "ChannelMgrCmdData.h" +#include "PortC32InterfaceBase.h" +#include "PortC32Interface.h" +#include "PortC32InterfaceIp.h" +#include "CommFrameWriterAo.h" +#include "CommFrameReaderAo.h" +#include "CsyDebugLogger.h" + +CPortFactory* CPortFactory::NewL() +/** + * This method uses two phase construction and the cleanup stack to create + * an instance of class CPortFactory. This method is invoked when the user + * invokes the C32 comm server's LoadCommModule() method to load the CSY. + * Note that multiple instances of CPortFactory can be created by C32, + * so no memory allocation or objects should be created when the + * port factory is first created. + * + * @return Pointer to the created instance of CPortFactory + */ + { + _LOG_L4C1("CPortFactory::NewL"); + + CPortFactory* self = new(ELeave) CPortFactory; + TCleanupItem closeSelf(CPortFactory::CloseObject, self); + CleanupStack::PushL(closeSelf); + self->ConstructL(); + CleanupStack::Pop(self); + return (self); + } + +CPortFactory::CPortFactory() +: iPortC32InterfaceList(_FOFF(CPortC32Interface, iLink)), + iPortC32InterfaceIter(iPortC32InterfaceList), + iDataChannelList(_FOFF(CChannelMgrCmdData, iLink)), + iDataChannelIter(iDataChannelList) +/** + * Constructor. + */ + { + _LOG_L4C1("CPortFactory::CPortFactory"); + } + +CPortFactory::~CPortFactory() +/** + * Destructor. Delete all objects and memory created/allocated by this class. + */ + { + _LOG_L4C1(">>CPortFactory::~CPortFactory"); + + // check if the CSY's objects have been created + // C32 creates a new instance of the port factory each time a client + // loads the CSY. The C32 recognizes a redundant CSerial name and promptly + // destroys the redundant CSY. + if (iMuxObjectsCreated) + { + // client channels + CChannelMgrCmdData* anyChannel = NULL; + iDataChannelIter.SetToFirst(); + while ((anyChannel = iDataChannelIter++) != NULL) + { + iDataChannelList.Remove(*anyChannel); + delete anyChannel; + } + + // control channel + delete iChannelCtrl; + + delete iCommWriterAo; + delete iCommReaderAo; + + delete iMux0710Protocol; + + // may not be necessary ??? + // MAF /\ what does this comment mean + + // delete any remaining C32 port objects that were not closed by the client + CPortC32InterfaceBase* port = NULL; + iPortC32InterfaceIter.SetToFirst(); + while ((port = iPortC32InterfaceIter++) != NULL) + { + delete port; + } + + iCommPort.Close(); + } + + //iMuxObjectsCreated = EFalse; + + _LOG_L4C1("<>CPortFactory::CreateCsyObjectsL"); + + // set flag + iMuxObjectsCreated = ETrue; + iOpenPortFailed = EFalse; + + // load physical device drivers for serial port(s) + +#ifdef __WINS__ + // 1st make sure the file server is running + RFs fileServer; + User::LeaveIfError(fileServer.Connect()); + fileServer.Close(); + + _LIT(KPDDName, "ECDRV"); + TInt retVal1 = User::LoadPhysicalDevice(KPDDName); + if ((retVal1 != KErrNone) && (retVal1 != KErrAlreadyExists)) + User::Leave(retVal1); + +#else // target + + TBuf<20> commPddName; + TInt retVal1; + + commPddName.Copy(KCsyDefaultPDDNameComm1); + retVal1 = User::LoadPhysicalDevice(commPddName); + if ((retVal1 != KErrNone) && (retVal1 != KErrAlreadyExists)) + User::Leave(retVal1); + // only load 2nd PDD if debugging is enabled + +#ifdef __DEBUGSERIALPORT__ + commPddName.Copy(KCsyDefaultPDDNameComm2); + retVal1 = User::LoadPhysicalDevice(commPddName); + if ((retVal1 != KErrNone) && (retVal1 != KErrAlreadyExists)) + User::Leave(retVal1); +#endif //__DEBUGSERIALPORT__ + +#endif //__WINS__ + + // load logical device driver for serial ports + _LIT(KLDDName, "ECOMM"); + retVal1 = User::LoadLogicalDevice(KLDDName); + if ((retVal1 != KErrNone) && (retVal1 != KErrAlreadyExists)) + User::Leave(retVal1); + + ConfigurePhysicalCommPortL(); + + // create 27.010 framer and comm i/o objects + iMux0710Protocol = CMux0710Protocol::NewL(*this); + iCommWriterAo = CCommFrameWriterAo::NewL(this, iMux0710Protocol); + iMux0710Protocol->SetCommWriter(iCommWriterAo); + + iCommReaderAo = CCommFrameReaderAo::NewL(this, iMux0710Protocol); + iCommReaderAo->Read(); // start it + + // create the Multiplexer Control Channel + iChannelCtrl = CChannelMgrCtrl::NewL(*this, *iMux0710Protocol); + + // create All Multiplexer Channels + for (TUint8 i=1; i < KCsyDefaultMaxDLCChannels; i++) + { + CChannelMgrCmdData* channelMgrCmd = + CChannelMgrCmdData::NewL(i, *this, *iMux0710Protocol); + iDataChannelList.AddLast(*channelMgrCmd); + } + + // switch to multiplexer mode + iMux0710Protocol->SwitchToMuxMode(); + +#ifdef _DEBUG + TInt usedCellCount; + TInt freeCellCount; + usedCellCount = User::CountAllocCells(freeCellCount); + _LOG_L4C3("Cell Counts Used = %d Free = %d", usedCellCount, freeCellCount); +#endif + + _LOG_L4C1("<(aObject)->Close(); + } + +CPort* CPortFactory::NewPortL(const TUint aUnit) +/** + * This method is called by C32 when a port is opened via RComm::Open(). + * An instance of the CSY's class CPortC32Interface is created. + * The instance is assigned a channel manager and then is added to + * the list of C32 port interface objects. + * + * @param aUnit - Numeric portion of the port identifier + * @return Pointer to the created C32 port interface object + */ + { + _LOG_L4C2(">>CPortFactory::NewPortL [aUnit=%d]", aUnit); + + TC32PortInfo portInfo; + + portInfo.iClientType = GetClientType(aUnit); + portInfo.iPortNumber = aUnit; + + // check if port number is valid and translates to a client type + if (portInfo.iClientType == EC32ClientUndefined) + { + _LOG_L4C1("<GetChannelReady() == EFalse) + { + _LOG_L4C1("CSY objects already exist"); + // switch to multiplexer mode + iMux0710Protocol->SwitchToMuxMode(); + } + + // first make sure control channel is already connected + iChannelCtrl->WaitForChannelReady(); + + if (! iOpenPortFailed) + { + // create a new C32 Interface Port based on port number + CPortC32InterfaceBase* port = NULL; + + if (portInfo.iClientType == EC32ClientIpNif) + { + _LOG_L4C1("Raw IP port"); + port = CPortC32InterfaceIp::NewL(*this, portInfo); + } + else + { + _LOG_L4C1("Normal port"); + port = CPortC32Interface::NewL(*this, portInfo); + } + + CleanupStack::PushL(port); + CChannelMgrCmdData* channel = FindChannelMgr(aUnit); + + if (channel == NULL) + { + _LOG_L2C1("** channel is null, delete interface **"); + CleanupStack::PopAndDestroy(port); + User::Leave(KErrNotFound); + } + TName name; + name.Format(_L("%d"), aUnit); + port->SetName(&name); + + // channel->Open() will add this port to it's port list, and connect channel if needed + // Make sure that during channel init we do not tx/rx through channel + channel->Open(port); + + iNumOfOpenPorts++; + _LOG_L4C2("No. of ports = %d",iNumOfOpenPorts); + + port->SetMuxChannel(channel); + + channel->WaitForChannelReady(); + + //Need to wait for the channel to be ready. How to wait for something without blocking whole thread? + // add it to list of ports + iPortC32InterfaceList.AddLast(*port); + CleanupStack::Pop(port); + +#ifdef _DEBUG + TInt usedCellCount; + TInt freeCellCount; + usedCellCount = User::CountAllocCells(freeCellCount); + _LOG_L4C3("Cell Counts Used = %d Free = %d", usedCellCount, freeCellCount); +#endif + + _LOG_L4C2("<= KCOMMP_IP_NIF_OFFSET) + { + _LOG_L4C1("Raw Ip"); + return CPortFactory::EC32ClientIpNif; + } + else if (aC32PortNum == KCOMMP_CSD_PPP_NUMBER) + { + _LOG_L4C1("Csd PPP"); + return CPortFactory::EC32ClientNif; + } + + _LOG_L4C1("Tsy"); + return CPortFactory::EC32ClientTsy; + } + +CPortC32InterfaceBase* CPortFactory::FindPortC32Interface(const TUint8 aDlcNum) +/** + * This method returns a pointer to the C32 port interface object + * that is associated with the specified DLC number. + * + * @param aDlcNum - DLC number + * @return Pointer to C32 port interface object or NULL if not found + */ + { + _LOG_L4C2(">>CPortFactory::FindPortC32Interface [aDlcNum=%d]", aDlcNum); + + CPortC32InterfaceBase* port = NULL; + + CChannelMgrCmdData* anyChannel = NULL; + iDataChannelIter.SetToFirst(); + while ((anyChannel = iDataChannelIter++) != NULL) + { + if (anyChannel->GetDlcNumber() == aDlcNum) + { + _LOG_L4C1("AT channel"); + port = anyChannel->GetC32Port(); + break; + } + } + + _LOG_L4C2("<>CPortFactory::FindChannelMgr [aC32PortNum=%d]", aC32PortNum); + + TUint dlcNum = ++iLastDlcNum; + _LOG_L4C2("iLastDlcNum: %d", iLastDlcNum); + + // + // MAF Horrible + // This just counts through the array of channel pointers + // and returns a pointer when the count gets to the correct value. + // + // We should be able to use an array here and directly access it using + // the dlc number!?!? + // + + TUint count = 1; + CChannelMgrCmdData* anyChannelCmd; + + iDataChannelIter.SetToFirst(); + while ((anyChannelCmd = iDataChannelIter++) != NULL) + { + if (count == dlcNum) + { + _LOG_L4C2("<>CPortFactory::FindChannelMgrByDlcNum [aDlcNum=%d]", aDlcNum); + + CChannelMgrCmdData* channel = NULL; + iDataChannelIter.SetToFirst(); + while ((channel = iDataChannelIter++) != NULL) + { + if (channel->GetDlcNumber() == aDlcNum) + { + _LOG_L4C1("AT channel"); + return channel; + } + } + + _LOG_L2C1("<>CPortFactory::ConfigurePhysicalCommPortL"); + + // create a package pointer + TPckgBuf newCfg; + TInt ret, len; + + // get the modem table - Get port settings from CommDB. + CMDBSession* db = CMDBSession::NewL(KCDLatestVersion); + + CleanupStack::PushL(db); + + TUint32 modemId = 0; + + // We are using the same modem as TSY + // Accessing the ModemPhoneServicesSMS field in the Global Settings Table + CMDBField* globalSettingsField = new(ELeave) CMDBField(KCDTIdModemPhoneServicesSMS); + CleanupStack::PushL(globalSettingsField); + globalSettingsField->SetRecordId(1); + globalSettingsField->LoadL(*db); + modemId = *globalSettingsField; // The field value gives the record id in the Modem Bearer Table. + CleanupStack::PopAndDestroy(globalSettingsField); + + _LOG_L4C2("Accessing modem entry %d in the modem table", modemId); + + TUint32 rate; + TUint32 dataBits; + TUint32 stopBits; + TUint32 parity; + TUint32 handshake; + TPckgBuf config; + + TUint port = 0; + TBuf controlPort; + + // Accessing the ControlChannelPort Name from the ModemBearer Table in the record number + //corresponding to modemId + CMDBField* portField = new(ELeave) CMDBField(KCDTIdControlChannelPortName); + CleanupStack::PushL(portField); + portField->SetRecordId(modemId); + portField->SetMaxLengthL(KMaxTextLength); + portField->LoadL(*db); + controlPort = *portField; + CleanupStack::PopAndDestroy(portField); + // Diagnostics - control port + if (controlPort.Length()) + { + TBuf8<16> portName; + portName.Copy(controlPort.Left(16)); + _LOG_L4C2("Control port(MODEM_CONTROL_CHANNEL_PORT_NAME) '%S'", + &portName); + + // Get number of port out of the port descriptor. + _LIT(KDoubleColon, "::"); + TInt len = controlPort.Find(KDoubleColon); + if (len < KErrNone) + { + _LOG_L1C1("** finding :: in control port failed **"); + User::Leave(KErrGeneral); + } + TUint numPos = len + KDoubleColon.iTypeLength; + TPtrC numPtr(&controlPort[numPos], controlPort.Length() - numPos); + TLex lexer(numPtr); + TInt ret = lexer.Val(port); + if (ret) + { + _LOG_L1C1("** lexing to get port number failed **"); + User::Leave(ret); + } + } + else + { + _LOG_L1C1("** Control port not set in commDb **"); + User::Leave(KErrGeneral); + } + + // Accessing the Rate from the ModemBearer Table in the record number + //corresponding to modemId + CMDBField* rateField = new(ELeave) CMDBField(KCDTIdRate); + CleanupStack::PushL(rateField); + rateField->SetRecordId(modemId); + rateField->LoadL(*db); + rate = *rateField; + CleanupStack::PopAndDestroy(rateField); + + // Accessing the DataBits from the ModemBearer Table in the record number + //corresponding to modemId + CMDBField* dataBitsField = new(ELeave) CMDBField(KCDTIdDataBits); + CleanupStack::PushL(dataBitsField); + dataBitsField->SetRecordId(modemId); + dataBitsField->LoadL(*db); + dataBits = *dataBitsField; + CleanupStack::PopAndDestroy(dataBitsField); + + // Accessing the StopBits from the ModemBearer Table in the record number + //corresponding to modemId + CMDBField* stopBitsField = new(ELeave) CMDBField(KCDTIdStopBits); + CleanupStack::PushL(stopBitsField); + stopBitsField->SetRecordId(modemId); + stopBitsField->LoadL(*db); + stopBits = *stopBitsField; + CleanupStack::PopAndDestroy(stopBitsField); + + // Accessing the Parity from the ModemBearer Table in the record number + //corresponding to modemId + CMDBField* parityField = new(ELeave) CMDBField(KCDTIdParity); + CleanupStack::PushL(parityField); + parityField->SetRecordId(modemId); + parityField->LoadL(*db); + parity = *parityField; + CleanupStack::PopAndDestroy(parityField); + + // Accessing the HandShake from the ModemBearer Table in the record number + //corresponding to modemId + CMDBField* handshakeField = new(ELeave) CMDBField(KCDTIdHandshaking); + CleanupStack::PushL(handshakeField); + handshakeField->SetRecordId(modemId); + handshakeField->LoadL(*db); + handshake = *handshakeField; + CleanupStack::PopAndDestroy(handshakeField); + + config().iRate = (TBps)rate; + config().iDataBits = (TDataBits)dataBits; + config().iStopBits = (TStopBits)stopBits; + config().iParity = (TParity)parity; + config().iHandshake = (TUint)handshake; + config().iParityError = KConfigParityErrorIgnore; + config().iFifo = static_cast(EFifoEnable); + + newCfg.Copy(config); + + ret = iCommPort.Open(port); + if (ret) + { + _LOG_L1C3("** Failed CommPort.Open(%d) ret=%d **", port,ret); + User::Leave(ret); + } + + // Configure the port + len = iCommPort.ReceiveBufferLength(); + + if (len < KErrNone) + { + _LOG_L1C2("** Leave: CommPort.ReceiveBufferLength() len=%d **", len); + User::Leave(KErrGeneral); + } + + // Increase the receive buffer to make sure we can operate as slowly as we + // can without filling up the LDD's receive buffer with incoming signals + len *= KCSY_ReceiveBufferSizeIncreaseFactor; + ret = iCommPort.SetReceiveBufferLength(len); + + TCommConfig cbuf; + TCommConfigV01 &cfg = cbuf(); + iCommPort.Config(cbuf); // Get the Configuration Parameters + + cfg.iRate = newCfg().iRate; + cfg.iDataBits = newCfg().iDataBits; + cfg.iStopBits = newCfg().iStopBits; + cfg.iParity = newCfg().iParity; + cfg.iHandshake = newCfg().iHandshake; + cfg.iParityError = newCfg().iParityError; + cfg.iFifo = newCfg().iFifo; + + _LOG_L4C2(" Set Phys Port Config for Port %d", port); + _LOG_L4C2(" Rate = 0x%x", cfg.iRate); + _LOG_L4C2(" DataBits = 0x%x", cfg.iDataBits); + _LOG_L4C2(" Stop Bits = 0x%x", cfg.iStopBits); + _LOG_L4C2(" Parity = 0x%x", cfg.iParity); + _LOG_L4C2(" Handshake = 0x%x", cfg.iHandshake); + _LOG_L4C2(" Parity Error = 0x%x", cfg.iParityError); + _LOG_L4C2(" Fifo = 0x%x", cfg.iFifo); + + ret = iCommPort.SetConfig(cbuf); // Set the Configuration Parameters + if (ret) + { + _LOG_L1C2("** CommPort.SetConfig ret=%d **", ret); + User::Leave(KErrNotSupported); + } + + // Clean up the stack + CleanupStack::PopAndDestroy(db); + + _LOG_L4C2("<Connect(); + if (ret != KErrNone) + { + _LOG_L4C2(("<Disconnect(); + } + +void CPortFactory::ChannelCtrlDoCancel() +/** + * This method is called when the port has been opened and the max number of retries has been reached. + */ + { + _LOG_L4C1(">>CPortFactory::ChannelCtrlDoCancel"); + + iOpenPortFailed = ETrue; + iChannelCtrl->NotifyChannelReady(); + + _LOG_L4C1("<>CPortFactory::RemoveC32Port [aPort=%d]", aPort->GetPortNumber()); + + CPortC32InterfaceBase* port = NULL; + iPortC32InterfaceIter.SetToFirst(); + while ((port = iPortC32InterfaceIter++) != NULL) + { + if (port == aPort) + { + iPortC32InterfaceList.Remove(*port); + _LOG_L4C1("<GetMuxChannel(); + if (iMuxChannel) + { + + TInt err = iMuxChannel->Connect(); + if (err != KErrNone) + { + _LOG_L1C2("iMuxChannel->Connect() error =%d",err); + } + } + else + { + // MAF assert here + _LOG_L1C2("** Could not find channel for port [0x%x] **",port); + } + } + else + { + // MAF assert here + _LOG_L1C1("** Could not find port for dlcnumber **"); + } + } + +TBool CPortFactory::FindDlcToEnable() +/** + * We have resources again. Find a dlc that has had its flow control + * set and re-enable. + * + * @return ETrue if there are still dlcs that are stopped due to flow control. + */ + { + _LOG_L4C1(">>CPortFactory::FindDlcToEnable"); + + TInt numberOfStopped = 0; + TBool dlcsStillToEnable = EFalse; + + CChannelMgrCmdData* channel = NULL; + + iDataChannelIter.SetToFirst(); + while ((channel = iDataChannelIter++) != NULL) + { + if (channel->CsyToModemFlowControl() == EFlowControlOn) + { + numberOfStopped++; + if (numberOfStopped == 1) + { + _LOG_L4C1("Found a dlc to re-enable"); + if (channel->SetCsyToModemFlowControl(EFlowControlOff)) + { + _LOG_L4C1("Re-enable failed"); + dlcsStillToEnable = ETrue; + break; + } + } + else + { + _LOG_L4C1("Other dlc still to re-enable"); + dlcsStillToEnable = ETrue; + } + } + } + + _LOG_L4C2("<>CPortFactory::FindActiveDataDlcToStop"); + + CChannelMgrCmdData* channel = NULL; + CPortC32InterfaceBase* port = NULL; + + iDataChannelIter.SetToFirst(); + while ((channel = iDataChannelIter++) != NULL) + { + port = channel->GetC32Port(); + if ((port)&&(port->GetClientType() == EC32ClientIpNif)) + { + if (channel->CsyToModemFlowControl() == EFlowControlOff) + { + _LOG_L4C1("Found a data dlc to stop"); + if (channel->SetCsyToModemFlowControl(EFlowControlOn)) + { + _LOG_L4C1("Failed to stop dlc"); + } + else + break; + } + } + } + + _LOG_L4C1("<>CPortFactory::StopAnyDlc"); + + CChannelMgrCmdData* channel = NULL; + + iDataChannelIter.SetToFirst(); + while ((channel = iDataChannelIter++) != NULL) + { + if (channel->CsyToModemFlowControl() == EFlowControlOff) + { + _LOG_L4C1("Found a dlc to stop"); + if (channel->SetCsyToModemFlowControl(EFlowControlOn)) + { + _LOG_L4C1("Failed to stop dlc"); + } + else + break; + } + } + + _LOG_L4C1("<