Hogwarts Wand Docs: ../server/wandio.c

Wand Sourcecode: ../server/wandio.c

//
// wandio.c - wand functions
// 
// Copyright (C) 2006  Nathan (Acorn) Pooley
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
// 
// 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 (gpl.txt) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// You can contact the author, Nathan (Acorn) Pooley, by writing
// to Nathan (Acorn) Pooley, 949 Buckeye Drive, Sunnyvale, CA  94086, USA
// or through the form at http://www.rawbw.com/~acorn/wand/feedback.html
//
//
//#@DOC@ functions for communicating with the wand (over serial port)



//###########################################################################
//############################### INCLUDES ##################################
//###########################################################################

#include "wandio.h"

#include "ac_serialio.h"
#include "ac_fdio.h"
#include "ac_time.h"
#include "ac_log.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

//###########################################################################
//############################### DEFINES ###################################
//###########################################################################

#define CTRL(c)     ((c) & 0x1f)

//###########################################################################
//############################### TYPEDEFS ##################################
//###########################################################################

typedef enum WandStateEnum {
    WS_UNKNOWN,
    WS_RECOVER,
    WS_PROMPT,
    WS_CMD,
    WS_PROMPT_PLUS,
    WS_ECHO,
    WS_REPLY,
    WS_DISCONNECT,

    WS_END
} WandState;

typedef enum WandCKSStateEnum {
    WCKS_DISABLED,
    WCKS_NEWLINE,
    WCKS_LINE,
    WCKS_AT,
    WCKS_C1,
    WCKS_C2,
    WCKS_ERR,
    WCKS_GARBAGE,

    WCKS_END
} WandCKSState;

struct WandInfoRec {
    AcSerial *sio;

    int fd;
    AcFdGroup *grpNull;
    AcFdGroup *grpHandler;
    AcFdGroup *grpSleep;
    char *outBuf;
    int   outBufLen;

    WandState state;
    int line_cnt;
    int cksum_start_cnt;
    int cksum;
    WandCKSState cksum_state;

    char recoverStr[4];
    char *recoverPtr;
    AcTime recoverTime;

    int debugLevel;
    int wasdisconnect;

    int quiet;

    WandOutFunction wandOutFunc;
    void *wandOutFuncData;

    char lastprompt[1000];
    char line[1000];
};

//###########################################################################
//############################### PROTOTYPES ################################
//###########################################################################

static void WandFlush(WandInfo *wi);

//###########################################################################
//############################### GLOBALS ###################################
//###########################################################################

//###########################################################################
//############################### CODE ######################################
//###########################################################################

//===========================================================================
// WandSleep()
//===========================================================================
//
// return values:
//   -2   = timeout
//    2   = keyboard input available
//
static int WandSleep(WandInfo *wi, unsigned long usec)
{
    if (usec == 0) return -2;
    return AcFdGroupWaitTimeout(wi->grpSleep,usec);
}

//===========================================================================
// makePrintable()
//===========================================================================
static char *makePrintable(char *s,int cnt)
{
    static char buf[2000];
    char *d = buf;

    while(cnt>0) {
        if (d-buf > sizeof(buf)-10) {
            *(d++) = '\\';
            *(d++) = '.';
            *(d++) = '.';
            *(d++) = '.';
            break;
        }
        if (*s < ' ' || *s > '}' || *s == '\\') {
            d += sprintf(d,"\\%02x",*(unsigned char*)s);
        } else {
            *(d++) = *s;
        }
        s++;
        cnt--;
    }
    *d = 0;
    return buf;
}

//===========================================================================
// WandPromptWait() - wait for prompt
//===========================================================================
//
// Return
//     0 success
//     2 keypressed
//    -2 timeout
//
int WandPromptWait(WandInfo *wi, AcTime *timeout)
{
    int rv = 0;
    unsigned long usec = 200000;
    AcTime t0;
    AcTimeGet(&t0);
    if (wi->debugLevel >= 9) {
        AcLogPrintf(0,4,"9:<enter WandPromptWait>\n");
        fflush(stdout);
    }
    if (timeout && timeout->sec == 0 && timeout->usec < usec) {
        usec = timeout->usec;
    }
    while(wi->state != WS_PROMPT || wi->recoverPtr) {

        int wrv = AcFdGroupWaitTimeout(wi->grpHandler,usec);
        if (wrv) {
            if (wrv < 0) {
                WandRecover(wi);
            }
            rv = wrv;
            break;
        }

        //
        // check timeout
        //
        if (timeout) {
            if (wi->state != WS_PROMPT || wi->recoverPtr) {

                AcTime t1, d;
                AcTimeGet(&t1);
                AcTimeDiff(&d,&t1,&t0);
                if (d.sec > timeout->sec || 
                    (d.sec == timeout->sec && d.usec > timeout->usec)) {
                    //
                    // timed out
                    //
                    if (wi->debugLevel >= 9) {
                        AcLogPrintf(0,4,"9:<exit  WandPromptWait (timeout)>\n");
                        fflush(stdout);
                    }
                    return -2;
                }
            }
        }
    }
    if (wi->debugLevel >= 9) {
        AcLogPrintf(0,4,"9:<exit  WandPromptWait>\n");
        fflush(stdout);
    }
    return rv;
}

