--- /dev/null
+//####COPYRIGHTBEGIN####
+//
+// ----------------------------------------------------------------------------
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
+//
+// This program is part of the eCos host tools.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// ----------------------------------------------------------------------------
+//
+//####COPYRIGHTEND####
+//=================================================================
+//
+// eCosTestSerialFilter.cpp
+//
+// Serial test filter class
+//
+//=================================================================
+//=================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s): jskov
+// Contributors: jskov
+// Date: 1999-03-01
+// Description: This filter sits between GDB and the test running on
+// the target, allowing testing of the serial driver
+// without confusing GDB.
+// To Do:
+// o Add timeout setup and handling for recovery, can rely on testing
+// agent to control global timeout.
+// o Saving chunks that caused transfer failure?
+// - In SEND with echo, do CRC on 32-byte sub-packets
+// o Additional To Do items under each sub-protocol function.
+// o Option to get all serial IO written (in hex, > and < prepends
+// input/output lines) to a file.
+// o Clean up the mess in this file....
+//####DESCRIPTIONEND####
+
+#include "eCosStd.h"
+
+#include "eCosTestSerialFilter.h"
+#include "eCosThreadUtils.h"
+
+char msg_ok[] = "OK";
+char msg_er[] = "ER";
+
+
+
+CeCosTestSerialFilter::CeCosTestSerialFilter():
+ m_bOptConsoleOutput(false),
+ m_bOptSerDebug(false),
+ m_bOptFilterTrace(false),
+ m_xUnreadBuffer(NULL),
+ m_nUnreadBufferIndex(0),
+ m_nUnreadBufferSize(0),
+ m_xStoredTraceBuffer(NULL),
+ m_nStoredTraceBufferSize(0),
+ m_bNullFilter(false),
+ m_nCmdIndex(0),
+ m_bCmdFlag(false),
+ m_bFirstCommandSeen(false),
+ m_cGDBSocket(NULL)
+{
+}
+
+CeCosTestSerialFilter::~CeCosTestSerialFilter()
+{
+}
+
+//------------------------
+// Output helpers.
+
+// Encode string in an O-packet and send it to GDB.
+void
+CeCosTestSerialFilter::GDBWrite(const char* pszStr)
+{
+ if (m_cGDBSocket) {
+ static const char hexchars[] = "0123456789abcdef";
+ char* packet = new char[strlen(pszStr)*2+6];
+ char* p = packet;
+
+ *p++ = '$';
+ *p++ = 'O';
+ unsigned char crc = 'O';
+ char c;
+ for (;;) {
+ c = *pszStr++;
+ if (0 == c)
+ break;
+
+ char h = hexchars[(c >> 4) & 0x0f];
+ char l = hexchars[c & 0x0f];
+ *p++ = h;
+ *p++ = l;
+ crc = (unsigned char) (crc + h + l);
+ };
+
+ *p++ = '#';
+ *p++ = hexchars[(crc >> 4) & 0x0f];
+ *p++ = hexchars[crc & 0x0f];
+
+ // Only try to send once. If it fails, it's probably because
+ // GDB has disconnected.
+ m_cGDBSocket->send(packet, p - packet);
+ m_cGDBSocket->recv(&c, 1);
+
+ delete [] packet;
+ }
+}
+
+void
+CeCosTestSerialFilter::ConsoleWrite(const char* pszStr)
+{
+ fputs(pszStr, stderr);
+ fflush(stderr);
+}
+
+void
+CeCosTestSerialFilter::Trace(const char* pszFormat, ...)
+{
+
+ va_list marker;
+ va_start (marker, pszFormat);
+
+ for(int nLength=100;nLength;) {
+ char *buf=new char[1+nLength];
+ int n=vsnprintf(buf+4, nLength-4, pszFormat, marker );
+ if(-1==n){
+ nLength*=2; // NT behavior
+ } else if (n<nLength){
+ memcpy(buf,"[f] ",4);
+ if (m_bOptConsoleOutput) {
+ ConsoleWrite(buf);
+ } else {
+ GDBWrite(buf);
+ }
+ nLength=0; // trigger exit from loop
+ } else {
+ nLength=n+1; // UNIX behavior generally, or NT behavior when buffer size exactly matches required length
+ }
+ delete [] buf;
+ }
+
+ va_end (marker);
+
+}
+
+void
+CeCosTestSerialFilter::Log(const char* pszFormat, ...)
+{
+ va_list marker;
+ va_start (marker, pszFormat);
+
+ for(int nLength=100;nLength;) {
+ char *buf=new char[1+nLength];
+ int n=vsnprintf(buf, nLength, pszFormat, marker );
+ if(-1==n){
+ nLength*=2; // NT behavior
+ } else if (n<nLength){
+ if (m_bOptConsoleOutput) {
+ ConsoleWrite(buf);
+ } else {
+ GDBWrite(buf);
+ }
+ nLength=0; // trigger exit from loop
+ } else {
+ nLength=n+1; // UNIX behavior generally, or NT behavior when buffer size exactly matches required length
+ }
+ delete [] buf;
+ }
+
+ va_end (marker);
+}
+
+
+void
+CeCosTestSerialFilter::PrintHex(const unsigned char* d1, int len, data_origin_t origin/*=SF_TARGET*/)
+{
+ int offset = 0;
+ int i;
+ char buf[128];
+ int width = 8;
+
+ while (len) {
+ int count = MIN(width, len);
+ char* p = buf;
+ switch (origin) {
+ case SF_TARGET:
+ p += sprintf(p, "T");
+ break;
+ case SF_FILTER:
+ p += sprintf(p, "F");
+ break;
+ }
+ p += sprintf(p, ":%04x ", offset);
+ // Print hex values.
+ for (i = 0; i < count; i++)
+ p += sprintf(p, "%02x ", d1[i]);
+ for ( ; i < width ; i++)
+ p += sprintf(p, ".. ");
+
+ // Print ASCII string
+ p += sprintf(p, "'");
+ for (i = 0; i < count; i++) {
+ int c = d1[i];
+ if (' ' > c || 'z' < c)
+ c = '.';
+ p += sprintf(p, "%c", c);
+ }
+ sprintf(p, "'\n");
+
+ Trace("%s", buf);
+
+ len -= count;
+ offset += count;
+ d1 += count;
+ }
+}
+
+void
+CeCosTestSerialFilter::TargetWrite(CeCosSerial &pSer,
+ const unsigned char* buffer, int len)
+{
+ unsigned int __written;
+
+ if (m_bOptFilterTrace)
+ PrintHex(buffer, len, SF_FILTER);
+
+ do {
+ if (!(pSer.Write((void*) buffer, len, __written))) {
+ fprintf(stderr, "Writing %d bytes to serial failed\n", len);
+ fprintf(stderr, "%s", (LPCTSTR)pSer.ErrString());
+ throw "serial write failed";
+ }
+ buffer += __written;
+ len -= __written;
+ } while (len);
+
+}
+
+
+bool
+CeCosTestSerialFilter::TargetRead(CeCosSerial &pSer,
+ unsigned char* buffer, int len)
+{
+ unsigned int __read;
+ int __total_read = 0;
+ unsigned char* __buffer_base = buffer;
+ int __timeouts = 0;
+ int __timeout_failure = 0;
+ int __orig_len = len;
+
+ do {
+ // First check for unread data.
+ if (m_nUnreadBufferSize) {
+ int i = 0;
+ __read = 0;
+ while (i < len && m_nUnreadBufferIndex < m_nUnreadBufferSize) {
+ buffer[i++] = m_xUnreadBuffer[m_nUnreadBufferIndex++];
+ __read++;
+ }
+
+ if (m_nUnreadBufferIndex == m_nUnreadBufferSize) {
+ free(m_xUnreadBuffer);
+ m_nUnreadBufferSize = 0;
+ m_nUnreadBufferIndex = 0;
+ }
+ } else {
+ // Then read directly from serial.
+ if (!(pSer.Read((void*) buffer, len, __read))) {
+ fprintf(stderr,"Reading %d bytes from serial failed (read %d).\n",
+ len, __read);
+ char *pszErr=pSer.ErrString().GetCString();
+ fprintf(stderr, "%s", pszErr);
+ delete [] pszErr;
+ throw "serial read failed";
+ }
+ }
+
+ __total_read += __read;
+ unsigned int i;
+ for (i = 0; i < __read; i++) {
+ if ('$' == buffer[i]) {
+
+ Log("FAIL:<target crashed>\n");
+
+ Trace("**** Detected $ -- resuming as null filter ****\n");
+
+ Trace("Data received %d bytes (of %d) from target:\n",
+ __total_read, __orig_len);
+ PrintHex(__buffer_base, __total_read);
+ Trace("<end>\n");
+
+ filter_abort_t* msg = new filter_abort_t();
+ msg->data_ptr = &buffer[i];
+ msg->data_len = __read - i;
+
+ throw msg;
+ }
+ }
+
+ if (0 == __read) {
+ CeCosThreadUtils::Sleep(20);
+ __timeouts++;
+ if (25 == __timeouts) {
+ __timeouts = 0;
+ if (5 == __timeout_failure++) {
+ Log("FAIL:<target timed out>\n");
+
+ Trace("**** Timed out while reading -- resuming as null filter\n");
+
+ Trace("Data received %d bytes (of %d) from target:\n",
+ __total_read, __orig_len);
+ PrintHex(__buffer_base, __total_read);
+ Trace("<end>\n");
+
+ static const char kill_msg[] = "$X00#b8";
+
+ filter_abort_t* msg = new filter_abort_t();
+ msg->data_len = strlen(kill_msg);
+ msg->data_ptr = (const unsigned char *)kill_msg;
+
+ throw msg;
+ }
+ }
+ } else {
+ __timeouts = 0;
+ __timeout_failure = 0;
+ }
+
+ buffer += __read;
+ len -= __read;
+
+ } while (len);
+
+ return true;
+}
+
+// Send C ASCII string to target.
+void
+CeCosTestSerialFilter::TargetASCIIWrite(CeCosSerial &pSer, const char* s)
+{
+ TargetWrite(pSer, (const unsigned char*) s, strlen(s));
+}
+
+//------------------------
+// Configuration Command.
+// Set serial configuration.
+bool
+CeCosTestSerialFilter::SetConfig(CeCosSerial &pSer,
+ const ser_cfg_t* new_cfg,
+ ser_cfg_t* old_cfg)
+{
+ // Note that for flow control, we assume that *both* receive and transmit
+ // flow control are set or not set
+ if (old_cfg) {
+ old_cfg->baud_rate = pSer.GetBaud();
+ old_cfg->parity = (0 != pSer.GetParity()) ? true : false;
+ old_cfg->data_bits = pSer.GetDataBits();
+ old_cfg->stop_bits = pSer.GetStopBits();
+ old_cfg->flags = pSer.GetXONXOFFFlowControl() ? FLOW_XONXOFF_RX : 0;
+ old_cfg->flags |= pSer.GetRTSCTSFlowControl() ? FLOW_RTSCTS_RX : 0;
+ old_cfg->flags |= pSer.GetDSRDTRFlowControl() ? FLOW_DSRDTR_RX : 0;
+ }
+
+ pSer.SetBaud(new_cfg->baud_rate, false);
+ pSer.SetParity(new_cfg->parity, false);
+ pSer.SetDataBits(new_cfg->data_bits, false);
+ pSer.SetXONXOFFFlowControl((new_cfg->flags&FLOW_XONXOFF_RX) != 0, false);
+ pSer.SetRTSCTSFlowControl((new_cfg->flags&FLOW_RTSCTS_RX) != 0, false);
+ pSer.SetDSRDTRFlowControl((new_cfg->flags&FLOW_DSRDTR_RX) != 0, false);
+ return pSer.SetStopBits(new_cfg->stop_bits, true); // apply settings
+}
+
+// Return false if the serial configuration is not valid for the host.
+bool
+CeCosTestSerialFilter::VerifyConfig(CeCosSerial &pSer, ser_cfg_t* new_cfg)
+{
+ ser_cfg_t old_cfg;
+ bool rc;
+
+ // Try changing to the new config, recording the result. Then restore
+ // the original config.
+ rc = SetConfig(pSer, new_cfg, &old_cfg);
+ SetConfig(pSer, &old_cfg, NULL);
+
+ return rc;
+}
+
+//-----------------------------------------------------------------------------
+// Configuration changing function.
+//
+// First change to the new config and back again to determine if the driver
+// can handle the config.
+// If not, return error.
+//
+// Then query the host for its capability to use the config:
+// Format out:
+// "@CONFIG:<baud rate code>:<#data bits>:<#stop bits>:<parity on/off>!"
+// Format in:
+// OK/ER
+//
+// On ER, return error.
+//
+// On OK, change to the new configuration. Resynchronize with the host:
+// Target waits for host to send S(ync)
+// [host will delay at least .1 secs after changing baud rate so the
+// line has time to settle.]
+//
+// When receiving S(ync), target replies OK to the host which then
+// acknowledges with D(one).
+//
+// Host can also send R(esync) which means it didn't receieve the OK. If
+// so the target resends its S(ync) message.
+//
+// If the synchronization has not succeeded within 1 second
+// (configurable in the protocol), both host and target will revert to
+// the previous configuration and attempt to synchronize again. If
+// this fails, this call will hang and the host will consider the test
+// a failure.
+//
+// To Do:
+// Host&protocol currently only supports:
+// - no/even parity
+void
+CeCosTestSerialFilter::CMD_ChangeConfig(CeCosSerial &pSer, char* cfg_str)
+{
+ ser_cfg_t new_cfg, old_cfg;
+
+ ParseConfig(cfg_str, &new_cfg);
+
+ // Return without changing the config if it's not valid.
+ if (!VerifyConfig(pSer, &new_cfg)) {
+ TargetASCIIWrite(pSer, "ER");
+ return;
+ }
+
+ // Tell target we're ready to go, wait 1/10 sec, and then change
+ // the config.
+ TargetASCIIWrite(pSer, "OK");
+ CeCosThreadUtils::Sleep(100);
+ SetConfig(pSer, &new_cfg, &old_cfg);
+
+ int loops;
+ for (loops = 0; loops < 3; loops++) {
+ unsigned int len, read;
+ unsigned char buffer[2];
+ int delay_mticks = 0; // millisecond-ticks. 10 of these per target tick
+
+ // Start by sending a Sync.
+ TargetASCIIWrite(pSer, "S");
+ for(;;) {
+ // Did target reply?
+ len = 2;
+ read = 0;
+ buffer[0] = 0;
+ buffer[1] = 0;
+ if (!pSer.Read((void*) buffer, len, read)) {
+ throw "CMD_ChangeConfig: serial read failure";
+ }
+
+ if (read) {
+ // If only one char read, try to get the next one.
+ if (1 == read) {
+ unsigned int read2 = 0;
+ len = 1;
+ if (!pSer.Read((void*) &buffer[1], len, read2)) {
+ throw "CMD_ChangeConfig: serial read failure";
+ }
+ read += read2;
+ }
+
+ if (m_bOptSerDebug)
+ PrintHex(buffer, read);
+
+ if ('O' == buffer[0] && 'K' == buffer[1]) {
+ // success!
+ TargetASCIIWrite(pSer, "D");
+ Trace("Config change succeeded.\n");
+ return;
+ } else {
+ // Garbage, ask target to resend its OK message.
+ TargetASCIIWrite(pSer, "R");
+ }
+ } else {
+ // Resend Sync message.
+ TargetASCIIWrite(pSer, "S");
+ }
+
+ CeCosThreadUtils::Sleep(1);
+ delay_mticks++;
+ // Timeout.
+ if (100 == delay_mticks/10)
+ break;
+ }
+
+ SetConfig(pSer, &old_cfg, NULL);
+ }
+
+ // Abort the test.
+ Log("FAIL:<target timed out>\n");
+ Trace("**** Timed out while changing config\n");
+
+ static const char kill_msg[] = "$X00#b8";
+
+ filter_abort_t* msg = new filter_abort_t();
+ msg->data_len = strlen(kill_msg);
+ msg->data_ptr = (const unsigned char *)kill_msg;
+
+ throw msg;
+}
+
+// Set default configuration.
+void
+CeCosTestSerialFilter::CMD_DefaultConfig(CeCosSerial &pSer)
+{
+ static const ser_cfg_t default_ser_cfg = { 9600,
+ 8,
+ CeCosSerial::ONE_STOP_BIT,
+ false };
+
+ TargetASCIIWrite(pSer, "OK");
+ SetConfig(pSer, &default_ser_cfg, NULL);
+}
+
+// Parse config string from target and set new_cfg accordingly.
+// String from target is:
+// <baud rate>:<data bits>:<stop bits>:<parity>:....
+void
+CeCosTestSerialFilter::ParseConfig(char* args, ser_cfg_t* new_cfg)
+{
+ int ecos_parity, ecos_stop_bits, ecos_baud_rate, ecos_flags;
+
+ CeCosSerial::StopBitsType t2h_stop_bits[3] = {
+ CeCosSerial::ONE_STOP_BIT,
+ CeCosSerial::ONE_POINT_FIVE_STOP_BITS,
+ CeCosSerial::TWO_STOP_BITS};
+ INIT_VALUE(args);
+
+ SET_VALUE(int, ecos_baud_rate);
+ SET_VALUE(int, new_cfg->data_bits);
+ SET_VALUE(int, ecos_stop_bits);
+ SET_VALUE(int, ecos_parity);
+ SET_VALUE(int, ecos_flags);
+
+ new_cfg->parity = (ecos_parity != 0) ? true : false;
+ new_cfg->stop_bits = t2h_stop_bits[ecos_stop_bits - 1];
+
+ // flags is an optional field
+ if ( -1 == ecos_flags )
+ new_cfg->flags = FLOW_NONE;
+ else
+ new_cfg->flags = ecos_flags;
+
+ // eCos->human translation of serial baud rate. This table must
+ // match the one in io/serial/current/include/serialio.h
+ static const int tt_baud_rate[] = {
+ -1, // 0 invalid
+ 50, // 1 50
+ 75, // 2 75
+ 110, // 3
+ 135, // 4 134_5
+ 150, // 5
+ 200, // 6 200
+ 300, // 7
+ 600, // 8
+ 1200, // 9
+ 1800, // 10 1800
+ 2400, // 11
+ 3600, // 12 3600
+ 4800, // 13
+ 7200, // 14 7200
+ 9600, // 15
+ 14400, // 16 14400
+ 19200, // 17
+ 38400, // 18
+ 57600, // 19
+ 115200, // 20
+ 234000 // 21 234000
+ };
+
+ if (ecos_baud_rate > 0 && ecos_baud_rate < (int) sizeof(tt_baud_rate))
+ ecos_baud_rate = tt_baud_rate[ecos_baud_rate];
+ else
+ ecos_baud_rate = -2;
+
+ new_cfg->baud_rate = ecos_baud_rate;
+
+ Trace("Parsed Config baud=%d, bParity=%d, stopbits=%d, databits=%d\n",
+ new_cfg->baud_rate, (int) new_cfg->parity, new_cfg->stop_bits,
+ new_cfg->data_bits);
+ Trace("Parsed Config xonxoff_rx=%d,tx=%d, rtscts_rx=%d,tx=%d, "
+ "dsrdtr_rx=%d,tx=%d\n",
+ (new_cfg->flags & FLOW_XONXOFF_RX) != 0,
+ (new_cfg->flags & FLOW_XONXOFF_TX) != 0,
+ (new_cfg->flags & FLOW_RTSCTS_RX) != 0,
+ (new_cfg->flags & FLOW_RTSCTS_TX) != 0,
+ (new_cfg->flags & FLOW_DSRDTR_RX) != 0,
+ (new_cfg->flags & FLOW_DSRDTR_TX) != 0);
+}
+
+// Always make sure CRC fits in 31 bits. Bit of a hack, but we want
+// to send CRC as ASCII without too much hassle.
+int
+CeCosTestSerialFilter::DoCRC(unsigned char* data, int size)
+{
+ int i;
+ unsigned long crc;
+
+ for (i = 0, crc = 0; i < size; i++) {
+ crc = (crc << 1) ^ data[i]; // FIXME: standard definition?
+ }
+
+ i = (int) crc;
+ if (i < 0)
+ i = -i;
+
+ return i;
+}
+
+void
+CeCosTestSerialFilter::SendChecksum(CeCosSerial &pSer, int crc)
+{
+ char buffer[128];
+ int len;
+
+ len = sprintf(buffer, "%d!", crc);
+
+ TargetWrite(pSer, (const unsigned char*)buffer, len);
+}
+
+void
+CeCosTestSerialFilter::SendStatus(CeCosSerial &pSer, int state)
+{
+ if (state)
+ TargetWrite(pSer, (unsigned char*) &msg_ok, 2);
+ else
+ TargetWrite(pSer, (unsigned char*) &msg_er, 2);
+}
+
+
+// Receive test DONE message from target.
+void
+CeCosTestSerialFilter::ReceiveDone(CeCosSerial &pSer,
+ unsigned char* data_in, int size)
+{
+ static const char msg_done[] = "DONE";
+ unsigned char data_reply[4];
+ int first = 1;
+
+ TargetRead(pSer, data_reply, 4);
+ while (0 != strncmp((char*) data_reply, msg_done, 4)) {
+ if (first) {
+ if (data_in && size) {
+ Trace("Data received from target:\n");
+ PrintHex(data_in, size);
+ Trace("<end>\n");
+ }
+ Trace("Receiving junk instead of DONE:\n");
+ first = 0;
+ }
+ PrintHex(data_reply, 4);
+
+ data_reply[0] = data_reply[1];
+ data_reply[1] = data_reply[2];
+ data_reply[2] = data_reply[3];
+
+ // The TargetRead call will handle recovery in case of timeout...
+ TargetRead(pSer, &data_reply[3], 1);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Test binary data transmission.
+// Format in:
+// <byte size>:<mode>
+// Format out:
+// <4 bytes binary checksum><#size bytes data>
+// If echo mode, also:
+// Format in:
+// <#size bytes data>
+// Format out:
+// OK/ER - according to CRC match on incomin data
+// Format in:
+// DONE
+//
+// To Do:
+// o Add mode/flag specifying 5-8 bit transfer.
+// Test that 0xff gets masked off accordingly when transfered.
+// (This should be an INFO result if failing)
+// o Clean up the DUPLEX_ECHO implementation. Currently it's an ugly hack
+// that doesn't match the arguments / behavior of the two other modes.
+void
+CeCosTestSerialFilter::CMD_TestBinary(CeCosSerial &pSer, char* args)
+{
+ int size;
+ cyg_mode_t mode;
+ unsigned char *data_out, *data_in;
+ int i;
+ int crc;
+
+ int loop_count = 0;
+
+ INIT_VALUE(args);
+
+ SET_VALUE(int, size);
+ SET_VALUE(cyg_mode_t, mode);
+
+ // Change behavior for DUPLEX mode.
+ if (MODE_DUPLEX_ECHO == mode) {
+ loop_count = size;
+ size = 1024; // must be at least 4*block_size
+ }
+
+ // Generate data.
+ data_out = (unsigned char*) malloc(size);
+ if (!data_out) {
+ fprintf(stderr, "Could not allocate %d byte buffer for data!\n", size);
+ throw "data_out malloc failed";
+ }
+ data_in = (unsigned char*) malloc(size);
+ if (!data_in) {
+ fprintf(stderr, "Could not allocate %d byte buffer for data!\n", size);
+ throw "data_in malloc failed";
+ }
+ int count = 0;
+ for (i = 0; i < size; i++) {
+ // Output 255 chars, not 256 so that we aren't a multiple/factor of the
+ // likely buffer sizes in the system, this can mask problems as I've
+ // found to my cost!
+ unsigned char c = (unsigned char) (count++ % 255);
+ // don't allow $s and @s in the data, nor 0x03 (GDB C-c), nor flow
+ // control chars
+ if ('$' == c || '@' == c || 0x03 == c || 0x11 == c || 0x13 == c)
+ c = (unsigned char) '*';
+ data_out[i] = c;
+ }
+
+ // Do checksum.
+ crc = DoCRC(data_out, size);
+
+ // Send checksum to target.
+ SendChecksum(pSer, crc);
+
+ // Give the target 1/10th of a sec to digest it
+ CeCosThreadUtils::Sleep(100);
+
+ switch (mode) {
+ case MODE_NO_ECHO:
+ {
+ // Simple transmit. Don't expect target to echo data back.
+ TargetWrite(pSer, data_out, size);
+ ReceiveDone(pSer, NULL, 0);
+ }
+ break;
+ case MODE_EOP_ECHO:
+ {
+ int in_crc;
+
+ TargetWrite(pSer, data_out, size);
+ Trace("Finished write, waiting for target echo.\n");
+
+ // Expect target to echo the data
+ TargetRead(pSer, data_in, size);
+
+ // Check echoed data, and reply OK/ER accordingly.
+ in_crc = DoCRC(data_in, size);
+ SendStatus(pSer, (in_crc == crc));
+
+
+ // Dump seen/expected on console.
+ if (in_crc != crc) {
+ Trace("Data seen:\n");
+ PrintHex(data_in, size);
+ Trace("<end>\n");
+ Trace("Data expected:\n");
+ PrintHex(data_out, size);
+ Trace("<end>\n");
+ }
+
+ ReceiveDone(pSer, data_in, size);
+
+ }
+ break;
+ case MODE_DUPLEX_ECHO:
+ {
+ int block_size = 64;
+ int fail, j;
+
+ // This is a simple implementation (maybe too simple).
+ // Host sends 4 packets with the same size (64 bytes atm).
+ // Target echoes in this way:
+ // packet1 -> packet1
+ // packet2 -> packet2, packet2
+ // packet3 -> packet3
+ // packet4 -> /dev/null
+ //
+ // The reads/writes are interleaved in a way that should ensure
+ // the target out buffer to be full before the target starts to read
+ // packet3. That is, the target should be both receiving (packet3)
+ // and sending (packet2) at the same time.
+
+ // This code needs restructuring. It's not very obvious what's
+ // happening: The same block of data is output several times,
+ // the target echoes the data back (one of the blocks is
+ // echoed twice). Then the echoed data is compared agains the
+ // outgoing data block.
+
+ fail = 0;
+ while (loop_count--) {
+ int i;
+ for (i = 0; i < block_size*4; i++)
+ data_in[i] = 0;
+
+ // out1: block_size -> block_size
+ TargetWrite(pSer, data_out, block_size);
+
+ // out2: block_size -> 2 x block_size
+ TargetWrite(pSer, data_out, block_size);
+
+ // in1:
+ TargetRead(pSer, data_in, block_size);
+
+ // out3: block_size -> block_size
+ TargetWrite(pSer, data_out, block_size);
+
+ // in2:
+ TargetRead(pSer, &data_in[block_size], 2*block_size);
+
+ // out4: block_size -> 0
+ TargetWrite(pSer, data_out, block_size);
+
+ // in3:
+ TargetRead(pSer, &data_in[block_size*3], block_size);
+
+ if (0 == loop_count % 10)
+ Trace("%d loops to go\n", loop_count);
+
+ // Verify data.
+ if (!fail) {
+ for (j = 0; j < 4 && !fail; j++) {
+ for (i = 0; i < block_size && !fail; i++) {
+ if (data_out[i] != data_in[j*block_size + i]) {
+ fail = 1;
+ Trace("Failed at byte %d\n", j*block_size + i);
+
+ Trace("Data seen:\n");
+ PrintHex(&data_in[j*block_size],
+ block_size);
+ Trace("<end>\n");
+ Trace("Data expected:\n");
+ PrintHex(data_out, block_size);
+ Trace("<end>\n");
+ }
+ }
+ }
+ }
+ }
+ // Check echoed data, and reply OK/ER accordingly.
+ SendStatus(pSer, (!fail));
+ ReceiveDone(pSer, data_in, block_size*4);
+ }
+ break;
+ default:
+ Trace("Unknown mode. Ignoring.\n");
+ }
+
+ // Free buffer.
+ free(data_in);
+ free(data_out);
+}
+
+//-----------------------------------------------------------------------------
+// Test transformations on text transmissions
+//
+// This test transmits null-terminated C strings back and forth. Since
+// the translation is under test and may fail, the length of the data is
+// (potentially) unknown. Sending with a null-terminator allows proper
+// recovery even if the translations do not work as intended.
+//
+// Format in:
+// <flags>!<4 bytes binary checksum><C string>
+// Format out:
+// <C string>
+// OK/ER
+//
+// Mode:
+// MODE_EOP_ECHO:
+// Receive data, verify CRC, resend data.
+// Send OK/ER reply when done.
+// MODE_DUPLEX_ECHO:
+// Receive data, echo data, verify CRC.
+// Send OK/ER reply when done.
+//
+// To Do:
+// Implement.
+void
+CeCosTestSerialFilter::CMD_TestText(CeCosSerial &pSer, char* /*args*/)
+{
+ SendStatus(pSer, 1);
+}
+
+//-----------------------------------------------------------------------------
+// Reply to PING packet from target.
+// Format in:
+// "!"
+// Format out:
+// OK
+void
+CeCosTestSerialFilter::CMD_TestPing(CeCosSerial &pSer, char* /*args*/)
+{
+ SendStatus(pSer, 1);
+}
+
+//-----------------------------------------------------------------------------
+// Dispatch test command.
+void
+CeCosTestSerialFilter::DispatchCommand(CeCosSerial &pSer, char* cmd)
+{
+ char* args;
+
+ args = strchr(cmd, (int) ':');
+ if (!args) {
+ Trace("Bogus command (%s) Ignoring.\n", cmd);
+ return;
+ }
+
+ *args++ = 0;
+
+ Trace("Dispatching command %s.\n", cmd);
+
+ if (0 == strcmp("CONFIG", cmd)) {
+ CMD_ChangeConfig(pSer, args);
+ }
+ else if (0 == strcmp("DEFCONFIG", cmd)) {
+ // Note: Currently the arguments are ignored. 9600 8N1 is default.
+ CMD_DefaultConfig(pSer);
+ }
+ else if (0 == strcmp("BINARY", cmd)) {
+ CMD_TestBinary(pSer, args);
+ }
+ else if (0 == strcmp("TEXT", cmd)) {
+ CMD_TestText(pSer, args);
+ }
+ else if (0 == strcmp("PING", cmd)) {
+ CMD_TestPing(pSer, args);
+ }
+ else
+ Trace("Unknown command '%s'.\n", cmd);
+
+ Trace("Command %s completed.\n", cmd);
+}
+
+bool CALLBACK
+SerialFilterFunction(void*& pBuf,
+ unsigned int& nRead,
+ CeCosSerial& serial,
+ CeCosSocket& socket,
+ void* pParem)
+{
+ CeCosTestSerialFilter* p = (CeCosTestSerialFilter*) pParem;
+ return p->FilterFunctionProper(pBuf, nRead, serial, socket);
+}
+
+bool
+CeCosTestSerialFilter::FilterFunctionProper(void*& pBuf,
+ unsigned int& nRead,
+ CeCosSerial& serial,
+ CeCosSocket& socket)
+{
+ char* buffer = (char*) pBuf;
+
+ // Don't do anything in the null filter mode.
+ if (m_bNullFilter)
+ return true;
+
+ // Allows trace to be called without a reference to the socket...
+ m_cGDBSocket = &socket;
+
+ // Put in trace buffer in case we have to leave it because the packet
+ // is incomplete
+ m_xStoredTraceBuffer = (unsigned char *)
+ realloc( m_xStoredTraceBuffer, m_nStoredTraceBufferSize + nRead );
+ if ( NULL == m_xStoredTraceBuffer )
+ throw "Could not allocate stored trace buffer";
+ memcpy( m_xStoredTraceBuffer + m_nStoredTraceBufferSize, buffer, nRead );
+ m_nStoredTraceBufferSize += nRead;
+
+ // Now search for distinct packets, delimited by '@' (filter commands)
+ // and '$' (GDB packets)
+ unsigned int i, newStart=0;
+ for (i=0; i<m_nStoredTraceBufferSize; i++) {
+ if ( m_xStoredTraceBuffer[i] == '@' ||
+ m_xStoredTraceBuffer[i] == '$' ) {
+ if (m_bOptSerDebug &&
+ (m_bOptConsoleOutput || m_bFirstCommandSeen)) {
+
+ // Output the serial data if option enabled - but only if
+ // dumping state to the console or after the first command
+ // has been seen from the filter. GDB gets confused by
+ // O-packets if they appear when it's trying to connect.
+
+ PrintHex(&m_xStoredTraceBuffer[newStart], i - newStart);
+ }
+ newStart = i;
+ }
+ }
+
+ // If we managed to print output, rejig the buffer size, and shunt
+ // the new start of the data to the front of the trace buffer
+ m_nStoredTraceBufferSize -= newStart;
+
+ memmove( m_xStoredTraceBuffer, &m_xStoredTraceBuffer[newStart],
+ m_nStoredTraceBufferSize );
+
+
+ // Command handling.
+ // If we are not presently reading a command, look for the
+ // start marker.
+ i = 0;
+ if (!m_bCmdFlag)
+ for (; i < nRead; i++) {
+ if ('@' == buffer[i]) {
+ m_bCmdFlag = true;
+ // Send the data before the marker.
+ if (i)
+ socket.send(buffer, i);
+ break;
+ }
+ }
+
+ // If reading a command, look for the end marker.
+ if (m_bCmdFlag) {
+ char c = 0;
+ while (i < nRead && m_nCmdIndex < MAX_CMD_LEN) {
+ c = buffer[i++];
+ m_aCmd[m_nCmdIndex++] = c;
+ if ('!' == c) {
+ if (i != nRead) {
+ m_nUnreadBufferIndex = 0;
+ m_nUnreadBufferSize = nRead - i;
+ m_xUnreadBuffer =
+ (unsigned char*) malloc(m_nUnreadBufferSize);
+ if (!m_xUnreadBuffer) {
+ m_nUnreadBufferSize = 0;
+ throw "Could not allocate unread buffer!";
+ }
+
+ int ix = 0;
+ while (i < nRead)
+ m_xUnreadBuffer[ix++] = buffer[i++];
+ }
+ break;
+ }
+ }
+
+ if (MAX_CMD_LEN == m_nCmdIndex) {
+ Trace("Received too long command. Ignoring it!\n");
+ m_nCmdIndex = 0;
+ m_bCmdFlag = false;
+ } else if ('!' == c) {
+ // Was the command completed?
+ m_aCmd[m_nCmdIndex - 1] = 0;// terminate cmd
+ m_nCmdIndex = 0;
+ m_bCmdFlag = false;
+
+ // First command dispatched. Initialize serial to nonblocking.
+ if (!m_bFirstCommandSeen) {
+ m_bFirstCommandSeen = true;
+ serial.SetBlockingReads(false);
+ }
+
+ try {
+ // skip @ when passing ptr
+ DispatchCommand(serial, &m_aCmd[1]);
+ }
+ catch (filter_abort_t* msg) {
+ // This allows the filter to unwind, wherever in the
+ // protocol it may be, when a $ is detected from the
+ // target side. When this happens, we may have a
+ // trap/exception on the target and we want the user
+ // to access the target via GDB without intervention.
+
+ // Do nothing from next call.
+ m_bNullFilter = true;
+
+ // Copy the start of the $-packet to the inbuffer.
+ unsigned char *d = (unsigned char*) pBuf;
+ const unsigned char *s = msg->data_ptr;
+ unsigned int len = msg->data_len;
+
+ // It should be possible to re-allocate buffer. Didn't seem
+ // to work properly though. Probably won't be a problem
+ // since we would normally only see 1-2 bytes of the
+ // $-packet anyway.
+ if (len > nRead)
+ throw "Not enough room for $-message";
+
+ while (len--)
+ *d++ = *s++;
+
+ nRead = msg->data_len;
+
+ delete msg;
+
+ return true;
+ }
+ }
+
+ nRead = 0; // Never leave anything for caller
+ // This is a violation of the intended
+ // filter function behavior.
+ }
+ return true;
+}