24
|
1 |
// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
2 |
// All rights reserved.
|
|
3 |
// This component and the accompanying materials are made available
|
|
4 |
// under the terms of "Eclipse Public License v1.0"
|
|
5 |
// which accompanies this distribution, and is available
|
|
6 |
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
7 |
//
|
|
8 |
// Initial Contributors:
|
|
9 |
// Nokia Corporation - initial contribution.
|
|
10 |
//
|
|
11 |
// Contributors:
|
|
12 |
//
|
|
13 |
// Description:
|
|
14 |
// implementation of etel driver context class
|
|
15 |
//
|
|
16 |
//
|
|
17 |
|
|
18 |
/**
|
|
19 |
@file
|
|
20 |
@internalComponent
|
|
21 |
*/
|
|
22 |
|
|
23 |
|
|
24 |
#include "ceteldrivercontext.h"
|
|
25 |
#include "ceteldriverfactory.h"
|
|
26 |
#include "spudteldebuglogger.h"
|
|
27 |
|
|
28 |
|
|
29 |
using namespace EtelDriver;
|
|
30 |
|
|
31 |
|
|
32 |
CEtelDriverContext* CEtelDriverContext::NewL(TContextId aId, SpudMan::TPdpContextType aContextType, CEtelDriverFactory& aFactory)
|
|
33 |
{
|
|
34 |
CEtelDriverContext* self = new (ELeave) CEtelDriverContext(aId, aContextType, aFactory);
|
|
35 |
CleanupStack::PushL(self);
|
|
36 |
self->ConstructL();
|
|
37 |
CleanupStack::Pop(self);
|
|
38 |
return self;
|
|
39 |
}
|
|
40 |
|
|
41 |
void CEtelDriverContext::ConstructL()
|
|
42 |
{
|
|
43 |
iContextConfig.CreateL(PdpFsm::KContextConfigBufferSize);
|
|
44 |
iContextConfig.SetMax();
|
|
45 |
iContextConfig.FillZ();
|
|
46 |
|
|
47 |
switch(PdpFsmInterface().UmtsRelease())
|
|
48 |
{
|
|
49 |
case TPacketDataConfigBase::KConfigGPRS:
|
|
50 |
{
|
|
51 |
RPacketContext::TContextConfigGPRS tmp;
|
|
52 |
Mem::Copy(const_cast<TUint8*>(iContextConfig.Ptr()), &tmp, sizeof(tmp));
|
|
53 |
}
|
|
54 |
break;
|
|
55 |
|
|
56 |
case TPacketDataConfigBase::KConfigRel99Rel4:
|
|
57 |
{
|
|
58 |
RPacketContext::TContextConfigR99_R4 tmp;
|
|
59 |
Mem::Copy(const_cast<TUint8*>(iContextConfig.Ptr()), &tmp, sizeof(tmp));
|
|
60 |
}
|
|
61 |
break;
|
|
62 |
|
|
63 |
case TPacketDataConfigBase::KConfigRel5:
|
|
64 |
{
|
|
65 |
RPacketContext::TContextConfig_R5 tmp;
|
|
66 |
Mem::Copy(const_cast<TUint8*>(iContextConfig.Ptr()), &tmp, sizeof(tmp));
|
|
67 |
}
|
|
68 |
break;
|
|
69 |
default:
|
|
70 |
User::Leave(KErrNotSupported);
|
|
71 |
break;
|
|
72 |
|
|
73 |
}
|
|
74 |
}
|
|
75 |
|
|
76 |
CEtelDriverContext::CEtelDriverContext (TContextId aId, SpudMan::TPdpContextType aContextType, CEtelDriverFactory& aFactory)
|
|
77 |
: CActive(CActive::EPriorityStandard),
|
|
78 |
iId(aId),
|
|
79 |
iContextType(aContextType),
|
|
80 |
iCompletionStatus(KErrNone),
|
|
81 |
iStrategyStep(MEtelDriverStrategy::EFinishStep),
|
|
82 |
iStrategyId(ESentinelStrategy),
|
|
83 |
iFactory(aFactory),
|
|
84 |
iQosRequestedPckg(iQosRequested),
|
|
85 |
iQosNegotiatedPckg(iQosNegotiated),
|
|
86 |
|
|
87 |
iDataChannelV2(),
|
|
88 |
iDataChannelV2Pckg(iDataChannelV2),
|
|
89 |
iPcktMbmsSessionList(NULL),
|
|
90 |
iContextConfigMbms(),
|
|
91 |
iContextConfigMbmsPckg(iContextConfigMbms),
|
|
92 |
iFilterV2(),
|
|
93 |
iFilterV2Pckg(iFilterV2)
|
|
94 |
{
|
|
95 |
SPUDTEL_FNLOG("CEtelDriverContext::CEtelDriverContext");
|
|
96 |
CActiveScheduler::Add(this);
|
|
97 |
}
|
|
98 |
CEtelDriverContext::~CEtelDriverContext()
|
|
99 |
{
|
|
100 |
SPUDTEL_FNLOG("CEtelDriverContext::~CEtelDriverContext()");
|
|
101 |
Cancel(); // N.B. This cancels all outstanding operations on the context, including deletion!
|
|
102 |
iContextConfig.Close();
|
|
103 |
|
|
104 |
// Guarantees proper release of all handles.
|
|
105 |
// If everything is closed already, this does no harm.
|
|
106 |
iPacketQoS.Close(); // Close QoS first, a buggy TSY may not handle it the other way.
|
|
107 |
iPacketContext.Close(); // At this point the reference count on the context is zero.
|
|
108 |
// TSY should cleanly dispose of the context, if it had not done so already.
|
|
109 |
|
|
110 |
// This is necessary only in a situtation where Spudman is destroyed while a strategy
|
|
111 |
// on a context is outstanding: in this case deletion of SpudTel results in cancellation
|
|
112 |
// of all outstanding operations, which is likely to result in handle leak.
|
|
113 |
// Under other circumstances, the handles will be closed via an appropriate strategy.
|
|
114 |
|
|
115 |
//delete if MBMS sessions structures.
|
|
116 |
iMbmsPacketContext.Close();
|
|
117 |
if (iMbmsSession)
|
|
118 |
{
|
|
119 |
delete iMbmsSession;
|
|
120 |
delete iPcktMbmsSessionList;
|
|
121 |
}
|
|
122 |
iSessionInfo.iSessionIds.Close(); //RArray should be closed.
|
|
123 |
|
|
124 |
}
|
|
125 |
|
|
126 |
/** initiates a new request
|
|
127 |
|
|
128 |
@param aOperation - type of an etel driver request
|
|
129 |
|
|
130 |
@return KErrInUse if pdp context has active strategy
|
|
131 |
*/
|
|
132 |
TInt CEtelDriverContext::Input (TEtelInput aOperation)
|
|
133 |
{
|
|
134 |
SPUDTEL_FNLOG("CEtelDriverContext::Input()");
|
|
135 |
SPUDTELVERBOSE_INFO_LOG1( _L("Operation %d"), aOperation );
|
|
136 |
|
|
137 |
if (MEtelDriverStrategy::EFinishStep != iStrategyStep)
|
|
138 |
{
|
|
139 |
SPUDTEL_ERROR_LOG(_L("ERROR: Pdp context is in use, return %d"), KErrInUse);
|
|
140 |
// I'm still doing something
|
|
141 |
ASSERT(EFalse); // shouldn't happen
|
|
142 |
return KErrInUse;
|
|
143 |
}
|
|
144 |
|
|
145 |
iStrategyId = iFactory.StrategyId(aOperation);
|
|
146 |
iStrategyStep = MEtelDriverStrategy::EStartStep;
|
|
147 |
iCompletionStatus = KErrNone;
|
|
148 |
|
|
149 |
SetActive();
|
|
150 |
Strategy(iStrategyId).Next(*this, &iStatus);
|
|
151 |
|
|
152 |
return KErrNone;
|
|
153 |
}
|
|
154 |
|
|
155 |
|
|
156 |
void CEtelDriverContext::RunL()
|
|
157 |
{
|
|
158 |
SPUDTEL_FNLOG("CEtelDriverContext::RunL()");
|
|
159 |
ASSERT(iStrategyId < ESentinelStrategy);
|
|
160 |
|
|
161 |
if(iStatus != KErrNone)
|
|
162 |
{
|
|
163 |
if(iCompletionStatus == KErrNone)
|
|
164 |
{
|
|
165 |
iCompletionStatus = iStatus;
|
|
166 |
SPUDTEL_ERROR_LOG(_L("Last async request completed with error %d"), iStatus.Int());
|
|
167 |
}
|
|
168 |
// Don't continue with the strategy for all cases except Delete
|
|
169 |
// N.B.: deletion of a context has to be done till the very last step
|
|
170 |
// to ensure proper cleanup of resources.
|
|
171 |
if(EContextDeleteStrategy != iStrategyId)
|
|
172 |
{
|
|
173 |
iStrategyStep = MEtelDriverStrategy::EFinishStep;
|
|
174 |
}
|
|
175 |
SPUDTELVERBOSE_INFO_LOG(_L("Strategy is completed"));
|
|
176 |
}
|
|
177 |
|
|
178 |
if(MEtelDriverStrategy::EFinishStep == iStrategyStep)
|
|
179 |
{
|
|
180 |
// we are done
|
|
181 |
SPUDTELVERBOSE_INFO_LOG(_L("Strategy is completed"));
|
|
182 |
Strategy(iStrategyId).NotifyFsm (*this, iCompletionStatus);
|
|
183 |
}
|
|
184 |
else
|
|
185 |
{
|
|
186 |
// continue with next step
|
|
187 |
SetActive();
|
|
188 |
Strategy(iStrategyId).Next(*this, &iStatus);
|
|
189 |
}
|
|
190 |
}
|
|
191 |
|
|
192 |
/** cancels last async request */
|
|
193 |
void CEtelDriverContext::DoCancel()
|
|
194 |
{
|
|
195 |
SPUDTEL_FNLOG("CEtelDriverContext::DoCancel()");
|
|
196 |
|
|
197 |
if(IsActive())
|
|
198 |
{
|
|
199 |
// delegate to strategy
|
|
200 |
Strategy(iStrategyId).CancelAsyncRequest(*this);
|
|
201 |
}
|
|
202 |
iStrategyStep = MEtelDriverStrategy::EFinishStep;
|
|
203 |
SPUDTELVERBOSE_INFO_LOG(_L("Strategy is cancelled"));
|
|
204 |
}
|
|
205 |
|
|
206 |
/** accessor */
|
|
207 |
const TName& CEtelDriverContext::ExistingContextName() const
|
|
208 |
{
|
|
209 |
SPUDTEL_FNLOG("CEtelDriverContext::ExistingContextName()");
|
|
210 |
for (TContextId i = 0; i < static_cast<TContextId>(iFactory.ContextCount()); i++)
|
|
211 |
{
|
|
212 |
|
|
213 |
if (iFactory.HasContext(i) && iFactory.Context(i).Name().Size())
|
|
214 |
{
|
|
215 |
return iFactory.Context(i).Name();
|
|
216 |
}
|
|
217 |
}
|
|
218 |
|
|
219 |
// Unacceptable situation: we didn't create a single context yet
|
|
220 |
SPUDTEL_ERROR_LOG(_L("CEtelDriverContext::ExistingContextName - can't find existing context. return %S"), &iFactory.Context(0).Name());
|
|
221 |
ASSERT(EFalse);
|
|
222 |
return iFactory.Context(0).Name();
|
|
223 |
}
|
|
224 |
|
|
225 |
/** accessor
|
|
226 |
|
|
227 |
@return reference to etel RPhone
|
|
228 |
*/
|
|
229 |
RPhone& CEtelDriverContext::Phone() const
|
|
230 |
{
|
|
231 |
return iFactory.Phone();
|
|
232 |
}
|
|
233 |
|
|
234 |
/** accessor
|
|
235 |
|
|
236 |
@return reference to etel RPacketService
|
|
237 |
*/
|
|
238 |
RPacketService& CEtelDriverContext::PacketService() const
|
|
239 |
{
|
|
240 |
return iFactory.PacketService();
|
|
241 |
}
|
|
242 |
|
|
243 |
/** accessor
|
|
244 |
|
|
245 |
@return reference to etel driver strategy
|
|
246 |
*/
|
|
247 |
MEtelDriverStrategy& CEtelDriverContext::Strategy(TEtelDriverStrategy aId) const
|
|
248 |
{
|
|
249 |
return iFactory.Strategy(aId);
|
|
250 |
}
|
|
251 |
|
|
252 |
/** accessor
|
|
253 |
|
|
254 |
@return reference to pdp fsm interface
|
|
255 |
*/
|
|
256 |
CPdpFsmInterface& CEtelDriverContext::PdpFsmInterface() const
|
|
257 |
{
|
|
258 |
return iFactory.PdpFsmInterface();
|
|
259 |
}
|
|
260 |
|
|
261 |
/** start pdp notifications */
|
|
262 |
void CEtelDriverContext::StartNotifications() const
|
|
263 |
{
|
|
264 |
iFactory.StartPdpNotifications(iId);
|
|
265 |
}
|
|
266 |
|
|
267 |
/** stops pdp notifications */
|
|
268 |
void CEtelDriverContext::StopNotifications() const
|
|
269 |
{
|
|
270 |
iFactory.CancelPdpNotifications(iId);
|
|
271 |
}
|
|
272 |
|
|
273 |
|
|
274 |
#ifdef _DEBUG
|
|
275 |
void CEtelDriverContext::DumpReqProfileParameters ()
|
|
276 |
{
|
|
277 |
SPUDTEL_INFO_LOG1( _L("Requested Profile Parameters Dump - Context Id = %d"), Id());
|
|
278 |
SPUDTEL_INFO_LOG( _L("==========================================================="));
|
|
279 |
SPUDTEL_INFO_LOG1( _L("ExtensionId = %d"), iQosRequested.ExtensionId());
|
|
280 |
|
|
281 |
if (iQosRequested.ExtensionId() != TPacketDataConfigBase::KConfigRel99Rel4
|
|
282 |
&& iQosRequested.ExtensionId() != TPacketDataConfigBase::KConfigRel5)
|
|
283 |
{
|
|
284 |
SPUDTEL_INFO_LOG( _L("Invalid/Unsupported ExtensionId"));
|
|
285 |
return;
|
|
286 |
}
|
|
287 |
|
|
288 |
if (iQosRequested.ExtensionId() == TPacketDataConfigBase::KConfigRel99Rel4
|
|
289 |
|| iQosRequested.ExtensionId() == TPacketDataConfigBase::KConfigRel5)
|
|
290 |
{
|
|
291 |
SPUDTEL_INFO_LOG1( _L("[1]iReqTrafficClass = %d"), iQosRequested.RequestedQoSR99_R4().iReqTrafficClass);
|
|
292 |
SPUDTEL_INFO_LOG1( _L("[2]iMinTrafficClass = %d"), iQosRequested.RequestedQoSR99_R4().iMinTrafficClass);
|
|
293 |
SPUDTEL_INFO_LOG1( _L("[3]iReqDeliveryOrderReqd = %d"), iQosRequested.RequestedQoSR99_R4().iReqDeliveryOrderReqd);
|
|
294 |
SPUDTEL_INFO_LOG1( _L("[4]iMinDeliveryOrderReqd = %d"), iQosRequested.RequestedQoSR99_R4().iMinDeliveryOrderReqd);
|
|
295 |
SPUDTEL_INFO_LOG1( _L("[5]iReqDeliverErroneousSDU = %d"), iQosRequested.RequestedQoSR99_R4().iReqDeliverErroneousSDU);
|
|
296 |
SPUDTEL_INFO_LOG1( _L("[6]iMinDeliverErroneousSDU = %d"), iQosRequested.RequestedQoSR99_R4().iMinDeliverErroneousSDU);
|
|
297 |
SPUDTEL_INFO_LOG1( _L("[7]iReqMaxSDUSize = %d"), iQosRequested.RequestedQoSR99_R4().iReqMaxSDUSize);
|
|
298 |
SPUDTEL_INFO_LOG1( _L("[8]iMinAcceptableMaxSDUSize = %d"), iQosRequested.RequestedQoSR99_R4().iMinAcceptableMaxSDUSize);
|
|
299 |
SPUDTEL_INFO_LOG1( _L("[9]iReqMaxRate.iUplinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iReqMaxRate.iUplinkRate);
|
|
300 |
SPUDTEL_INFO_LOG1( _L("[10]iReqMaxRate.iDownlinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iReqMaxRate.iDownlinkRate);
|
|
301 |
SPUDTEL_INFO_LOG1( _L("[11]iMinAcceptableMaxRate.iUplinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iMinAcceptableMaxRate.iUplinkRate);
|
|
302 |
SPUDTEL_INFO_LOG1( _L("[12]iMinAcceptableMaxRate.iDownlinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iMinAcceptableMaxRate.iDownlinkRate);
|
|
303 |
SPUDTEL_INFO_LOG1( _L("[13]iReqBER = %d"), iQosRequested.RequestedQoSR99_R4().iReqBER);
|
|
304 |
SPUDTEL_INFO_LOG1( _L("[14]iMaxBER = %d"), iQosRequested.RequestedQoSR99_R4().iMaxBER);
|
|
305 |
SPUDTEL_INFO_LOG1( _L("[15]iReqSDUErrorRatio = %d"), iQosRequested.RequestedQoSR99_R4().iReqSDUErrorRatio);
|
|
306 |
SPUDTEL_INFO_LOG1( _L("[16]iMaxSDUErrorRatio = %d"), iQosRequested.RequestedQoSR99_R4().iMaxSDUErrorRatio);
|
|
307 |
SPUDTEL_INFO_LOG1( _L("[17]iReqTrafficHandlingPriority = %d"), iQosRequested.RequestedQoSR99_R4().iReqTrafficHandlingPriority);
|
|
308 |
SPUDTEL_INFO_LOG1( _L("[18]iReqTrafficHandlingPriority = %d"), iQosRequested.RequestedQoSR99_R4().iMinTrafficHandlingPriority);
|
|
309 |
SPUDTEL_INFO_LOG1( _L("[19]iReqTransferDelay = %d"), iQosRequested.RequestedQoSR99_R4().iReqTransferDelay);
|
|
310 |
SPUDTEL_INFO_LOG1( _L("[20]iMaxTransferDelay = %d"), iQosRequested.RequestedQoSR99_R4().iMaxTransferDelay);
|
|
311 |
SPUDTEL_INFO_LOG1( _L("[21]iReqGuaranteedRate.iUplinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iReqGuaranteedRate.iUplinkRate);
|
|
312 |
SPUDTEL_INFO_LOG1( _L("[22]iReqGuaranteedRate.iDownlinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iReqGuaranteedRate.iDownlinkRate);
|
|
313 |
SPUDTEL_INFO_LOG1( _L("[23]iMinGuaranteedRate.iUplinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iMinGuaranteedRate.iUplinkRate);
|
|
314 |
SPUDTEL_INFO_LOG1( _L("[24]iMinGuaranteedRate.iDownlinkRate = %d"), iQosRequested.RequestedQoSR99_R4().iMinGuaranteedRate.iDownlinkRate);
|
|
315 |
}
|
|
316 |
|
|
317 |
#ifdef SYMBIAN_NETWORKING_UMTSR5
|
|
318 |
if (iQosRequested.ExtensionId() == TPacketDataConfigBase::KConfigRel5)
|
|
319 |
{
|
|
320 |
SPUDTEL_INFO_LOG1( _L("[25]iSignallingIndication = %d"), iQosRequested.RequestedQoSR5().iSignallingIndication);
|
|
321 |
SPUDTEL_INFO_LOG1( _L("[26]iSourceStatisticsDescriptor = %d"), iQosRequested.RequestedQoSR5().iSourceStatisticsDescriptor);
|
|
322 |
}
|
|
323 |
#endif
|
|
324 |
}
|
|
325 |
|
|
326 |
|
|
327 |
|
|
328 |
void CEtelDriverContext::DumpNegProfileParameters ()
|
|
329 |
{
|
|
330 |
SPUDTEL_INFO_LOG1( _L("Negotiated Profile Parameters Dump - Context Id = %d"), Id());
|
|
331 |
SPUDTEL_INFO_LOG( _L("==========================================================="));
|
|
332 |
SPUDTEL_INFO_LOG1( _L("ExtensionId = %d"), iQosNegotiated.ExtensionId());
|
|
333 |
|
|
334 |
if (iQosNegotiated.ExtensionId() != TPacketDataConfigBase::KConfigRel99Rel4
|
|
335 |
&& iQosNegotiated.ExtensionId() != TPacketDataConfigBase::KConfigRel5)
|
|
336 |
{
|
|
337 |
SPUDTEL_INFO_LOG( _L("Invalid/Unsupported ExtensionId"));
|
|
338 |
return;
|
|
339 |
}
|
|
340 |
|
|
341 |
if (iQosNegotiated.ExtensionId() == TPacketDataConfigBase::KConfigRel99Rel4
|
|
342 |
|| iQosNegotiated.ExtensionId() == TPacketDataConfigBase::KConfigRel5)
|
|
343 |
{
|
|
344 |
SPUDTEL_INFO_LOG1( _L("[2]iTrafficClass = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iTrafficClass);
|
|
345 |
SPUDTEL_INFO_LOG1( _L("[3]iDeliveryOrderReqd = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iDeliveryOrderReqd);
|
|
346 |
SPUDTEL_INFO_LOG1( _L("[4]iDeliverErroneousSDU = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iDeliverErroneousSDU);
|
|
347 |
SPUDTEL_INFO_LOG1( _L("[5]iMaxSDUSize = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iMaxSDUSize);
|
|
348 |
SPUDTEL_INFO_LOG1( _L("[6]iBER = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iBER);
|
|
349 |
SPUDTEL_INFO_LOG1( _L("[7]iSDUErrorRatio = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iSDUErrorRatio);
|
|
350 |
SPUDTEL_INFO_LOG1( _L("[8]iTrafficHandlingPriority = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iTrafficHandlingPriority);
|
|
351 |
SPUDTEL_INFO_LOG1( _L("[9]iTransferDelay = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iTransferDelay);
|
|
352 |
SPUDTEL_INFO_LOG1( _L("[10]iGuaranteedRate.iUplinkRate = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iGuaranteedRate.iUplinkRate);
|
|
353 |
SPUDTEL_INFO_LOG1( _L("[11]iGuaranteedRate.iDownlinkRate = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iGuaranteedRate.iDownlinkRate);
|
|
354 |
SPUDTEL_INFO_LOG1( _L("[12]iMaxRate.iUplinkRate = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iMaxRate.iUplinkRate);
|
|
355 |
SPUDTEL_INFO_LOG1( _L("[13]iMaxRate.iDownlinkRate = %d"), iQosNegotiated.NegotiatedQoSR99_R4().iMaxRate.iDownlinkRate);
|
|
356 |
}
|
|
357 |
|
|
358 |
|
|
359 |
#ifdef SYMBIAN_NETWORKING_UMTSR5
|
|
360 |
if (iQosNegotiated.ExtensionId() == TPacketDataConfigBase::KConfigRel5)
|
|
361 |
{
|
|
362 |
SPUDTEL_INFO_LOG1( _L("[14]iSignallingIndication = %d"), iQosNegotiated.NegotiatedQoSR5().iSignallingIndication);
|
|
363 |
SPUDTEL_INFO_LOG1( _L("[15]iSourceStatisticsDescriptor = %d"), iQosNegotiated.NegotiatedQoSR5().iSourceStatisticsDescriptor);
|
|
364 |
}
|
|
365 |
#endif
|
|
366 |
}
|
|
367 |
|
|
368 |
#endif
|
|
369 |
// _DEBUG
|