//===========================================================================
// WandPromptWaitUsec() - wait for prompt with usec timeout
//===========================================================================
//
// Return
//     0 success
//     2 keypressed
//    -2 timeout
//
int WandPromptWaitUsec(WandInfo *wi, unsigned long usec)
{
    AcTime timeout;
    timeout.sec = 0;
    while(usec > 1000000) {
        timeout.sec++;
        usec -= 1000000;
    }
    timeout.usec = usec;
    return WandPromptWait(wi,&timeout);
}

//===========================================================================
// WandGetPrompt()
//===========================================================================
char *WandGetPrompt(WandInfo *wi)
{
    return strdup(wi->lastprompt);
}

//===========================================================================
// WandPrompt()
//===========================================================================
void WandPrompt(WandInfo *wi)
{
    if (wi->state != WS_DISCONNECT && wi->state != WS_PROMPT) {
        AcTime timeout;
        timeout.sec = 1;
        timeout.usec = 0;
        WandPromptWait(wi,&timeout);
    }

    if (wi->state == WS_PROMPT) {
        wi->wasdisconnect = 0;
        AcLogPrompt(0,wi->lastprompt,0);
    } else if (wi->state == WS_DISCONNECT) {
        wi->wasdisconnect = 1;
        AcLogPrompt(0,"(disconnect)> ",0);
    }
}

#if 1
//===========================================================================
// WandWrite() - write chars to wand (raw)
//===========================================================================
int WandWrite(WandInfo *wi, const char *buf, int cnt)
{
    int sum = 0;
    if (wi->debugLevel >= 9) {
        int i;
        AcLogPrintf(0,4,"9:--WandWrite: ");
        for (i=0; i<cnt; i++) {
            if (buf[i]>=0x20 && buf[i]<0x7f) {
                AcLogPrintf(0,4,"9:%c",buf[i]);
            } else {
                AcLogPrintf(0,4,"9:\\%02x",buf[i]);
            }
        }
        AcLogPrintf(0,4,"9:\n");
    }
    while (cnt>0) {
        int n = write(wi->fd, buf, 1);
        AcLogWrite(0,1,buf,1);
        if (!n) break;
        sum += n;
        buf += n;
        cnt -= n;
        if (cnt == 0) break;
#if 0
        //
        // Do this to slow things down if serial does not work right
        //
        if ((sum & 0xf) == 0) {
            if (WandSleep(wi,20000) == 2) return sum;
            //if (WandSleep(wi,1000) == 2) return sum;
        }
#endif
    }
    AcLogFlush(0);
    return sum;
}
#else
//===========================================================================
// WandWrite() - write chars to wand (raw) SLOW BUT WORKS!!
//===========================================================================
int WandWrite(WandInfo *wi, const char *buf, int cnt)
{
    int sum = 0;
    if (wi->debugLevel >= 9) {
        int i;
        AcLogPrintf(0,4,"9:--WandWrite: ");
        for (i=0; i<cnt; i++) {
            if (buf[i]>=0x20 && buf[i]<0x7f) {
                AcLogPrintf(0,4,"9:%c",buf[i]);
            } else {
                AcLogPrintf(0,4,"9:\\%02x",buf[i]);
            }
        }
        AcLogPrintf(0,4,"9:\n");
    }
    while (cnt>0) {
        int n = write(wi->fd, buf, 1);
        AcLogWrite(0,1,buf,1);
        if (!n) break;
        sum += n;
        buf += n;
        cnt -= n;
        if (cnt == 0) break;
        //if (WandSleep(wi,20000) == 2) return sum;
        if (WandSleep(wi,1000) == 2) return sum;
    }
    AcLogFlush(0);
    return sum;
}
#endif

//===========================================================================
// WandWriteChar() - write 1 char to wand
//===========================================================================
int WandWriteChar(WandInfo *wi, int ch)
{
    char buf[1];
    buf[0] = ch;
    return WandWrite(wi, buf, 1);
}

