]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/tools/ecostest/common/ResetAttributes.cpp
Initial revision
[karo-tx-redboot.git] / tools / src / tools / ecostest / common / ResetAttributes.cpp
1 //####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 }