1 |
1254 |
phoenix |
//####COPYRIGHTBEGIN####
|
2 |
|
|
//
|
3 |
|
|
// ----------------------------------------------------------------------------
|
4 |
|
|
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
|
5 |
|
|
//
|
6 |
|
|
// This program is part of the eCos host tools.
|
7 |
|
|
//
|
8 |
|
|
// This program is free software; you can redistribute it and/or modify it
|
9 |
|
|
// under the terms of the GNU General Public License as published by the Free
|
10 |
|
|
// Software Foundation; either version 2 of the License, or (at your option)
|
11 |
|
|
// any later version.
|
12 |
|
|
//
|
13 |
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
14 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
15 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
16 |
|
|
// more details.
|
17 |
|
|
//
|
18 |
|
|
// You should have received a copy of the GNU General Public License along with
|
19 |
|
|
// this program; if not, write to the Free Software Foundation, Inc.,
|
20 |
|
|
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
21 |
|
|
//
|
22 |
|
|
// ----------------------------------------------------------------------------
|
23 |
|
|
//
|
24 |
|
|
//####COPYRIGHTEND####
|
25 |
|
|
//#####DESCRIPTIONBEGIN####
|
26 |
|
|
//
|
27 |
|
|
// Author(s): sdf
|
28 |
|
|
// Contributors: sdf
|
29 |
|
|
// Date: 1999-04-01
|
30 |
|
|
// Description: Holds information related to target reset
|
31 |
|
|
// Usage:
|
32 |
|
|
//
|
33 |
|
|
//####DESCRIPTIONEND####
|
34 |
|
|
#include "eCosStd.h"
|
35 |
|
|
#include "eCosThreadUtils.h"
|
36 |
|
|
#include "eCosTrace.h"
|
37 |
|
|
#include "ResetAttributes.h"
|
38 |
|
|
|
39 |
|
|
const CResetAttributes CResetAttributes::NoReset;
|
40 |
|
|
|
41 |
|
|
CResetAttributes::CResetAttributes(LPCTSTR psz) :
|
42 |
|
|
// Default values:
|
43 |
|
|
m_nDelay(1000),
|
44 |
|
|
m_nReadTimeout(10*1000),
|
45 |
|
|
m_nBaud(38400)
|
46 |
|
|
{
|
47 |
|
|
// Remove spaces
|
48 |
|
|
while(*psz){
|
49 |
|
|
if(!_istspace(*psz)){
|
50 |
|
|
m_str+=*psz;
|
51 |
|
|
}
|
52 |
|
|
psz++;
|
53 |
|
|
}
|
54 |
|
|
}
|
55 |
|
|
|
56 |
|
|
/*
|
57 |
|
|
LPCTSTR CResetAttributes::Image(int nErr)
|
58 |
|
|
{
|
59 |
|
|
switch(nErr){
|
60 |
|
|
case RESET_OK:
|
61 |
|
|
return _T("RESET_OK");
|
62 |
|
|
break;
|
63 |
|
|
case RESET_ILLEGAL_DEVICE_CODE:
|
64 |
|
|
return _T("Illegal device code");
|
65 |
|
|
break;
|
66 |
|
|
case RESET_NO_REPLY:
|
67 |
|
|
return _T("No reply from reset unit");
|
68 |
|
|
break;
|
69 |
|
|
case RESET_BAD_CHECKSUM:
|
70 |
|
|
return _T("Bad checksum");
|
71 |
|
|
break;
|
72 |
|
|
case RESET_BAD_ACK:
|
73 |
|
|
return _T("Bad ack");
|
74 |
|
|
break;
|
75 |
|
|
default:
|
76 |
|
|
return _T("Unknown reset error");
|
77 |
|
|
break;
|
78 |
|
|
}
|
79 |
|
|
}
|
80 |
|
|
*/
|
81 |
|
|
void CResetAttributes::SuckThreadFunc()
|
82 |
|
|
{
|
83 |
|
|
m_strResetOutput=_T("");
|
84 |
|
|
|
85 |
|
|
// Board has apparently been powered on. Suck initial output.
|
86 |
|
|
ResetLog(String::SFormat(_T("Reading board startup output from %s with timeout of %d seconds..."),(LPCTSTR)m_strAuxPort,m_nReadTimeout/1000));
|
87 |
|
|
|
88 |
|
|
enum {BUFSIZE=512};
|
89 |
|
|
TCHAR buf[1+BUFSIZE];
|
90 |
|
|
memset(buf,0,BUFSIZE); // safety for string functions in IsValidReset
|
91 |
|
|
do {
|
92 |
|
|
unsigned int dwRead=0;
|
93 |
|
|
// We are working in non-blocking mode
|
94 |
|
|
if(m_Socket.Ok()){
|
95 |
|
|
if(!m_Socket.Peek(dwRead)||!m_Socket.recv(buf,MIN(dwRead,BUFSIZE))){
|
96 |
|
|
break;
|
97 |
|
|
}
|
98 |
|
|
} else if (!m_Serial.Read(buf,BUFSIZE,dwRead)){
|
99 |
|
|
m_Serial.ClearError();
|
100 |
|
|
continue;
|
101 |
|
|
}
|
102 |
|
|
if(dwRead>0){
|
103 |
|
|
buf[dwRead]=_TCHAR('\0');
|
104 |
|
|
|
105 |
|
|
// Remove unprintable characters
|
106 |
|
|
String str;
|
107 |
|
|
for(const TCHAR *t=buf;*t;t++){
|
108 |
|
|
if(_istprint(*t)){
|
109 |
|
|
str+=*t;
|
110 |
|
|
}
|
111 |
|
|
}
|
112 |
|
|
|
113 |
|
|
if(m_pfnReset){
|
114 |
|
|
ENTERCRITICAL;
|
115 |
|
|
m_pfnReset(m_pfnResetparam,str);
|
116 |
|
|
LEAVECRITICAL;
|
117 |
|
|
}
|
118 |
|
|
|
119 |
|
|
ResetLog(str);
|
120 |
|
|
m_strResetOutput+=str;
|
121 |
|
|
|
122 |
|
|
if(IsValidReset()){
|
123 |
|
|
break;
|
124 |
|
|
}
|
125 |
|
|
// } else { // Nothing read
|
126 |
|
|
// CeCosThreadUtils::Sleep(50);
|
127 |
|
|
}
|
128 |
|
|
} while (0==m_tResetOccurred || Now()-m_tResetOccurred<m_nReadTimeout);
|
129 |
|
|
|
130 |
|
|
if(0==m_strResetOutput.size()){
|
131 |
|
|
ResetLog(_T("No response from board"));
|
132 |
|
|
} else {
|
133 |
|
|
if(m_pfnReset){
|
134 |
|
|
ENTERCRITICAL;
|
135 |
|
|
m_pfnReset(m_pfnResetparam,_T("\n"));
|
136 |
|
|
LEAVECRITICAL;
|
137 |
|
|
}
|
138 |
|
|
TRACE(_T("%s"),(LPCTSTR)m_strResetOutput);
|
139 |
|
|
}
|
140 |
|
|
}
|
141 |
|
|
|
142 |
|
|
bool CResetAttributes::Reset(Action action,bool bCheckOutput)
|
143 |
|
|
{
|
144 |
|
|
m_tResetOccurred=0;
|
145 |
|
|
m_strResetOutput=_T("");
|
146 |
|
|
bool rc=false;
|
147 |
|
|
CeCosSocket sock;
|
148 |
|
|
String strStatus;
|
149 |
|
|
strStatus.Format(_T("Reset target using %s %s port=%s(%d) read timeout=%d delay=%d"),
|
150 |
|
|
(LPCTSTR)m_strHostPort,(LPCTSTR)m_strControl,
|
151 |
|
|
(LPCTSTR)m_strAuxPort, m_nBaud, m_nReadTimeout, m_nDelay);
|
152 |
|
|
if(bCheckOutput){
|
153 |
|
|
strStatus+=_T(" expect(");
|
154 |
|
|
for(unsigned int i=0;i<m_arValidResetStrings.size();i++){
|
155 |
|
|
if(i>0){
|
156 |
|
|
strStatus+=_TCHAR(',');
|
157 |
|
|
}
|
158 |
|
|
strStatus+=m_arValidResetStrings[i];
|
159 |
|
|
}
|
160 |
|
|
strStatus+=_T(")");
|
161 |
|
|
}
|
162 |
|
|
ResetLog(strStatus);
|
163 |
|
|
|
164 |
|
|
// Open up communication to port whence we read the board startup
|
165 |
|
|
bool bThreadDone=false;
|
166 |
|
|
bCheckOutput&=(m_strAuxPort.size()>0);
|
167 |
|
|
if(bCheckOutput){
|
168 |
|
|
TRACE(_T("Opening %s\n"),(LPCTSTR)m_strAuxPort);
|
169 |
|
|
if(CeCosSocket::IsLegalHostPort(m_strAuxPort)){
|
170 |
|
|
// tcp/ip port
|
171 |
|
|
if(!m_Socket.Connect(m_strAuxPort,m_nReadTimeout)){
|
172 |
|
|
ResetLog(String::SFormat(_T("Failed to open %s - %s"),(LPCTSTR)m_strAuxPort,(LPCTSTR)m_Socket.SocketErrString()));
|
173 |
|
|
return false;
|
174 |
|
|
}
|
175 |
|
|
} else {
|
176 |
|
|
// Comms device
|
177 |
|
|
m_Serial.SetBlockingReads(false);
|
178 |
|
|
if(m_Serial.Open(m_strAuxPort,m_nBaud)){
|
179 |
|
|
m_Serial.Flush();
|
180 |
|
|
} else {
|
181 |
|
|
ResetLog(String::SFormat(_T("Failed to open comms port %s - %s"),(LPCTSTR)m_strAuxPort,(LPCTSTR)m_Serial.ErrString()));
|
182 |
|
|
return false;
|
183 |
|
|
}
|
184 |
|
|
}
|
185 |
|
|
CeCosThreadUtils::RunThread(SSuckThreadFunc,this,&bThreadDone,_T("SSuckThreadFunc"));
|
186 |
|
|
} else {
|
187 |
|
|
ResetLog(_T("[not checking output]"));
|
188 |
|
|
}
|
189 |
|
|
|
190 |
|
|
// This will be true if we need to talk to a reset server, false to talk down a local port
|
191 |
|
|
bool bRemote=CeCosSocket::IsLegalHostPort(m_strHostPort);
|
192 |
|
|
if(bRemote){
|
193 |
|
|
if(sock.Connect(m_strHostPort,10*1000)){
|
194 |
|
|
// Write the message to the socket
|
195 |
|
|
int nDelay=(action==ON_OFF || action==OFF_ON)?m_nDelay:0;
|
196 |
|
|
TRACE(_T("-Control=%s -Action=%d -Delay=%d"),(LPCTSTR)m_strControl,action,nDelay);
|
197 |
|
|
if(sock.sendString(String::SFormat(_T("-Control=%s -Action=%d -Delay=%d"),(LPCTSTR)m_strControl,action,nDelay),_T("Reset control codes"), 10*1000)){
|
198 |
|
|
// Wait for an acknowledgement
|
199 |
|
|
String strResponse;
|
200 |
|
|
if(sock.recvString(strResponse, _T("Response"), nDelay+20*1000)){
|
201 |
|
|
rc=(0==strResponse.size());
|
202 |
|
|
if(!rc && m_pfnReset){
|
203 |
|
|
ResetLog(String::SFormat(_T("Reset server reports error '%s'"),(LPCTSTR)strResponse));
|
204 |
|
|
}
|
205 |
|
|
} else {
|
206 |
|
|
ResetLog(String::SFormat(_T("Failed to read response from reset server %s - %s"),(LPCTSTR)m_strHostPort,(LPCTSTR)sock.SocketErrString()));
|
207 |
|
|
}
|
208 |
|
|
} else {
|
209 |
|
|
ResetLog(String::SFormat(_T("Failed to contact reset server %s - %s"),(LPCTSTR)m_strHostPort,(LPCTSTR)sock.SocketErrString()));
|
210 |
|
|
}
|
211 |
|
|
m_tResetOccurred=Now();
|
212 |
|
|
if(bCheckOutput){
|
213 |
|
|
if(!rc){
|
214 |
|
|
// force thread to time out
|
215 |
|
|
m_tResetOccurred=Now()-m_nReadTimeout;
|
216 |
|
|
}
|
217 |
|
|
CeCosThreadUtils::WaitFor(bThreadDone); // do not apply a timeout - the thread has one
|
218 |
|
|
rc=IsValidReset();
|
219 |
|
|
ResetLog(rc?_T("Reset output valid"):_T("Reset output INVALID"));
|
220 |
|
|
}
|
221 |
|
|
} else {
|
222 |
|
|
ResetLog(String::SFormat(_T("Failed to contact reset server %s - %s"),(LPCTSTR)m_strHostPort,(LPCTSTR)sock.SocketErrString()));
|
223 |
|
|
}
|
224 |
|
|
} else {
|
225 |
|
|
// Sending something locally
|
226 |
|
|
m_tResetOccurred=Now();
|
227 |
|
|
unsigned int nWritten;
|
228 |
|
|
m_Serial.Write((void *)(LPCTSTR)m_strControl,1,nWritten);
|
229 |
|
|
if(bCheckOutput){
|
230 |
|
|
CeCosThreadUtils::WaitFor(bThreadDone); // do not apply a timeout - the thread has one
|
231 |
|
|
rc=IsValidReset();
|
232 |
|
|
ResetLog(rc?_T("Reset output valid"):_T("Reset output INVALID"));
|
233 |
|
|
}
|
234 |
|
|
}
|
235 |
|
|
|
236 |
|
|
if(m_Socket.Ok()){
|
237 |
|
|
m_Socket.Close();
|
238 |
|
|
} else {
|
239 |
|
|
m_Serial.Close();
|
240 |
|
|
}
|
241 |
|
|
return rc && bCheckOutput;
|
242 |
|
|
}
|
243 |
|
|
|
244 |
|
|
// We expect to be passed a string that starts with "xxx(yyy)"
|
245 |
|
|
// and the task is to extract xxx into strID and yyy into strArg
|
246 |
|
|
const TCHAR *CResetAttributes::GetIdAndArg (LPCTSTR psz,String &strID,String &strArg)
|
247 |
|
|
{
|
248 |
|
|
const TCHAR *cEnd=_tcschr(psz,_TCHAR('('));
|
249 |
|
|
if(cEnd){
|
250 |
|
|
strID=String(psz,cEnd-psz);
|
251 |
|
|
int nNest=0;
|
252 |
|
|
for(const TCHAR *c=cEnd;*c;c++){
|
253 |
|
|
if(_TCHAR('(')==*c){
|
254 |
|
|
nNest++;
|
255 |
|
|
} else if(_TCHAR(')')==*c){
|
256 |
|
|
nNest--;
|
257 |
|
|
if(0==nNest){
|
258 |
|
|
strArg=String(cEnd+1,c-(cEnd+1));
|
259 |
|
|
return c+1;
|
260 |
|
|
}
|
261 |
|
|
}
|
262 |
|
|
}
|
263 |
|
|
assert(false);
|
264 |
|
|
}
|
265 |
|
|
return 0;
|
266 |
|
|
}
|
267 |
|
|
|
268 |
|
|
// Do the reset
|
269 |
|
|
CResetAttributes::ResetResult CResetAttributes::Reset (LogFunc *pfnLog, void *pfnLogparam,bool bCheckOnly)
|
270 |
|
|
{
|
271 |
|
|
m_pfnReset=pfnLog;
|
272 |
|
|
m_pfnResetparam=pfnLogparam;
|
273 |
|
|
|
274 |
|
|
// First we clean up the reset string so as to make subsequent parsing less complicated.
|
275 |
|
|
// Spaces have already been removed in the ctor
|
276 |
|
|
|
277 |
|
|
// Check paren matching:
|
278 |
|
|
int nNest=0;
|
279 |
|
|
for(const TCHAR *c=m_str;*c;c++){
|
280 |
|
|
if(_TCHAR('(')==*c){
|
281 |
|
|
nNest++;
|
282 |
|
|
} else if(_TCHAR(')')==*c){
|
283 |
|
|
nNest--;
|
284 |
|
|
if(nNest<0){
|
285 |
|
|
ResetLog(_T("Too many right parentheses"));
|
286 |
|
|
return INVALID_STRING;
|
287 |
|
|
}
|
288 |
|
|
}
|
289 |
|
|
}
|
290 |
|
|
if(nNest>0){
|
291 |
|
|
ResetLog(_T("Too many left parentheses"));
|
292 |
|
|
return INVALID_STRING;
|
293 |
|
|
}
|
294 |
|
|
|
295 |
|
|
return Parse(m_str,bCheckOnly);
|
296 |
|
|
}
|
297 |
|
|
|
298 |
|
|
// This function parses the reset string, whose form is something like:
|
299 |
|
|
// expect($T05) 3(off(ginga:5000,a1) delay(2000) on(ginga:5000,a1,com1,38400,10000))
|
300 |
|
|
// It is recursive (which is another reason elementary syntax checking was carried out above)
|
301 |
|
|
// and calls itself to perform repeats [e.g. 3(...)]
|
302 |
|
|
CResetAttributes::ResetResult CResetAttributes::Parse (LPCTSTR psz,bool bCheckOnly)
|
303 |
|
|
{
|
304 |
|
|
enum {ARGSEP=_TCHAR(',')};
|
305 |
|
|
bool bCheck=false;
|
306 |
|
|
for(const TCHAR *c=psz;*c;){
|
307 |
|
|
String strID,strArg;
|
308 |
|
|
c=GetIdAndArg(c,strID,strArg);
|
309 |
|
|
if(0==c){
|
310 |
|
|
ResetLog(_T("Invalid reset string"));
|
311 |
|
|
return INVALID_STRING;
|
312 |
|
|
}
|
313 |
|
|
|
314 |
|
|
if(isdigit(*(LPCTSTR)strID)){
|
315 |
|
|
// Process a repeat-until-reset. Syntax is n(resetstring)
|
316 |
|
|
int nRepeat=_ttoi(strID);
|
317 |
|
|
if(0==nRepeat){
|
318 |
|
|
ResetLog(_T("Invalid reset string"));
|
319 |
|
|
return INVALID_STRING;
|
320 |
|
|
}
|
321 |
|
|
if(bCheckOnly){
|
322 |
|
|
return Parse(strArg,true);
|
323 |
|
|
} else {
|
324 |
|
|
while(nRepeat--){
|
325 |
|
|
ResetResult r=Parse(strArg);
|
326 |
|
|
if(RESET_OK==r||INVALID_STRING==r){
|
327 |
|
|
return r;
|
328 |
|
|
}
|
329 |
|
|
}
|
330 |
|
|
}
|
331 |
|
|
} else if (_T("expect")==strID) {
|
332 |
|
|
// Expected string(s). e.g. expect(str1,str2,...).
|
333 |
|
|
strArg.Chop(m_arValidResetStrings,ARGSEP,true);
|
334 |
|
|
} else if (_T("port")==strID) {
|
335 |
|
|
// Port information. e.g. port(com1,38400,1000)
|
336 |
|
|
// This information will apply to all subsequent actions until overwritten.
|
337 |
|
|
// Specifically args are:
|
338 |
|
|
// 0. Port
|
339 |
|
|
// 1. Baud
|
340 |
|
|
// 2. Read timeout
|
341 |
|
|
StringArray ar;
|
342 |
|
|
int nArgs=strArg.Chop(ar,ARGSEP,true);
|
343 |
|
|
if(nArgs>0){
|
344 |
|
|
m_strAuxPort=ar[0];
|
345 |
|
|
}
|
346 |
|
|
if(nArgs>1){
|
347 |
|
|
m_nBaud=_ttoi(ar[1]);
|
348 |
|
|
}
|
349 |
|
|
if(nArgs>2){
|
350 |
|
|
m_nReadTimeout=_ttoi(ar[2]);
|
351 |
|
|
}
|
352 |
|
|
} else if (_T("off")==strID || _T("on")==strID || _T("on_off")==strID || _T("off_on")==strID) {
|
353 |
|
|
// Action information. e.g. off(ginga:500,A4,com1,38400,10000,1000)
|
354 |
|
|
// Specifically args are:
|
355 |
|
|
// 0. Reset host:port
|
356 |
|
|
// 1. Control string
|
357 |
|
|
// 2. Port
|
358 |
|
|
// 3. Baud
|
359 |
|
|
// 4. Read timeout
|
360 |
|
|
// 5. Delay
|
361 |
|
|
StringArray ar;
|
362 |
|
|
int nArgs=strArg.Chop(ar,ARGSEP,true);
|
363 |
|
|
if(nArgs>0){
|
364 |
|
|
m_strHostPort=ar[0];
|
365 |
|
|
}
|
366 |
|
|
if(nArgs>1){
|
367 |
|
|
m_strControl=ar[1];
|
368 |
|
|
}
|
369 |
|
|
if(nArgs>2){
|
370 |
|
|
m_strAuxPort=ar[2];
|
371 |
|
|
}
|
372 |
|
|
if(nArgs>3){
|
373 |
|
|
m_nBaud=_ttoi(ar[3]);
|
374 |
|
|
}
|
375 |
|
|
if(nArgs>4){
|
376 |
|
|
m_nReadTimeout=_ttoi(ar[4]);
|
377 |
|
|
}
|
378 |
|
|
if(nArgs>5){
|
379 |
|
|
m_nDelay=_ttoi(ar[5]);
|
380 |
|
|
}
|
381 |
|
|
|
382 |
|
|
if(0==m_strHostPort.size()){
|
383 |
|
|
ResetLog(_T("Failed to specify reset host:port"));
|
384 |
|
|
return INVALID_STRING;
|
385 |
|
|
}
|
386 |
|
|
|
387 |
|
|
Action action=ON; // prevent compiler warning
|
388 |
|
|
if(_T("on")==strID){
|
389 |
|
|
action=ON;
|
390 |
|
|
} else if(_T("off")==strID){
|
391 |
|
|
action=OFF;
|
392 |
|
|
} else if(_T("on_off")==strID){
|
393 |
|
|
action=ON_OFF;
|
394 |
|
|
} else if(_T("off_on")==strID){
|
395 |
|
|
action=OFF_ON;
|
396 |
|
|
}
|
397 |
|
|
|
398 |
|
|
if(!bCheckOnly && Reset(action,bCheck||action==ON_OFF||action==OFF_ON)){
|
399 |
|
|
return RESET_OK;
|
400 |
|
|
}
|
401 |
|
|
bCheck ^= 1;
|
402 |
|
|
} else if (_T("delay")==strID) {
|
403 |
|
|
// Delay for a given time right now. e.g. delay(1000)
|
404 |
|
|
// Specifically args are:
|
405 |
|
|
// 0. msec to delay
|
406 |
|
|
TRACE(_T("CeCosThreadUtils::Sleep %d\n"),_ttoi(strArg));
|
407 |
|
|
if(!bCheckOnly){
|
408 |
|
|
CeCosThreadUtils::Sleep(_ttoi(strArg));
|
409 |
|
|
}
|
410 |
|
|
} else {
|
411 |
|
|
ResetLog(String::SFormat(_T("Unrecognized command '%s'"),(LPCTSTR)strID));
|
412 |
|
|
return INVALID_STRING;
|
413 |
|
|
}
|
414 |
|
|
}
|
415 |
|
|
ResetLog(_T("Target reset not verified"));
|
416 |
|
|
return NOT_RESET;
|
417 |
|
|
}
|
418 |
|
|
|
419 |
|
|
// Log some output to the reset log function.
|
420 |
|
|
void CResetAttributes::ResetLog(LPCTSTR psz)
|
421 |
|
|
{
|
422 |
|
|
if(m_pfnReset){
|
423 |
|
|
ENTERCRITICAL;
|
424 |
|
|
m_pfnReset(m_pfnResetparam,String::SFormat(_T("%s >>> %s\n"),(LPCTSTR)CeCosTrace::Timestamp(),psz));
|
425 |
|
|
TRACE(_T("%s"),psz);
|
426 |
|
|
LEAVECRITICAL;
|
427 |
|
|
}
|
428 |
|
|
}
|
429 |
|
|
|
430 |
|
|
bool CResetAttributes::IsValidReset()
|
431 |
|
|
{
|
432 |
|
|
unsigned int n=0;
|
433 |
|
|
ENTERCRITICAL;
|
434 |
|
|
for(int i=m_arValidResetStrings.size()-1;i>=0;--i){
|
435 |
|
|
if(_tcsstr(m_strResetOutput,m_arValidResetStrings[i])){
|
436 |
|
|
n++;
|
437 |
|
|
}
|
438 |
|
|
}
|
439 |
|
|
LEAVECRITICAL;
|
440 |
|
|
return n==m_arValidResetStrings.size();
|
441 |
|
|
}
|