//===========================================================================
// WandWriteTimed() - write chars to wand slowly
//===========================================================================
int WandWriteTimed(WandInfo *wi, const char *buf, int cnt, unsigned long usec)
{
    int sum = 0;
    while(cnt>0) {
        if (WandSleep(wi,usec) == 2) return sum;
        if (!WandWrite(wi, buf, 1)) {
            return sum;
        }
        buf++;
        cnt--;
        sum++;
    }
    return sum;
}

//===========================================================================
// WandIsRunning() - true if wand is running (as of last prompt)
//===========================================================================
//
// Return
//     0 halted or disconnected
//     1 running (RUN or DEBUG mode)
//
int WandIsRunning(WandInfo *wi)
{
printf(">>>>>>>>> state=%d  lastpompt=%s\n",wi->state,wi->lastprompt);
    switch(wi->state) {
        case WS_RECOVER:
        case WS_PROMPT:
        case WS_CMD:
        case WS_PROMPT_PLUS:
        case WS_ECHO:
        case WS_REPLY:
            break;

        case WS_DISCONNECT:
        case WS_UNKNOWN:
        default:
            return 0;
    }
    if (!strncmp(wi->lastprompt,"Debug",4) ||
        !strncmp(wi->lastprompt,"Run",3)) {
        return 1;
    }
    return 0;
}

#if 1
//===========================================================================
// WandHalt() - halt the wand
//===========================================================================
//
// Return
//     0 success
//     2 keypressed
//    -2 timeout
//
int WandHalt(WandInfo *wi)
{
    int rv;
    int i;
    unsigned long usec1 = 100000;
    unsigned long usec2 = 1000000;
    static char hcmd1[] = "H\n";
    static char hcmd2[] = "Hbeefb055\n";
    hcmd1[1] = 0x0a;
    hcmd2[9] = 0x0a;

    for (i=0; ; i++) {
        char *hcmd = hcmd1;
        int len = 2;

        if (i<3 && !strncmp(wi->lastprompt,"Run>",4)) {
            i = 3;
        }
        if (i>2 && (i&1)) {
            hcmd = hcmd2;
            len = 10;
        }
        WandWriteTimed(wi, hcmd, len, usec1);
        wi->state = WS_CMD;

        WandPromptWaitUsec(wi, usec2);

        if (wi->state == WS_PROMPT) {
            if (!strncmp(wi->lastprompt,"Halt",4)) {
                if (wi->debugLevel >= 9) {
                    AcLogPrintf(0,4,"9:--Halt succeeded\n");
                }
                return 0;
            }
        }

        rv = WandRecoverWait(wi);

        //
        // return if keypressed
        //
        if (rv == 2) return 2;

        if (i>3) {
            usec1 *= 2;
            usec2 *= 2;
        }
        if (usec1 > 1000000) {
            usec1 = 1000000;
        }
        if (usec2 > 5000000) {
            usec2 = 5000000;
        }
        if (i>1) {
            AcLogPrintf(0,4,
                    "2:Attempting to halt wand...  (usec1=%ld  usec2=%ld)\n",
                    usec1,usec2);
        }
    }
}

