diff host/tools/ecostest/common/eCosTestDownloadFilter.cpp @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children 59d97b6ba612
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/host/tools/ecostest/common/eCosTestDownloadFilter.cpp
@@ -0,0 +1,499 @@
+//####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####
+//=================================================================
+//
+//        eCosTestDownloadFilter.cpp
+//
+//        Socket/serial download filter class
+//
+//=================================================================
+//=================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     jskov
+// Contributors:  jskov
+// Date:          1999-09-20
+
+// Description:   This filter sits between the (farm) host's socket and
+
+//                the serial connection to the target board. The
+//                filter listens for special download packets from the
+//                client using the board. The download packet allows
+//                the relative slow GDB download protocol to be restricted
+//                to the connection between the farm host and the board.
+//                This prevents slow downloads due to long round-trip times
+//                and TCP packets with only a few bytes in them.
+// To Do:
+//  o Move parts shared between eCosTestDownloadFilter and eCosTestSerialFilter
+//    into a shared class.
+//####DESCRIPTIONEND####
+
+#include "eCosStd.h"
+#include "eCosTrace.h"
+
+#define DL_FILTER_VER "$Id: eCosTestDownloadFilter.cpp,v 1.1 2000/03/28 14:10:57 jlarmour Exp $"
+#include "eCosTestDownloadFilter.h"
+
+CeCosTestDownloadFilter::CeCosTestDownloadFilter():
+    m_bNullFilter(false), m_bOptSerDebug(false), m_bOptFilterTrace(false),
+    m_nCmdIndex(0), m_bCmdFlag(false), m_bContinueSession(false)
+{
+}
+
+CeCosTestDownloadFilter::~CeCosTestDownloadFilter()
+{
+}
+
+//------------------------
+// Output helpers.
+
+void
+CeCosTestDownloadFilter::ConsoleWrite(const char* pszStr)
+{
+    fputs(pszStr, stderr);
+    fflush(stderr);
+}
+
+void
+CeCosTestDownloadFilter::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,"[d] ",4);
+      ConsoleWrite(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
+CeCosTestDownloadFilter::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
+CeCosTestDownloadFilter::TargetWrite(CeCosTestSerial &pSer, 
+                                    const unsigned char* buffer, int len)
+{
+    unsigned int __written;
+
+    if (m_bOptSerDebug)
+        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 _T("serial write failed");
+        }
+        buffer += __written;
+        len -= __written;
+    } while (len);
+}
+
+// Snuffed from gdb/remote.c
+int
+CeCosTestDownloadFilter::tohex (int nib)
+{
+    if (nib < 10)
+        return '0'+nib;
+    else
+        return 'a'+nib-10;
+}
+
+int
+CeCosTestDownloadFilter::hexnumstr (unsigned char* buf, unsigned long num)
+{
+  int i;
+  unsigned long num2 = num;
+
+  for (i = 0; num2 != 0; i++)
+    num2 >>= 4;
+
+  int len = MAX (i, 1);
+
+  buf[len] = '\0';
+
+  for (i = len - 1; i >= 0; i--)
+    {
+      buf[i] = "0123456789abcdef" [(num & 0xf)];
+      num >>= 4;
+    }
+
+  return len;
+}
+
+int
+CeCosTestDownloadFilter::hexnumlen (unsigned long num)
+{
+  int i;
+  unsigned long num2 = num;
+
+  for (i = 0; num2 != 0; i++)
+    num2 >>= 4;
+
+  return MAX (i, 1);
+}
+
+// Based on routines in gdb/remote.c
+int
+CeCosTestDownloadFilter::put_binary (unsigned char* buf, int len,
+                                     unsigned long dl_address,
+                                     int packet_size,
+                                     CeCosTestSerial& serial)
+{
+    int i;
+    unsigned char csum;
+    Buffer buf2(packet_size);
+    unsigned char ch;
+    int tcount = 0;
+    unsigned char *p, *p2, *plen;
+
+    while (len > 0) {
+        /* Subtract header overhead from MAX payload size: 
+           $M<memaddr>,<len>:#nn */
+        int max_buf_size = 
+            buf2.Size() 
+            - ( 2 + hexnumlen(dl_address) + 1 + hexnumlen(buf2.Size()) + 4);
+
+        /* Copy the packet into buffer BUF2, encapsulating it
+           and giving it a checksum.  */
+        int todo = MIN (len, max_buf_size);
+
+        p = (unsigned char*) buf2.Data();
+        *p++ = '$';
+
+        // Add X header.
+        *p++ = 'X';
+        p += hexnumstr(p, dl_address);
+        *p++ = ',';
+        plen = p;			/* remember where len field goes */
+        p += hexnumstr(p, todo);
+        *p++ = ':';
+
+        int escaped = 0;
+        for (i = 0;
+             (i < todo) && (i + escaped) < (max_buf_size - 2);
+             i++)
+        {
+            switch (buf[i] & 0xff)
+            {
+            case '$':
+            case '#':
+            case 0x7d:
+                /* These must be escaped */
+                escaped++;
+                *p++ = 0x7d;
+                *p++ = (unsigned char) ((buf[i] & 0xff) ^ 0x20);
+                break;
+            default:
+                *p++ = (unsigned char) (buf[i] & 0xff);
+                break;
+            }
+        }
+
+        if (i < todo)
+        {
+            /* Escape chars have filled up the buffer prematurely, 
+               and we have actually sent fewer bytes than planned.
+               Fix-up the length field of the packet.  */
+        
+            /* FIXME: will fail if new len is a shorter string than 
+               old len.  */
+            
+            plen += hexnumstr (plen, i);
+            *plen++ = ':';
+        }
+
+        // Calculate checksum
+        p2 = (unsigned char*)buf2.Data();
+        p2++; // skip $
+        csum = 0;
+        while (p2 < p)
+            csum = (unsigned char)(csum + *p2++);
+        *p++ = '#';
+        *p++ = (unsigned char) tohex ((csum >> 4) & 0xf);
+        *p++ = (unsigned char) tohex (csum & 0xf);
+
+        /* Send it over and over until we get a positive ack.  */
+
+        int resend = 1;
+        const unsigned char* write_ptr = (const unsigned char*) buf2.Data();
+        int write_len = (int)p-(int)buf2.Data();
+        while (resend)
+        {
+            unsigned int __written;
+
+            Trace("Sending bytes for %p-%p\n", dl_address, dl_address+i);
+            TargetWrite(serial, write_ptr, write_len);
+
+            /* read until either a timeout occurs (-2) or '+' is read */
+            for(;;)
+            {
+                unsigned int __read;
+                serial.Read(&ch, 1, __read);
+                
+                if (0 == __read) {
+                    tcount ++;
+                    if (tcount > 3) {
+                        Trace("Timeout in putpkt_binary\n");
+                        return 0;
+                    }
+                    break;		/* Retransmit buffer */
+                }
+                
+                switch (ch)
+                {
+                case '+':
+                    // Now expect OK packet from target
+                    unsigned char ok_msg[6];// $OK#9a
+                    serial.Read(ok_msg, 6, __read);
+                    
+                    // Reply with ACK
+                    serial.Write("+", 1, __written);
+                    
+                    // And process next packet.
+                    resend = 0;
+                    break;
+
+                case '-':
+                    // Bad packet CRC. Retransmit.
+                    Trace ("Bad CRC\n");
+                    break;
+                    
+                default:
+                    Trace("Got junk..%02x\n", ch);
+                    continue;           // keep reading
+                }
+                break;		/* Here to retransmit */
+            }
+        }
+
+        len -= i;
+        dl_address += i;
+        buf += i;
+    }
+
+    return 1;
+}    
+
+bool CALLBACK
+DownloadFilterFunction(void*& pBuf,
+                       unsigned int& nRead,
+                       CeCosTestSerial& serial,
+                       CeCosTestSocket& socket,
+                       void* pParem)
+{
+    CeCosTestDownloadFilter* p = (CeCosTestDownloadFilter*) pParem;
+    bool res = false;
+
+    try {
+        res = p->FilterFunctionProper(pBuf, nRead, serial, socket);
+    } 
+    catch (LPCTSTR s) {
+        TRACE(_T("Download filter caught string: %s\n"), s);
+    }
+    catch (...) {
+        TRACE(_T("Download filter caught unknown exception\n"));
+    }
+
+    return res;
+}
+
+bool
+CeCosTestDownloadFilter::FilterFunctionProper(void*& pBuf,
+                                              unsigned int& nRead,
+                                              CeCosTestSerial& serial,
+                                              CeCosTestSocket& socket)
+{
+    char* buffer = (char*) pBuf;
+
+    // Assume the worst - don't allow session to continue until a successful
+    // download.
+    m_bContinueSession = false;
+
+    // Output the serial data if option enabled
+    if (m_bOptSerDebug)
+        PrintHex((unsigned char*) buffer, nRead);
+
+    // Stop here if in NULL-filter mode
+    if (m_bNullFilter)
+        return true;
+
+    // Command handling.
+    // Be strict here; very first byte we see must be the start marker,
+    // else go into NULL-filter mode
+    unsigned int i = 0;
+    if (!m_bCmdFlag) {
+        if ('@' != buffer[i]) {
+            m_bNullFilter = true;
+            return true;
+        }
+        m_bCmdFlag = true;
+    }
+
+    // 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) {
+                    throw _T("Extra bytes after command packet!?!");
+                }
+            }
+        }
+
+        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;
+            // command now in m_aCmd[]
+
+            Trace("Got command %s\n", m_aCmd);
+
+            // After this, never interfere.
+            m_bNullFilter = true;
+
+            // Get arguments: @<length>:<dl_address>:<start_address>!
+            int length, packet_size;
+            unsigned long dl_addr, start_addr;
+            INIT_VALUE(&m_aCmd[1]);
+            SET_VALUE(int, length);
+            SET_VALUE(unsigned long, dl_addr);
+            SET_VALUE(unsigned long, start_addr);
+            SET_VALUE(int, packet_size);
+
+            Trace("len %d, dl %08x, start %08x, packet_size %d\n", 
+                  length, dl_addr, start_addr, packet_size);
+
+            // Reply so host will send file.
+            socket.send("@", 1);
+
+            // Read file from host
+            Buffer buf(length);
+            if (socket.recv(buf.Data(), length)) {
+            
+                // Remember old blocking state, and set serial to
+                // blocking reads.
+                bool __blocking = serial.GetBlockingReads();
+                serial.SetBlockingReads(true);
+                serial.Flush();
+
+                // Send + to target, acking whatever packet was pending
+                unsigned int __written = 0;
+                serial.Write("+", 1, __written);
+                
+                // Convert to packets and transfer to target.
+                if (put_binary((unsigned char*) buf.Data(), 
+                               length, dl_addr, packet_size, serial)) {
+                    // Send detach signal to target
+                    unsigned char ch;
+                    unsigned int __read;
+                    serial.Write("$D#44", 5, __written);
+                    serial.Read(&ch, 1, __read);
+                    
+                    // Reply to host marking end of download
+                    socket.send("+", 1);
+
+                    // Let server know it's OK to accept another connection
+                    // in this session.
+                    m_bContinueSession = true;
+                } else {
+                    // Reply to host marking failed download
+                    socket.send("-", 1);
+                }
+
+                // Reset previous blocking mode
+                serial.SetBlockingReads(__blocking);
+
+            } else {
+                // Reply to host marking failed file transfer
+                socket.send("?", 1);
+            }
+        }
+        nRead = 0;                      // Never leave anything for caller
+                                        // This is a violation of the intended
+                                        // filter function behavior.
+    }
+    return true;
+}