]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - tools/src/tools/ecostest/common/eCosTestSerialFilter.cpp
Initial revision
[karo-tx-redboot.git] / tools / src / tools / ecostest / common / eCosTestSerialFilter.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 //=================================================================
26 //
27 //        eCosTestSerialFilter.cpp
28 //
29 //        Serial test filter class
30 //
31 //=================================================================
32 //=================================================================
33 //#####DESCRIPTIONBEGIN####
34 //
35 // Author(s):     jskov
36 // Contributors:  jskov
37 // Date:          1999-03-01
38 // Description:   This filter sits between GDB and the test running on
39 //                the target, allowing testing of the serial driver 
40 //                without confusing GDB.
41 // To Do:
42 //  o Add timeout setup and handling for recovery, can rely on testing
43 //    agent to control global timeout.
44 //  o Saving chunks that caused transfer failure?
45 //     - In SEND with echo, do CRC on 32-byte sub-packets
46 //  o Additional To Do items under each sub-protocol function.
47 //  o Option to get all serial IO written (in hex, > and < prepends 
48 //    input/output lines) to a file.
49 //  o Clean up the mess in this file....
50 //####DESCRIPTIONEND####
51
52 #include "eCosStd.h"
53
54 #include "eCosTestSerialFilter.h"
55 #include "eCosThreadUtils.h"
56
57 char msg_ok[] = "OK";
58 char msg_er[] = "ER";
59
60
61
62 CeCosTestSerialFilter::CeCosTestSerialFilter():
63   m_bOptConsoleOutput(false), 
64   m_bOptSerDebug(false), 
65   m_bOptFilterTrace(false),
66   m_xUnreadBuffer(NULL),
67   m_nUnreadBufferIndex(0), 
68   m_nUnreadBufferSize(0), 
69   m_xStoredTraceBuffer(NULL),
70   m_nStoredTraceBufferSize(0),
71   m_bNullFilter(false), 
72   m_nCmdIndex(0), 
73   m_bCmdFlag(false), 
74   m_bFirstCommandSeen(false),
75   m_cGDBSocket(NULL)
76 {
77 }
78
79 CeCosTestSerialFilter::~CeCosTestSerialFilter()
80 {
81 }
82
83 //------------------------
84 // Output helpers.
85
86 // Encode string in an O-packet and send it to GDB.
87 void 
88 CeCosTestSerialFilter::GDBWrite(const char* pszStr)
89 {
90     if (m_cGDBSocket) {
91         static const char hexchars[] = "0123456789abcdef";
92         char* packet = new char[strlen(pszStr)*2+6];
93         char* p = packet;
94
95         *p++ = '$';
96         *p++ = 'O';
97         unsigned char crc = 'O';
98         char c;
99         for (;;) {
100             c = *pszStr++;
101             if (0 == c)
102                 break;
103
104             char h = hexchars[(c >> 4) & 0x0f];
105             char l = hexchars[c & 0x0f];
106             *p++ = h;
107             *p++ = l;
108             crc = (unsigned char) (crc + h + l);
109         };
110
111         *p++ = '#';
112         *p++ = hexchars[(crc >> 4) & 0x0f];
113         *p++ = hexchars[crc & 0x0f];
114         
115         // Only try to send once. If it fails, it's probably because
116         // GDB has disconnected.
117         m_cGDBSocket->send(packet, p - packet);
118         m_cGDBSocket->recv(&c, 1);
119
120         delete [] packet;
121     }
122 }
123
124 void
125 CeCosTestSerialFilter::ConsoleWrite(const char* pszStr)
126 {
127     fputs(pszStr, stderr);
128     fflush(stderr);
129 }
130
131 void
132 CeCosTestSerialFilter::Trace(const char* pszFormat, ...)
133 {
134   
135   va_list marker;
136   va_start (marker, pszFormat);
137   
138   for(int nLength=100;nLength;) {
139     char *buf=new char[1+nLength];
140     int n=vsnprintf(buf+4, nLength-4, pszFormat, marker ); 
141     if(-1==n){
142       nLength*=2;  // NT behavior
143     } else if (n<nLength){
144       memcpy(buf,"[f] ",4);
145       if (m_bOptConsoleOutput) {
146         ConsoleWrite(buf);
147       } else {
148         GDBWrite(buf);
149       }
150       nLength=0;   // trigger exit from loop
151     } else {
152       nLength=n+1; // UNIX behavior generally, or NT behavior when buffer size exactly matches required length
153     }
154     delete [] buf;
155   }
156   
157   va_end (marker);
158   
159 }
160
161 void
162 CeCosTestSerialFilter::Log(const char* pszFormat, ...)
163 {
164   va_list marker;
165   va_start (marker, pszFormat);
166   
167   for(int nLength=100;nLength;) {
168     char *buf=new char[1+nLength];
169     int n=vsnprintf(buf, nLength, pszFormat, marker ); 
170     if(-1==n){
171       nLength*=2;  // NT behavior
172     } else if (n<nLength){
173       if (m_bOptConsoleOutput) {
174         ConsoleWrite(buf);
175       } else {
176         GDBWrite(buf);
177       }
178       nLength=0;   // trigger exit from loop
179     } else {
180       nLength=n+1; // UNIX behavior generally, or NT behavior when buffer size exactly matches required length
181     }
182     delete [] buf;
183   }
184   
185   va_end (marker);
186 }
187
188
189 void
190 CeCosTestSerialFilter::PrintHex(const unsigned char* d1, int len, data_origin_t origin/*=SF_TARGET*/)
191 {
192     int offset = 0;
193     int i;
194     char buf[128];
195     int width = 8;
196
197     while (len) {
198         int count = MIN(width, len);
199         char* p = buf;
200         switch (origin) {
201         case SF_TARGET:
202             p += sprintf(p, "T");
203             break;
204         case SF_FILTER:
205             p += sprintf(p, "F");
206             break;
207         }
208         p += sprintf(p, ":%04x ", offset);
209         // Print hex values.
210         for (i = 0; i < count; i++)
211             p += sprintf(p, "%02x ", d1[i]);
212         for (     ; i < width   ; i++)
213             p += sprintf(p, ".. ");
214
215         // Print ASCII string
216         p += sprintf(p, "'");
217         for (i = 0; i < count; i++) {
218             int c = d1[i];
219             if (' ' > c || 'z' < c)
220                 c = '.';
221             p += sprintf(p, "%c", c);
222         }
223         sprintf(p, "'\n");
224
225         Trace("%s", buf);
226
227         len -= count;
228         offset += count;
229         d1 += count;
230     }
231 }
232
233 void
234 CeCosTestSerialFilter::TargetWrite(CeCosSerial &pSer, 
235                                     const unsigned char* buffer, int len)
236 {
237     unsigned int __written;
238
239     if (m_bOptFilterTrace)
240         PrintHex(buffer, len, SF_FILTER);
241
242     do {
243         if (!(pSer.Write((void*) buffer, len, __written))) {
244             fprintf(stderr, "Writing %d bytes to serial failed\n", len);
245             fprintf(stderr, "%s", (LPCTSTR)pSer.ErrString());
246             throw "serial write failed";
247         }
248         buffer += __written;
249         len -= __written;
250     } while (len);
251
252 }
253
254
255 bool
256 CeCosTestSerialFilter::TargetRead(CeCosSerial &pSer, 
257                                   unsigned char* buffer, int len)
258 {
259     unsigned int __read;
260     int __total_read = 0;
261     unsigned char* __buffer_base = buffer;
262     int __timeouts = 0;
263     int __timeout_failure = 0;
264     int __orig_len = len;
265
266     do {
267         // First check for unread data.
268         if (m_nUnreadBufferSize) {
269             int i = 0;
270             __read = 0;
271             while (i < len && m_nUnreadBufferIndex < m_nUnreadBufferSize) {
272                 buffer[i++] = m_xUnreadBuffer[m_nUnreadBufferIndex++];
273                 __read++;
274             }
275             
276             if (m_nUnreadBufferIndex == m_nUnreadBufferSize) {
277                 free(m_xUnreadBuffer);
278                 m_nUnreadBufferSize = 0;
279                 m_nUnreadBufferIndex = 0;
280             }
281         } else { 
282             // Then read directly from serial.
283             if (!(pSer.Read((void*) buffer, len, __read))) {
284                 fprintf(stderr,"Reading %d bytes from serial failed (read %d).\n",
285                         len, __read);
286                 char *pszErr=pSer.ErrString().GetCString();
287                 fprintf(stderr, "%s", pszErr);
288                 delete [] pszErr;
289                 throw "serial read failed";
290             }
291         }
292
293         __total_read += __read;
294         unsigned int i;
295         for (i = 0; i < __read; i++) {
296             if ('$' == buffer[i]) {
297
298                 Log("FAIL:<target crashed>\n"); 
299
300                 Trace("**** Detected $ -- resuming as null filter ****\n");
301
302                 Trace("Data received %d bytes (of %d) from target:\n", 
303                       __total_read, __orig_len);
304                 PrintHex(__buffer_base, __total_read);
305                 Trace("<end>\n");
306
307                 filter_abort_t* msg = new filter_abort_t();
308                 msg->data_ptr = &buffer[i];
309                 msg->data_len = __read - i;
310
311                 throw msg;
312             }
313         }
314
315         if (0 == __read) {
316             CeCosThreadUtils::Sleep(20);
317             __timeouts++;
318             if (25 == __timeouts) {
319                 __timeouts = 0;
320                 if (5 == __timeout_failure++) {
321                     Log("FAIL:<target timed out>\n"); 
322
323                     Trace("**** Timed out while reading -- resuming as null filter\n");
324
325                     Trace("Data received %d bytes (of %d) from target:\n", 
326                           __total_read, __orig_len);
327                     PrintHex(__buffer_base, __total_read);
328                     Trace("<end>\n");
329
330                     static const char kill_msg[] = "$X00#b8";
331
332                     filter_abort_t* msg = new filter_abort_t();
333                     msg->data_len = strlen(kill_msg);
334                     msg->data_ptr = (const unsigned char *)kill_msg;
335
336                     throw msg;
337                 }
338             }
339         } else {
340             __timeouts = 0;
341             __timeout_failure = 0;
342         }
343
344         buffer += __read;
345         len -= __read;
346
347     } while (len);
348
349     return true;
350 }
351
352 // Send C ASCII string to target.
353 void 
354 CeCosTestSerialFilter::TargetASCIIWrite(CeCosSerial &pSer, const char* s) 
355
356     TargetWrite(pSer, (const unsigned char*) s, strlen(s)); 
357 }
358
359 //------------------------
360 // Configuration Command.
361 // Set serial configuration.
362 bool
363 CeCosTestSerialFilter::SetConfig(CeCosSerial &pSer, 
364                                  const ser_cfg_t* new_cfg, 
365                                  ser_cfg_t* old_cfg)
366 {
367     // Note that for flow control, we assume that *both* receive and transmit
368     // flow control are set or not set
369     if (old_cfg) {
370         old_cfg->baud_rate = pSer.GetBaud();
371         old_cfg->parity = (0 != pSer.GetParity()) ? true : false;
372         old_cfg->data_bits = pSer.GetDataBits();
373         old_cfg->stop_bits = pSer.GetStopBits();
374         old_cfg->flags = pSer.GetXONXOFFFlowControl() ? FLOW_XONXOFF_RX : 0;
375         old_cfg->flags |= pSer.GetRTSCTSFlowControl() ? FLOW_RTSCTS_RX : 0;
376         old_cfg->flags |= pSer.GetDSRDTRFlowControl() ? FLOW_DSRDTR_RX : 0;
377     }
378
379     pSer.SetBaud(new_cfg->baud_rate, false);
380     pSer.SetParity(new_cfg->parity, false);
381     pSer.SetDataBits(new_cfg->data_bits, false);
382     pSer.SetXONXOFFFlowControl((new_cfg->flags&FLOW_XONXOFF_RX) != 0, false);
383     pSer.SetRTSCTSFlowControl((new_cfg->flags&FLOW_RTSCTS_RX) != 0, false);
384     pSer.SetDSRDTRFlowControl((new_cfg->flags&FLOW_DSRDTR_RX) != 0, false);
385     return pSer.SetStopBits(new_cfg->stop_bits, true); // apply settings
386 }
387
388 // Return false if the serial configuration is not valid for the host.
389 bool
390 CeCosTestSerialFilter::VerifyConfig(CeCosSerial &pSer, ser_cfg_t* new_cfg)
391 {
392     ser_cfg_t old_cfg;
393     bool rc;
394
395     // Try changing to the new config, recording the result. Then restore
396     // the original config.
397     rc = SetConfig(pSer, new_cfg, &old_cfg);
398     SetConfig(pSer, &old_cfg, NULL);
399
400     return rc;
401 }
402
403 //-----------------------------------------------------------------------------
404 // Configuration changing function.
405 //
406 // First change to the new config and back again to determine if the driver
407 // can handle the config.
408 // If not, return error.
409 //
410 // Then query the host for its capability to use the config:
411 // Format out:
412 //  "@CONFIG:<baud rate code>:<#data bits>:<#stop bits>:<parity on/off>!"
413 // Format in:
414 //  OK/ER
415 //
416 // On ER, return error.
417 //
418 // On OK, change to the new configuration. Resynchronize with the host:
419 //  Target waits for host to send S(ync) 
420 //     [host will delay at least .1 secs after changing baud rate so the 
421 //      line has time to settle.]
422 //
423 //  When receiving S(ync), target replies OK to the host which then
424 //  acknowledges with D(one).
425 //
426 //  Host can also send R(esync) which means it didn't receieve the OK. If
427 //  so the target resends its S(ync) message.
428 //
429 // If the synchronization has not succeeded within 1 second
430 // (configurable in the protocol), both host and target will revert to
431 // the previous configuration and attempt to synchronize again. If
432 // this fails, this call will hang and the host will consider the test
433 // a failure.
434 //
435 // To Do:
436 //  Host&protocol currently only supports:
437 //   - no/even parity
438 void
439 CeCosTestSerialFilter::CMD_ChangeConfig(CeCosSerial &pSer, char* cfg_str)
440 {
441     ser_cfg_t new_cfg, old_cfg;
442
443     ParseConfig(cfg_str, &new_cfg);
444
445     // Return without changing the config if it's not valid.
446     if (!VerifyConfig(pSer, &new_cfg)) {
447         TargetASCIIWrite(pSer, "ER");
448         return;
449     }
450
451     // Tell target we're ready to go, wait 1/10 sec, and then change
452     // the config.
453     TargetASCIIWrite(pSer, "OK");
454     CeCosThreadUtils::Sleep(100);
455     SetConfig(pSer, &new_cfg, &old_cfg);
456
457     int loops;
458     for (loops = 0; loops < 3; loops++) {
459         unsigned int len, read;
460         unsigned char buffer[2];
461         int delay_mticks = 0; // millisecond-ticks. 10 of these per target tick
462
463         // Start by sending a Sync.
464         TargetASCIIWrite(pSer, "S");
465         for(;;) {
466             // Did target reply?
467             len = 2;
468             read = 0;
469             buffer[0] = 0;
470             buffer[1] = 0;
471             if (!pSer.Read((void*) buffer, len, read)) {
472                 throw "CMD_ChangeConfig: serial read failure";
473             }
474             
475             if (read) {
476                 // If only one char read, try to get the next one.
477                 if (1 == read) {
478                     unsigned int read2 = 0;
479                     len = 1;
480                     if (!pSer.Read((void*) &buffer[1], len, read2)) {
481                         throw "CMD_ChangeConfig: serial read failure";
482                     }
483                     read += read2;
484                 }
485
486                 if (m_bOptSerDebug)
487                     PrintHex(buffer, read);
488
489                 if ('O' == buffer[0] && 'K' == buffer[1]) {
490                     // success!
491                     TargetASCIIWrite(pSer, "D");
492                     Trace("Config change succeeded.\n");
493                     return;
494                 } else {
495                     // Garbage, ask target to resend its OK message.
496                     TargetASCIIWrite(pSer, "R");
497                 }
498             } else {
499                 // Resend Sync message.
500                 TargetASCIIWrite(pSer, "S");
501             }
502
503             CeCosThreadUtils::Sleep(1);
504             delay_mticks++;
505             // Timeout.
506             if (100 == delay_mticks/10)
507                 break;
508         }
509
510         SetConfig(pSer, &old_cfg, NULL);
511     }
512
513     // Abort the test.
514     Log("FAIL:<target timed out>\n"); 
515     Trace("**** Timed out while changing config\n");
516
517     static const char kill_msg[] = "$X00#b8";
518
519     filter_abort_t* msg = new filter_abort_t();
520     msg->data_len = strlen(kill_msg);
521     msg->data_ptr = (const unsigned char *)kill_msg;
522
523     throw msg;
524 }
525
526 // Set default configuration.
527 void
528 CeCosTestSerialFilter::CMD_DefaultConfig(CeCosSerial &pSer)
529 {
530     static const ser_cfg_t default_ser_cfg = { 9600, 
531                                                8, 
532                                                CeCosSerial::ONE_STOP_BIT,
533                                                false };
534
535     TargetASCIIWrite(pSer, "OK");
536     SetConfig(pSer, &default_ser_cfg, NULL);
537 }
538
539 // Parse config string from target and set new_cfg accordingly.
540 // String from target is:
541 //  <baud rate>:<data bits>:<stop bits>:<parity>:....
542 void
543 CeCosTestSerialFilter::ParseConfig(char* args, ser_cfg_t* new_cfg)
544 {
545     int ecos_parity, ecos_stop_bits, ecos_baud_rate, ecos_flags;
546
547     CeCosSerial::StopBitsType t2h_stop_bits[3] = {
548         CeCosSerial::ONE_STOP_BIT, 
549         CeCosSerial::ONE_POINT_FIVE_STOP_BITS,
550         CeCosSerial::TWO_STOP_BITS};
551     INIT_VALUE(args);
552
553     SET_VALUE(int, ecos_baud_rate);
554     SET_VALUE(int, new_cfg->data_bits);
555     SET_VALUE(int, ecos_stop_bits);
556     SET_VALUE(int, ecos_parity);
557     SET_VALUE(int, ecos_flags);
558
559     new_cfg->parity = (ecos_parity != 0) ? true : false;
560     new_cfg->stop_bits = t2h_stop_bits[ecos_stop_bits - 1];
561
562     // flags is an optional field
563     if ( -1 == ecos_flags )
564         new_cfg->flags = FLOW_NONE;
565     else
566         new_cfg->flags = ecos_flags;
567
568     // eCos->human translation of serial baud rate. This table must
569     // match the one in io/serial/current/include/serialio.h
570     static const int tt_baud_rate[] = {
571         -1,                                 // 0 invalid
572         50,                                 // 1 50
573         75,                                 // 2 75
574         110,                                // 3
575         135,                                // 4 134_5
576         150,                                // 5
577         200,                                // 6 200
578         300,                                // 7
579         600,                                // 8
580         1200,                               // 9
581         1800,                               // 10 1800
582         2400,                               // 11
583         3600,                               // 12 3600
584         4800,                               // 13
585         7200,                               // 14 7200
586         9600,                               // 15
587         14400,                              // 16 14400
588         19200,                              // 17
589         38400,                              // 18
590         57600,                              // 19
591         115200,                             // 20
592         234000                              // 21 234000
593     };
594
595     if (ecos_baud_rate > 0 && ecos_baud_rate < (int) sizeof(tt_baud_rate))
596         ecos_baud_rate = tt_baud_rate[ecos_baud_rate];
597     else
598         ecos_baud_rate = -2;
599
600     new_cfg->baud_rate = ecos_baud_rate;
601
602     Trace("Parsed Config baud=%d, bParity=%d, stopbits=%d, databits=%d\n",
603           new_cfg->baud_rate, (int) new_cfg->parity, new_cfg->stop_bits,
604           new_cfg->data_bits);
605     Trace("Parsed Config xonxoff_rx=%d,tx=%d, rtscts_rx=%d,tx=%d, "
606           "dsrdtr_rx=%d,tx=%d\n",
607           (new_cfg->flags & FLOW_XONXOFF_RX) != 0,
608           (new_cfg->flags & FLOW_XONXOFF_TX) != 0,
609           (new_cfg->flags & FLOW_RTSCTS_RX) != 0,
610           (new_cfg->flags & FLOW_RTSCTS_TX) != 0,
611           (new_cfg->flags & FLOW_DSRDTR_RX) != 0,
612           (new_cfg->flags & FLOW_DSRDTR_TX) != 0);
613 }
614
615 // Always make sure CRC fits in 31 bits. Bit of a hack, but we want
616 // to send CRC as ASCII without too much hassle.
617 int
618 CeCosTestSerialFilter::DoCRC(unsigned char* data, int size)
619 {
620     int i;
621     unsigned long crc;
622
623     for (i = 0, crc = 0; i < size; i++) {
624         crc = (crc << 1) ^ data[i];     // FIXME: standard definition?
625     }
626
627     i = (int) crc;
628     if (i < 0)
629         i = -i;
630
631     return i;
632 }
633
634 void
635 CeCosTestSerialFilter::SendChecksum(CeCosSerial &pSer, int crc)
636 {
637     char buffer[128];
638     int len;
639
640     len = sprintf(buffer, "%d!", crc);
641
642     TargetWrite(pSer, (const unsigned char*)buffer, len);
643 }
644
645 void
646 CeCosTestSerialFilter::SendStatus(CeCosSerial &pSer, int state)
647 {
648     if (state)
649         TargetWrite(pSer, (unsigned char*) &msg_ok, 2);
650     else
651         TargetWrite(pSer, (unsigned char*) &msg_er, 2);
652 }
653
654
655 // Receive test DONE message from target.
656 void
657 CeCosTestSerialFilter::ReceiveDone(CeCosSerial &pSer, 
658                                    unsigned char* data_in, int size)
659 {
660     static const char msg_done[] = "DONE";
661     unsigned char data_reply[4];
662     int first = 1;
663
664     TargetRead(pSer, data_reply, 4);
665     while (0 != strncmp((char*) data_reply, msg_done, 4)) {
666         if (first) {
667             if (data_in && size) {
668                 Trace("Data received from target:\n");
669                 PrintHex(data_in, size);
670                 Trace("<end>\n");
671             }
672             Trace("Receiving junk instead of DONE:\n");
673             first = 0;
674         }
675         PrintHex(data_reply, 4);
676
677         data_reply[0] = data_reply[1];
678         data_reply[1] = data_reply[2];
679         data_reply[2] = data_reply[3];
680
681         // The TargetRead call will handle recovery in case of timeout...
682         TargetRead(pSer, &data_reply[3], 1);
683     }
684 }
685
686 //-----------------------------------------------------------------------------
687 // Test binary data transmission.
688 // Format in:
689 //  <byte size>:<mode>
690 // Format out:
691 //  <4 bytes binary checksum><#size bytes data>
692 // If echo mode, also:
693 //    Format in:
694 //     <#size bytes data>
695 //    Format out:
696 //     OK/ER - according to CRC match on incomin data
697 // Format in:
698 //  DONE
699 //
700 // To Do:
701 //  o Add mode/flag specifying 5-8 bit transfer.
702 //     Test that 0xff gets masked off accordingly when transfered.
703 //     (This should be an INFO result if failing)
704 //  o Clean up the DUPLEX_ECHO implementation. Currently it's an ugly hack
705 //    that doesn't match the arguments / behavior of the two other modes.
706 void
707 CeCosTestSerialFilter::CMD_TestBinary(CeCosSerial &pSer, char* args)
708 {
709     int size;
710     cyg_mode_t mode;
711     unsigned char *data_out, *data_in;
712     int i;
713     int crc;
714
715     int loop_count = 0;
716
717     INIT_VALUE(args);
718
719     SET_VALUE(int, size);
720     SET_VALUE(cyg_mode_t, mode);
721
722     // Change behavior for DUPLEX mode.
723     if (MODE_DUPLEX_ECHO == mode) {
724         loop_count = size;
725         size = 1024;                    // must be at least 4*block_size
726     }
727
728     // Generate data.
729     data_out = (unsigned char*) malloc(size);
730     if (!data_out) {
731         fprintf(stderr, "Could not allocate %d byte buffer for data!\n", size);
732         throw "data_out malloc failed";
733     }
734     data_in = (unsigned char*) malloc(size);
735     if (!data_in) {
736         fprintf(stderr, "Could not allocate %d byte buffer for data!\n", size);
737         throw "data_in malloc failed";
738     }
739     int count = 0;
740     for (i = 0; i < size; i++) {
741         // Output 255 chars, not 256 so that we aren't a multiple/factor of the
742         // likely buffer sizes in the system, this can mask problems as I've
743         // found to my cost!
744         unsigned char c = (unsigned char) (count++ % 255);
745         // don't allow $s and @s in the data, nor 0x03 (GDB C-c), nor flow
746         // control chars
747         if ('$' == c || '@' == c || 0x03 == c || 0x11 == c || 0x13 == c)
748             c = (unsigned char) '*';
749         data_out[i] = c;
750     }
751
752     // Do checksum.
753     crc = DoCRC(data_out, size);
754
755     // Send checksum to target.
756     SendChecksum(pSer, crc);
757
758     // Give the target 1/10th of a sec to digest it
759     CeCosThreadUtils::Sleep(100);
760
761     switch (mode) {
762     case MODE_NO_ECHO:
763     {
764         // Simple transmit. Don't expect target to echo data back.
765         TargetWrite(pSer, data_out, size);
766         ReceiveDone(pSer, NULL, 0);
767     }
768     break;
769     case MODE_EOP_ECHO:
770     {
771         int in_crc;
772
773         TargetWrite(pSer, data_out, size);
774         Trace("Finished write, waiting for target echo.\n");
775
776         // Expect target to echo the data
777         TargetRead(pSer, data_in, size);
778
779         // Check echoed data, and reply OK/ER accordingly.
780         in_crc = DoCRC(data_in, size);
781         SendStatus(pSer, (in_crc == crc));
782
783
784         // Dump seen/expected on console.
785         if (in_crc != crc) {
786             Trace("Data seen:\n");
787             PrintHex(data_in, size);
788             Trace("<end>\n");
789             Trace("Data expected:\n");
790             PrintHex(data_out, size);
791             Trace("<end>\n");
792         }
793
794         ReceiveDone(pSer, data_in, size);
795
796     }
797     break;
798     case MODE_DUPLEX_ECHO:
799     {
800         int block_size = 64;
801         int fail, j;
802
803         // This is a simple implementation (maybe too simple).
804         // Host sends 4 packets with the same size (64 bytes atm).
805         // Target echoes in this way:
806         //  packet1 -> packet1
807         //  packet2 -> packet2, packet2
808         //  packet3 -> packet3
809         //  packet4 -> /dev/null
810         //
811         // The reads/writes are interleaved in a way that should ensure
812         // the target out buffer to be full before the target starts to read
813         // packet3. That is, the target should be both receiving (packet3)
814         // and sending (packet2) at the same time.
815
816         // This code needs restructuring. It's not very obvious what's
817         // happening: The same block of data is output several times,
818         // the target echoes the data back (one of the blocks is
819         // echoed twice). Then the echoed data is compared agains the
820         // outgoing data block.
821
822         fail = 0;
823         while (loop_count--) {
824             int i;
825             for (i = 0; i < block_size*4; i++)
826                 data_in[i] = 0;
827
828             // out1: block_size -> block_size
829             TargetWrite(pSer, data_out, block_size);
830
831             // out2: block_size -> 2 x block_size
832             TargetWrite(pSer, data_out, block_size);
833
834             // in1:
835             TargetRead(pSer, data_in, block_size);
836
837             // out3: block_size -> block_size
838             TargetWrite(pSer, data_out, block_size);
839         
840             // in2:
841             TargetRead(pSer, &data_in[block_size], 2*block_size);
842
843             // out4: block_size -> 0
844             TargetWrite(pSer, data_out, block_size);
845         
846             // in3:
847             TargetRead(pSer, &data_in[block_size*3], block_size);
848
849             if (0 == loop_count % 10)
850                 Trace("%d loops to go\n", loop_count);
851
852             // Verify data.
853             if (!fail) {
854                 for (j = 0; j < 4 && !fail; j++) {
855                     for (i = 0; i < block_size && !fail; i++) {
856                         if (data_out[i] != data_in[j*block_size + i]) {
857                             fail = 1;
858                             Trace("Failed at byte %d\n", j*block_size + i);
859                             
860                             Trace("Data seen:\n");
861                             PrintHex(&data_in[j*block_size], 
862                                            block_size);
863                             Trace("<end>\n");
864                             Trace("Data expected:\n");
865                             PrintHex(data_out, block_size);
866                             Trace("<end>\n");
867                         }
868                     }
869                 }
870             }
871         }
872         // Check echoed data, and reply OK/ER accordingly.
873         SendStatus(pSer, (!fail));
874         ReceiveDone(pSer, data_in, block_size*4);
875     }
876     break;
877     default:
878         Trace("Unknown mode. Ignoring.\n");
879     }
880
881     // Free buffer.
882     free(data_in);
883     free(data_out);
884 }
885
886 //-----------------------------------------------------------------------------
887 // Test transformations on text transmissions
888 //
889 // This test transmits null-terminated C strings back and forth. Since
890 // the translation is under test and may fail, the length of the data is
891 // (potentially) unknown. Sending with a null-terminator allows proper
892 // recovery even if the translations do not work as intended.
893 //
894 // Format in:
895 //  <flags>!<4 bytes binary checksum><C string>
896 // Format out:
897 //  <C string>
898 //  OK/ER
899 //
900 // Mode:
901 //   MODE_EOP_ECHO:
902 //       Receive data, verify CRC, resend data.
903 //       Send OK/ER reply when done.
904 //   MODE_DUPLEX_ECHO:
905 //       Receive data, echo data, verify CRC.
906 //       Send OK/ER reply when done.
907 //
908 // To Do:
909 //  Implement.
910 void
911 CeCosTestSerialFilter::CMD_TestText(CeCosSerial &pSer, char* /*args*/)
912 {
913     SendStatus(pSer, 1);
914 }
915
916 //-----------------------------------------------------------------------------
917 // Reply to PING packet from target.
918 // Format in:
919 //  "!"
920 // Format out:
921 //  OK
922 void
923 CeCosTestSerialFilter::CMD_TestPing(CeCosSerial &pSer, char* /*args*/)
924
925     SendStatus(pSer, 1);
926 }
927
928 //-----------------------------------------------------------------------------
929 // Dispatch test command. 
930 void
931 CeCosTestSerialFilter::DispatchCommand(CeCosSerial &pSer, char* cmd)
932 {
933     char* args;
934
935     args = strchr(cmd, (int) ':');
936     if (!args) {
937         Trace("Bogus command (%s) Ignoring.\n", cmd);
938         return;
939     }
940         
941     *args++ = 0;
942
943     Trace("Dispatching command %s.\n", cmd);
944
945     if (0 == strcmp("CONFIG", cmd)) {
946         CMD_ChangeConfig(pSer, args);
947     } 
948     else if (0 == strcmp("DEFCONFIG", cmd)) {
949         // Note: Currently the arguments are ignored. 9600 8N1 is default.
950         CMD_DefaultConfig(pSer);
951     }
952     else if (0 == strcmp("BINARY", cmd)) {
953         CMD_TestBinary(pSer, args);
954     }
955     else if (0 == strcmp("TEXT", cmd)) {
956         CMD_TestText(pSer, args);
957     }
958     else if (0 == strcmp("PING", cmd)) {
959         CMD_TestPing(pSer, args);
960     }
961     else
962         Trace("Unknown command '%s'.\n", cmd);
963
964     Trace("Command %s completed.\n", cmd);
965 }
966
967 bool CALLBACK
968 SerialFilterFunction(void*& pBuf,
969                      unsigned int& nRead,
970                      CeCosSerial& serial,
971                      CeCosSocket& socket,
972                      void* pParem)
973 {
974     CeCosTestSerialFilter* p = (CeCosTestSerialFilter*) pParem;
975     return p->FilterFunctionProper(pBuf, nRead, serial, socket);
976 }
977
978 bool
979 CeCosTestSerialFilter::FilterFunctionProper(void*& pBuf,
980                                             unsigned int& nRead,
981                                             CeCosSerial& serial,
982                                             CeCosSocket& socket)
983 {
984     char* buffer = (char*) pBuf;
985
986     // Don't do anything in the null filter mode.
987     if (m_bNullFilter)
988         return true;
989
990     // Allows trace to be called without a reference to the socket...
991     m_cGDBSocket = &socket;
992
993     // Put in trace buffer in case we have to leave it because the packet
994     // is incomplete
995     m_xStoredTraceBuffer = (unsigned char *)
996         realloc( m_xStoredTraceBuffer, m_nStoredTraceBufferSize + nRead );
997     if ( NULL == m_xStoredTraceBuffer ) 
998         throw "Could not allocate stored trace buffer";
999     memcpy( m_xStoredTraceBuffer + m_nStoredTraceBufferSize, buffer, nRead );
1000     m_nStoredTraceBufferSize += nRead;
1001
1002     // Now search for distinct packets, delimited by '@' (filter commands)
1003     // and '$' (GDB packets)
1004     unsigned int i, newStart=0;
1005     for (i=0; i<m_nStoredTraceBufferSize; i++) {
1006         if ( m_xStoredTraceBuffer[i] == '@' ||
1007              m_xStoredTraceBuffer[i] == '$' ) {
1008             if (m_bOptSerDebug &&
1009                 (m_bOptConsoleOutput || m_bFirstCommandSeen)) {
1010
1011                 // Output the serial data if option enabled - but only if
1012                 // dumping state to the console or after the first command
1013                 // has been seen from the filter. GDB gets confused by
1014                 // O-packets if they appear when it's trying to connect.
1015
1016                 PrintHex(&m_xStoredTraceBuffer[newStart], i - newStart);
1017             }
1018             newStart = i;
1019         }
1020     }
1021
1022     // If we managed to print output, rejig the buffer size, and shunt
1023     // the new start of the data to the front of the trace buffer
1024     m_nStoredTraceBufferSize -= newStart;
1025     
1026     memmove( m_xStoredTraceBuffer, &m_xStoredTraceBuffer[newStart],
1027              m_nStoredTraceBufferSize );
1028         
1029
1030     // Command handling.
1031     // If we are not presently reading a command, look for the
1032     // start marker.
1033     i = 0;
1034     if (!m_bCmdFlag)
1035         for (; i < nRead; i++) {
1036             if ('@' == buffer[i]) {
1037                 m_bCmdFlag = true;
1038                 // Send the data before the marker.
1039                 if (i)
1040                     socket.send(buffer, i);
1041                 break;
1042             }
1043         }
1044
1045     // If reading a command, look for the end marker.
1046     if (m_bCmdFlag) {
1047         char c = 0;
1048         while (i < nRead && m_nCmdIndex < MAX_CMD_LEN) {
1049             c = buffer[i++];
1050             m_aCmd[m_nCmdIndex++] = c;
1051             if ('!' == c) {
1052                 if (i != nRead) {
1053                     m_nUnreadBufferIndex = 0;
1054                     m_nUnreadBufferSize = nRead - i;
1055                     m_xUnreadBuffer = 
1056                         (unsigned char*) malloc(m_nUnreadBufferSize);
1057                     if (!m_xUnreadBuffer) {
1058                         m_nUnreadBufferSize = 0;
1059                         throw "Could not allocate unread buffer!";
1060                     }
1061                     
1062                     int ix = 0;
1063                     while (i < nRead)
1064                         m_xUnreadBuffer[ix++] = buffer[i++];
1065                 }
1066                 break;
1067             }
1068         }
1069
1070         if (MAX_CMD_LEN == m_nCmdIndex) {
1071             Trace("Received too long command. Ignoring it!\n");
1072             m_nCmdIndex = 0;
1073             m_bCmdFlag = false;
1074         } else if ('!' == c) {
1075             // Was the command completed?
1076             m_aCmd[m_nCmdIndex - 1] = 0;// terminate cmd
1077             m_nCmdIndex = 0;
1078             m_bCmdFlag = false;
1079
1080             // First command dispatched. Initialize serial to nonblocking.
1081             if (!m_bFirstCommandSeen) {
1082                 m_bFirstCommandSeen = true;
1083                 serial.SetBlockingReads(false);
1084             }
1085   
1086             try {
1087                 // skip @ when passing ptr
1088                 DispatchCommand(serial, &m_aCmd[1]);
1089             } 
1090             catch (filter_abort_t* msg) {
1091                 // This allows the filter to unwind, wherever in the
1092                 // protocol it may be, when a $ is detected from the
1093                 // target side.  When this happens, we may have a
1094                 // trap/exception on the target and we want the user
1095                 // to access the target via GDB without intervention.
1096
1097                 // Do nothing from next call.
1098                 m_bNullFilter = true;
1099
1100                 // Copy the start of the $-packet to the inbuffer.
1101                 unsigned char *d = (unsigned char*) pBuf;
1102                 const unsigned char *s = msg->data_ptr;
1103                 unsigned int len = msg->data_len;
1104                 
1105                 // It should be possible to re-allocate buffer. Didn't seem
1106                 // to work properly though. Probably won't be a problem
1107                 // since we would normally only see 1-2 bytes of the
1108                 // $-packet anyway.
1109                 if (len > nRead)
1110                     throw "Not enough room for $-message";
1111
1112                 while (len--)
1113                     *d++ = *s++;
1114
1115                 nRead = msg->data_len;
1116
1117                 delete msg;
1118
1119                 return true;
1120             }
1121         }
1122
1123         nRead = 0;                      // Never leave anything for caller
1124                                         // This is a violation of the intended
1125                                         // filter function behavior.
1126     }
1127     return true;
1128 }