#else
//===========================================================================
// WandHalt() - halt the wand
//===========================================================================
//
// return 0 on success
// return -1 on failure
//
int WandHalt(WandInfo *wi)
{
    static AcTime timeout1;
    static AcTime timeout2;
    static char hcmd1[] = "_H_";
    static char hcmd2[] = "_Hbeefb055_";
    unsigned long usec = 250000;
    AcTime *timeout = &timeout1;

    if (hcmd1[0] == '_') {
        hcmd1[0] = CTRL('X');
        hcmd2[0] = CTRL('X');
        hcmd1[sizeof(hcmd1)-2] = 0x0a;
        hcmd2[sizeof(hcmd2)-2] = 0x0a;
        timeout1.sec  = 0;
        timeout1.usec = 100000;
        timeout2.sec  = 1;
        timeout2.usec = 0;
    }

    while(1) {

        //
        // try simple halt command
        //
        if (wi->debugLevel >= 9) {
            AcLogPrintf(0,4,"9:--Attempting to Halt wand with H command\n");
        }
        WandWriteTimed(wi, hcmd1, sizeof(hcmd1)-1, usec);
        if (WandSleep(wi,usec) == 2) {
            AcLogPrintf(0,4,"3:Halt aborted by keyboard!\n");
            return -1;
        }
        WandPromptWait(wi, timeout);


        //
        // halted yet?
        //
        if (wi->state == WS_PROMPT) {
            if (!strncmp(wi->lastprompt,"Halt",4)) {
                if (wi->debugLevel >= 9) {
                    AcLogPrintf(0,4,"9:--Halt succeeded\n");
                }
                return 0;
            }
        }

        //
        // taking a while - tell the user
        //
        AcLogPrintf(0,4,"2:Attempting to halt wand...  (usec=%ld)\n",usec);

        //
        // try complex halt command
        //
        if (wi->debugLevel >= 9) {
            AcLogPrintf(0,4,"9:--simple H command failed\n");
            AcLogPrintf(0,4,
                "9:--Attempting to Halt wand with password H command\n");
        }
        WandWriteTimed(wi, hcmd2, sizeof(hcmd2)-1, usec);
        if (WandSleep(wi,usec) == 2) {
            AcLogPrintf(0,4,"3:Halt aborted by keyboard!\n");
            return -1;
        }
        WandPromptWait(wi, timeout);

        //
        // halted yet?
        //
        if (wi->state == WS_PROMPT) {
            if (!strncmp(wi->lastprompt,"Halt",4)) {
                if (wi->debugLevel >= 9) {
                    AcLogPrintf(0,4,"9:--Halt succeeded\n");
                }
                return 0;
            }
        }
        
        //
        // increase the time between chars
        //
        if (usec < 2000000) {
            usec *= 2;
        } else {
            AcLogPrintf(0,4,"1:Failed to halt wand!\n");
            return -1;
        }
        timeout = &timeout2;
        if (wi->debugLevel >= 9) {
            AcLogPrintf(0,4,"9:--Increasing halt char timeout to %f seconds\n",
                (double)usec/1000000.0);
        }

    }
}
#endif

//===========================================================================
// WandOutCallback() - set callback for wand output text
//===========================================================================
void WandOutCallback(WandInfo *wi, 
                    WandOutFunction func,
                    void            *data)
{
    wi->wandOutFunc     = func;
    wi->wandOutFuncData = data;
}

//===========================================================================
// WandCmdf()
//===========================================================================
int WandCmdf(WandInfo *wi, const char *fmt, ...)
{
    int ccnt;
    va_list va;
    char cbuf[1000];
    char rbuf[1000];
    char *s;
    char *r;
    char *se;
    char *re;

    va_start(va, fmt);
    ccnt = vsnprintf(cbuf,sizeof(cbuf)-5,fmt,va);
    va_end(va);

    if (wi->debugLevel >= 9) {
        AcLogPrintf(0,4,"9:--Got CMD: '%s'\n",cbuf);
    }

    if (wi->state != WS_PROMPT) {
        WandRecoverWait(wi);
    }

    wi->state = WS_CMD;

#if 0
    //
    // handle halt command specially
    //
    if (!strcmp(cbuf,"H")) {
        return WandHalt(wi);
    }
#endif

    if (ccnt) {
        s = cbuf;
        se = cbuf + ccnt;
        while(s != se) {
            s += WandWrite(wi, s, ccnt);
        }

        s = cbuf;
        while(s != se) {
            int rcnt = 0;
            int rv = AcFdGroupWaitTimeout(wi->grpNull,250000);

            if (rv) {
                AcLogPrintf(0,4,"1:Wand command failed: '%s'\n",cbuf);
                WandRecoverWait(wi);
                return -1;  // timeout
            }

            rcnt = read(wi->fd, rbuf, sizeof(rbuf));
            AcLogWrite(0,2,rbuf,rcnt);
            AcLogFlush(0);
            rbuf[rcnt] = 0;
            r = rbuf;
            re = r + rcnt;
            while(r != re && s != se) {
                if (*r != *s) {
                    AcLogPrintf(0,4,"5:Error in command: '%s'\n",cbuf);
                    AcLogPrintf(0,4,"5:Error: Wand sent: '%s' (bad echo)\n",
                        makePrintable(rbuf,rcnt));
                    return -1;
                }
                wi->cksum += *s;
                s++;
                r++;
            }
        }

        if (r != re) {
            AcLogPrintf(0,4,"5:Error in command: '%s'\n",cbuf);
            AcLogPrintf(0,4,"5:Error: Wand sent: '%s' (too many)\n",
                makePrintable(rbuf,re-rbuf));
            return -1;
        }
    }

    if (wi->debugLevel >= 9) {
        AcLogPrintf(0,4,"9:--Sending LF\n");
    }
    cbuf[0] = 0xa;
#if 1   // this improves reliability significantly
    WandSleep(wi,20000);
#endif
    WandWrite(wi, cbuf, 1);

    return 0;
}

//===========================================================================
// WandCmdfStr() - send wand command & get result in string (free str when done)
//===========================================================================
char *WandCmdfStr(WandInfo *wi, const char *fmt, ...)
{
    va_list va;
    char cbuf[1000];
    static char buf[20000];
    char *s;
    int cnt;
    int rv;
    AcTime t0;
    
    if (wi->outBuf) {
        AcLogPrintf(0,4,"0:Error: called WandCmdfStr() but outBuf != 0\n");
        return 0;
    }

    va_start(va, fmt);
    cnt = vsnprintf(cbuf,sizeof(buf)-5,fmt,va);
    va_end(va);

    cbuf[cnt] = 0;

    rv = WandCmdf(wi, "%s", cbuf);

    if (rv) return 0;

    wi->outBuf = buf;
    wi->outBufLen = sizeof(buf)-1;
    s = buf;

    AcTimeGet(&t0);

    while(wi->state != WS_PROMPT) {
        int wrv = AcFdGroupWait(wi->grpHandler);
        if (wrv) {
            WandRecoverWait(wi);
            return 0;
        }
        if (wi->outBuf == s) {
            AcTime t1, d;
            AcTimeGet(&t1);
            AcTimeDiff(&d,&t1,&t0);
            //if (d.sec > 1 || d.usec > 250000) {
            if (d.sec >= 2) {
                if (wi->outBuf == buf) {
                    AcLogPrintf(0,4,
                        "5:Error: no response from cmd '%s'\n",cbuf);
                } else {
                    *(wi->outBuf) = 0;
                    AcLogPrintf(0,4,
                        "5:Error: incomplete response from cmd '%s'\n",cbuf);
                    AcLogPrintf(0,4,"5:============== start response\n");
                    AcLogPrintf(0,4,"5:%s\n",buf);
                    AcLogPrintf(0,4,"5:============== end   response\n");
                }
                WandRecoverWait(wi);
                return 0;
            }
        } else {
            AcTimeGet(&t0);
        }
        s = wi->outBuf;
    }
    
    cnt = wi->outBuf - buf;

    s = malloc(cnt+1);
    if (s) {
        memcpy(s,buf,cnt);
        s[cnt] = 0;
        if (cnt > 0 && s[cnt-1] == '\n') s[cnt-1] = 0;
    }
    wi->outBuf = 0;
    wi->outBufLen = 0;

    return s;
}

//===========================================================================
// wandChar()
//===========================================================================
static void wandChar(WandInfo *wi, int c)
{
    char *outStr = 0;
    char *msg = "";

    if (wi->recoverPtr) {
        if (c == wi->recoverPtr[0]) {
            wi->recoverPtr++;
            if (wi->debugLevel >= 9) {
                AcLogPrintf(0,4,"9:--recoverPtr='%s'\n",wi->recoverPtr);
            }
            if (!wi->recoverPtr[0]) {
                wi->recoverPtr = 0;
            }
        } else {
            wi->recoverPtr = wi->recoverStr;
        }
    }

    if ((c==0x0a || c==0x0d) && wi->cksum_state != WCKS_DISABLED) {
        if (wi->cksum_state == WCKS_GARBAGE) {
            msg = "<garbage>";
        } else if (wi->cksum_state != WCKS_C2) {
            msg = "<BAD CHECKSUM>";
        }
        wi->cksum_state = WCKS_NEWLINE;
        wi->cksum = 0;
    } else switch(wi->cksum_state) {
        case WCKS_NEWLINE:
            if (c=='\n' || c=='\r') {
                wi->cksum = 0;
                break;
            }
            wi->cksum_state = WCKS_LINE;
        case WCKS_LINE:
            if (c=='@') {
                wi->cksum_state = WCKS_AT;
                wi->cksum_start_cnt = wi->line_cnt;
            } else {
                wi->cksum += c;
            }
            break;
        case WCKS_AT:
            if (isxdigit(c)) {
                int val = isdigit(c) ? c-'0' : toupper(c)-'A'+10;
                if (val == ((wi->cksum>>4)&0xf)) {
                    wi->cksum_state = WCKS_C1;
                    break;
                }
            }
            wi->cksum_state = WCKS_LINE;
            wi->cksum += '@';
            wi->cksum += c;
            break;
        case WCKS_C1:
            if (isxdigit(c)) {
                int val = isdigit(c) ? c-'0' : toupper(c)-'A'+10;
                if (val == ((wi->cksum)&0xf)) {
                    wi->cksum_state = WCKS_C2;
                    break;
                }
            }
            wi->cksum_state = WCKS_LINE;
            wi->cksum += '@';
            wi->cksum += wi->line[wi->line_cnt-1];
            wi->cksum += c;
            break;
        case WCKS_C2:
            wi->cksum_state = WCKS_LINE;
            break;
        case WCKS_GARBAGE:
        case WCKS_DISABLED:
        default:
            break;
    }

    wi->line[wi->line_cnt++] = c;

    //
    // check for prompt
    //
    if (c == ' ' && 
        wi->line_cnt>2 && 
        wi->line[wi->line_cnt-2] == '>') {

        int i;
        for (i=0; ; i++) {
            if (i==wi->line_cnt-2) {
                //
                // got a prompt
                //
                if (memcmp(wi->lastprompt, wi->line, wi->line_cnt)) {
                    memcpy(wi->lastprompt, wi->line, wi->line_cnt);
                    wi->lastprompt[wi->line_cnt] = 0;
                    if (!wi->outBuf) {
                        AcLogPrintf(0,4,"1:Wand: '%s'\n",wi->lastprompt);
                    }
                }
                wi->line_cnt = 0;
                if (wi->debugLevel >= 9) {
                    AcLogPrintf(0,4,"9:--Got prompt: '%s'\n",wi->lastprompt);
                }

                wi->state = WS_PROMPT;
                if (wi->debugLevel >= 9) {
                    AcLogPrintf(0,4,"9:--state=WS_PROMPT=%d\n",wi->state);
                }
                return;
            }
            if (!isalpha(wi->line[i])) {
                if (i< 2 && isxdigit(wi->line[i])) continue;
                if (i==2 && isspace(wi->line[i])) continue;
                break;
            }
        }
    }

    //
    // check for end of line
    //
    if (c!=0xa && 
        c!=0xd && 
        wi->line_cnt<sizeof(wi->line)-20) {

        if (wi->state == WS_PROMPT) {
            wi->state = WS_PROMPT_PLUS;
            if (wi->debugLevel >= 9) {
                AcLogPrintf(0,4,"9:--state=WS_PROMPT_PLUS=%d\n",wi->state);
            }
        }
        return;
    }

    if (msg[0]==0 && 
        wi->cksum_start_cnt >= 0 && 
        wi->cksum_start_cnt < wi->line_cnt) {
        wi->line_cnt = wi->cksum_start_cnt+1;
    }
    wi->line[--wi->line_cnt] = 0;

    //
    // ignore blank lines
    //
    if (wi->line_cnt == 0) {
        return;
    }

    //
    // check for recover timeout
    //
    if (wi->recoverPtr) {
        AcTime t, d;
        AcTimeGet(&t);
        AcTimeDiff(&d,&t,&wi->recoverTime);
        if (d.sec >= 2) {
            AcLogPrintf(0,4,"6:<<<recoverPtr '%s' timed out>>>\n",
                wi->recoverStr);
            wi->recoverPtr = 0;
        }
    }

    if (wi->outBuf) {
        if (msg[0]) {
            AcLogPrintf(0,4,"4:Error: %s\n",msg);
            AcLogPrintf(0,4,"4:Error: wand reply buffer overflow\n");
            AcLogPrintf(0,4,"4:Wand: '%s'%s\n",wi->line,msg);
#if 0
            if (wi->wandOutFunc) {
                wi->wandOutFunc(wi->wandOutFuncData, wi->line);
            }
#endif

        } else if (wi->line_cnt < wi->outBufLen) {
            memcpy(wi->outBuf, wi->line, wi->line_cnt);
            wi->outBufLen -= wi->line_cnt;
            wi->outBuf    += wi->line_cnt;
            if (wi->outBufLen) {
                wi->outBuf[0] = '\n';
                wi->outBuf++;
                wi->outBufLen--;
            }
            
        } else {
            AcLogPrintf(0,4,"4:Error: wand reply buffer overflow\n");
            wi->outBufLen = 0;
            AcLogPrintf(0,4,"4:Wand: '%s'%s\n",wi->line,msg);
#if 0
            if (wi->wandOutFunc) {
                wi->wandOutFunc(wi->wandOutFuncData, wi->line);
            }
#endif
        }
    } else if (wi->recoverPtr || wi->quiet) {
        AcLogPrintf(0,4,"5:Wand: '%s'%s\n",wi->line,msg);
    } else {
        AcLogPrintf(0,4,"3:Wand: '%s'%s\n",wi->line,msg);
#if 0
        if (wi->wandOutFunc) {
            wi->wandOutFunc(wi->wandOutFuncData, wi->line);
        }
#endif
        if (wi->line[0] && wi->wandOutFunc)  {
            outStr = strdup(wi->line);
        }
    }
    wi->line_cnt = 0;

    if (outStr) {
        if (wi->wandOutFunc) {
            wi->wandOutFunc(wi->wandOutFuncData, outStr);
        }
        free(outStr);
    }

    return;
}

//===========================================================================
// WandRecoverString() - set a recover string - send string to look for
//===========================================================================
//
// set a string to look for.
//
void WandRecoverString(WandInfo *wi)
{
    static int c = 'A';
    char buf[6];

    if (c=='Z') {
        c = 'a';
    } else if (c=='z') {
        c = 'A';
    } else {
        c++;
    }
    
    wi->recoverStr[0] = c;
    wi->recoverStr[1] = c;
    wi->recoverStr[2] = c;
    wi->recoverStr[3] = 0;
    wi->recoverPtr = wi->recoverStr;

    if (wi->debugLevel >= 9) {
        AcLogPrintf(0,4,"9:--recoverStr='%s'\n", wi->recoverPtr);
    }

    buf[0] = c;
    buf[1] = c;
    buf[2] = c;
    buf[3] = c;
    buf[4] = CTRL('X');
    buf[5] = 0;

    WandWrite(wi, buf, 5);
}

//===========================================================================
// WandRecover() - recover to known state
//===========================================================================
void WandRecover(WandInfo *wi)
{

    AcLogPrintf(0,4,"6:<<<WandRecover() called>>>\n");
    if (wi->debugLevel >= 9) {
        AcLogPrintf(0,4,"9:--WandRecover called  state=%d\n",wi->state);
    }
    wi->outBuf = 0;
    wi->outBufLen = 0;
    wi->state = WS_DISCONNECT;
    WandSleep(wi,10000);
    WandRecoverString(wi);
    WandSleep(wi,10000);

    wi->cksum_state = WCKS_GARBAGE;
    wi->cksum = 0;
}

//===========================================================================
// WandRecoverWait() - recover to known state
//===========================================================================
//
// Return
//     0 success
//     2 keypressed
//    -2 timeout
//
int WandRecoverWait(WandInfo *wi)
{
    WandRecover(wi);
    return WandPromptWaitUsec(wi,1000000);
}

//===========================================================================
// WandHandleReturn0() - return 0
//===========================================================================
int WandHandleReturn0(int fd, void *data)
{
    return 0;
}

//===========================================================================
// WandHandleReturn2() - return 2
//===========================================================================
int WandHandleReturn2(int fd, void *data)
{
    return 2;
}

//===========================================================================
// WandFlush() - get rid of all pending chars
//===========================================================================
static void WandFlush(WandInfo *wi)
{
    char buf[1000];
    int cnt;
    
    while(1) {
        int rv = AcFdGroupWaitTimeout(wi->grpNull,100000);

        if (rv) {
            return; // timeout -- nothing to read
        }
        cnt = read(wi->fd, buf, sizeof(buf)-100);
        AcLogWrite(0,2,buf,cnt);
        AcLogFlush(0);
    }
}

//===========================================================================
// WandHandleSerial()
//===========================================================================
int WandHandleSerial(int fd, void *data)
{
    WandInfo *wi = data;
    char buf[1000];
    int cnt;
    
    //
    // make sure we really do have something to read
    //
    {
        int rv = AcFdGroupWaitTimeout(wi->grpNull,0);

        if (rv) {
            AcLogPrintf(0,4,
                "0:<<<WandHandleSerial called with nothing to read>>>\n");
            return 0; // timeout -- nothing to read
        }
    }

    cnt = read(fd, buf, sizeof(buf)-100);
    AcLogWrite(0,2,buf,cnt);
    AcLogFlush(0);

    if (cnt) {
        int i;

        buf[cnt] = 0;

        if (wi->debugLevel >= 9) {
            AcLogPrintf(0,4,"9:--From wand: '%s'\n",makePrintable(buf,cnt));
        }

        for (i=0; i<cnt; i++) {
            wandChar(wi,buf[i]);
        }
    }

    if (wi->wasdisconnect && wi->state == WS_PROMPT) {
        WandPrompt(wi);
    }

    return 0;
}

//===========================================================================
// WandGetFd() - get file descriptor group for wand
//===========================================================================
int WandGetFd(WandInfo *wi)
{
    return wi->fd;
}

//===========================================================================
// WandDebugLevel() - set debug level
//===========================================================================
int WandDebugLevel(WandInfo *wi, int level)
{
    int old = wi->debugLevel;
    wi->debugLevel = level;
    return old;
}

//===========================================================================
// WandQuiet() - turn quiet on - surpress some errors a normal verbose level
//===========================================================================
void WandQuiet(WandInfo *wi)
{
    wi->quiet++;
}

//===========================================================================
// WandUnQuiet() - turn quiet off
//===========================================================================
void WandUnQuiet(WandInfo *wi)
{
    wi->quiet--;
    if (wi->quiet<0) wi->quiet = 0;
}

//===========================================================================
// WandInit()
//===========================================================================
WandInfo *WandInit(void)
{
    WandInfo *wi = calloc(1,sizeof(WandInfo));
    if (!wi) return 0;

    wi->debugLevel = 8;

    wi->state = WS_UNKNOWN;
    strcpy(wi->lastprompt,"?>");

    wi->sio = acSerialCreate("/dev/ttyS0", 9600);
    //wi->sio = acSerialCreate("/dev/ttyUSB0", 9600);
    if (!wi->sio) {
        AcLogPrintf(0,4,"0:Error in acSerialCreate\n");
        free(wi);
        return 0;
    }

    acSerialSetNonCanonical(wi->sio, 1, 200);
    acSerialOpen(wi->sio);
    wi->fd = acSerialGetFD(wi->sio);
    if (wi->fd < 0) {
        AcLogPrintf(0,4,"0:Error in acSerialOpen\n");
        free(wi);
        return 0;
    }

    //
    // grpNull does not call any real handler functions.
    // Returns
    //    0 if wand has sent characters
    //    2 if keyboard has sent characters
    //   -2 if timeout
    //
    wi->grpNull = AcFdGroupCreate();
    if (!wi->grpNull) {
        WandShutdown(wi);
        return 0;
    }
    AcFdGroupAdd(wi->grpNull, wi->fd, WandHandleReturn0, wi);
    AcFdGroupAdd(wi->grpNull, 0,      WandHandleReturn2, wi);
    AcFdGroupSetTimeout(wi->grpNull,0,250000);  // 1/4 sec timeout

    //
    // grpHandler calls WandHandleSerial when wand has chars
    // Returns
    //    0 if wand has sent characters (handler gets called too)
    //    2 if keyboard has sent characters
    //   -2 if timeout
    //
    wi->grpHandler = AcFdGroupCreate();
    if (!wi->grpHandler) {
        WandShutdown(wi);
        return 0;
    }
    AcFdGroupAdd(wi->grpHandler, wi->fd, WandHandleSerial, wi);
    AcFdGroupAdd(wi->grpHandler, 0,      WandHandleReturn2, wi);
    AcFdGroupSetTimeout(wi->grpHandler,0,250000);   // 1/4 sec timeout

    //
    // grpSleep waits for some number of usec
    // Characters from wand are ignored (but not lost)
    // Returns
    //    2 if keyboard has sent characters
    //   -2 if timeout
    //
    wi->grpSleep = AcFdGroupCreate();
    if (!wi->grpSleep) {
        WandShutdown(wi);
        return 0;
    }
    AcFdGroupAdd(wi->grpSleep, 0,      WandHandleReturn2, wi);
    AcFdGroupSetTimeout(wi->grpSleep,0,250000); // 1/4 sec timeout

    wi->cksum_state = WCKS_NEWLINE;
    wi->cksum = 0;

    WandRecover(wi);
    WandFlush(wi);
    WandRecover(wi);

    return wi;
}

//===========================================================================
// WandShutdown()
//===========================================================================
void WandShutdown(WandInfo *wi)
{
    WandRecover(wi);

    if (wi->grpSleep) {
        AcFdGroupDestroy(wi->grpSleep);
    }
    if (wi->grpHandler) {
        AcFdGroupDestroy(wi->grpHandler);
    }
    if (wi->grpNull) {
        AcFdGroupDestroy(wi->grpNull);
    }

    acSerialDestroy(wi->sio);
}

This file Copyright (C) 2006 by Nathan (Acorn) Pooley
Go to TOP Wand page
Go to Acorn's personal webpage
Go to Hogwarts website: www.gotohogwarts.com
Snout: www.snout.org/game
Gadgets of Justice Unlimited
Snout GC (Wiki)
Snout Wiki
File created by do_doc at Wed May 30 03:29:06 PDT 2007