/*
 * File      : rz.c
 * the core functions of implementing zmodem protocol
 * Change Logs:
 * Date           Author       Notes
 * 2011-03-29     itspy
 */

#include <rtthread.h>
#include <finsh.h>
#include <shell.h>
#include <rtdef.h>
#include <dfs.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <stdio.h>
#include "zdef.h"

char ZF0_CMD;                    /* file conversion request */
char ZF1_CMD;                    /* file management request */
char ZF2_CMD;                    /* file transport request */
char ZF3_CMD;
rt_uint8_t   Rxframeind;         /* ZBIN ZBIN32, or ZHEX type of frame */
rt_uint16_t  Rxcount;            /* received count*/
char header_type;                /* header type */
rt_uint8_t   rx_header[4];       /* received header */
rt_uint8_t   tx_header[4];       /* transmitted header */
rt_uint32_t  Rxpos;              /* received file position */
rt_uint32_t  Txpos;              /* transmitted file position */
rt_uint8_t   Txfcs32;            /* TURE means send binary frames with 32 bit FCS */
rt_uint8_t   TxCRC;              /* controls 32 bit CRC being sent */
rt_uint8_t   RxCRC;              /* indicates/controls 32 bit CRC being received */
                                 /* 0 == CRC16,  1 == CRC32,  2 == CRC32 + RLE */
char Attn[ZATTNLEN+1];           /* attention string rx sends to tx on err */

void zinit_parameter(void);
void zsend_bin_header(rt_uint8_t type, rt_uint8_t *hdr);
void zsend_hex_header(rt_uint8_t type, rt_uint8_t *hdr);
void zsend_bin_data(rt_uint8_t *buf, rt_int16_t len, rt_uint8_t frameend);
static rt_int16_t zrec_data16(rt_uint8_t *buf, rt_uint16_t len);
static rt_int16_t zrec_data32(rt_uint8_t *buf, rt_int16_t len);
static rt_int16_t zrec_data32r(rt_uint8_t *buf, rt_int16_t len);
rt_int16_t zget_data(rt_uint8_t *buf, rt_uint16_t len);
rt_int16_t zget_header(rt_uint8_t *hdr);
static rt_int16_t zget_bin_header(rt_uint8_t *hdr);
static rt_int16_t zget_bin_fcs(rt_uint8_t *hdr);
rt_int16_t zget_hex_header(rt_uint8_t *hdr);
static void zsend_ascii(rt_uint8_t c);
void zsend_zdle_char(rt_uint16_t ch);
static rt_int16_t zget_hex(void);
rt_int16_t zread_byte(void);
rt_int16_t zxor_read(void);
void zput_pos(rt_uint32_t pos);
void zget_pos(rt_uint32_t pos);




void zinit_parameter(void)
{
    rt_uint8_t i;

    ZF0_CMD  = CANFC32|CANFDX|CANOVIO;      /*  not chose CANFC32,CANRLE,although it have been supported */
    ZF1_CMD  = 0;                               /* fix header length,not support CANVHDR */
    ZF2_CMD  = 0;
    ZF3_CMD  = 0;
    Rxframeind =0;
    header_type   = 0;
    Rxcount  = 0;
    for (i=0;i<4;i++) rx_header[i] = tx_header[i] = 0;
    Rxpos    = Txpos = 0;
    RxCRC    = 0;
    Txfcs32  = 0;

    return ;
}

/* send binary header */
void zsend_bin_header(rt_uint8_t type, rt_uint8_t *hdr)
{
    rt_uint8_t i;
    rt_uint32_t crc;

    zsend_byte(ZPAD);
    zsend_byte(ZDLE);
    TxCRC = Txfcs32;
    if (TxCRC == 0)
    {
        zsend_byte(ZBIN);
        zsend_zdle_char(type);
        /* add 16bits crc */
        crc = 0L;
        crc = updcrc16(type, 0);
        for (i=0;i<4;i++)
        {
            zsend_zdle_char(*hdr);
            crc = updcrc16((0377 & *hdr++),crc);
        }
        crc = updcrc16(0,updcrc16(0,crc));
        zsend_zdle_char(((int)(crc>>8)));
        zsend_zdle_char(crc);
    }
    else if(TxCRC == 1)
    {
        zsend_byte(ZBIN32);
        zsend_zdle_char(type);
        /* add 32bits crc */
        crc = 0xffffffffL;
        crc = updcrc32(type, crc);
        for (i=0;i<4;i++)
        {
            zsend_zdle_char(*hdr);
            crc = updcrc32((0377 & *hdr++), crc);
        }
        crc = ~crc;
        for (i=0; i<4;i++)
        {
            zsend_zdle_char(crc);
            crc >>= 8;
        }
    }
    else if (TxCRC == 2)
    {
        zsend_byte(ZBINR32);
        zsend_zdle_char(type);
        /* add 32bits crc */
        crc = 0xffffffffL;
        crc = updcrc32(type, crc);
        for (i=0;i<4;i++)
        {
            zsend_zdle_char(*hdr);
            crc = updcrc32((0377 & *hdr++), crc);
        }
        crc = ~crc;
        for (i=0; i<4;i++)
        {
            zsend_zdle_char(crc);
            crc >>= 8;
        }
    }

    return;
}

/* send hex header */
void zsend_hex_header(rt_uint8_t type, rt_uint8_t *hdr)
{
    rt_uint8_t i;
    rt_uint16_t crc;

    zsend_line(ZPAD); zsend_line(ZPAD); zsend_line(ZDLE);
    zsend_line(ZHEX);
    zsend_ascii(type);
    crc = updcrc16(type, 0);
    for (i=0; i<4; i++)
    {
        zsend_ascii(*hdr);
        crc = updcrc16((0377 & *hdr++), crc);
    }
    crc = updcrc16(0,updcrc16(0,crc));
    zsend_ascii(crc>>8);
    zsend_ascii(crc);
    /* send display control cmd */
    zsend_line(015); zsend_line(0212);
    if (type != ZFIN && type != ZACK)
        zsend_line(021);
    TxCRC = 0;               /* clear tx crc type */

    return;
}

/* send binary data,with frameend */
void zsend_bin_data(rt_uint8_t *buf, rt_int16_t len, rt_uint8_t frameend)
{
    rt_int16_t i,c,tmp;
    rt_uint32_t crc;

    if (TxCRC == 0)         /* send binary data with 16bits crc check */
    {
        crc = 0x0L;
        for (i=0;i<len;i++)
        {
            zsend_zdle_char(*buf);
            crc = updcrc16((0377 & *buf++), crc);
        }
        zsend_byte(ZDLE); zsend_byte(frameend);
        crc = updcrc16(frameend, crc);
        crc = updcrc16(0,updcrc16(0,crc));
        zsend_zdle_char(crc>>8);
        zsend_zdle_char(crc);
    }
    else if (TxCRC == 1)   /* send binary data with 32 bits crc check */
    {
        crc = 0xffffffffL;
        for (i=0;i<len;i++)
        {
            c = *buf++ & 0377;
            zsend_zdle_char(c);
            crc = updcrc32(c, crc);
        }
        zsend_byte(ZDLE); zsend_byte(frameend);
        crc = updcrc32(frameend, crc);
        crc = ~crc;
        for (i=0;i<4;i++)
        {
            zsend_zdle_char((int)crc);  crc >>= 8;
        }
    }
    else if (TxCRC == 2)   /* send binary data with 32bits crc check,RLE encode */
    {
        crc = 0xffffffffL;
        tmp = *buf++ & 0377;
        for (i = 0; --len >= 0; ++buf)
        {
           if ((c = *buf & 0377) == tmp && i < 126 && len>0)
           {
              ++i;  continue;
           }
           if (i==0)
           {
               zsend_zdle_char(tmp);
               crc = updcrc32(tmp, crc);
               if (tmp == ZRESC)
               {
                   zsend_zdle_char(0100); crc = updcrc32(0100, crc);
               }
               tmp = c;
           }
           else if (i == 1)
           {
                if (tmp != ZRESC)
                {
                    zsend_zdle_char(tmp); zsend_zdle_char(tmp);
                    crc = updcrc32(tmp, crc);
                    crc = updcrc32(tmp, crc);
                    i = 0; tmp = c;
                }

           }
           else
           {
                zsend_zdle_char(ZRESC); crc = updcrc32(ZRESC, crc);
                if (tmp == 040 && i < 34)
                {
                    i += 036;
                    zsend_zdle_char(i);
                    crc = updcrc32(i, crc);
                }
                else
                {
                    i += 0101;
                    zsend_zdle_char(i); crc = updcrc32(i, crc);
                    zsend_zdle_char(tmp); crc = updcrc32(tmp, crc);
                }
                i = 0; tmp = c;
           }
       }
       zsend_byte(ZDLE); zsend_byte(frameend);
       crc = updcrc32(frameend, crc);
       crc = ~crc;
       for (i=0;i<4;i++)
       {
           zsend_zdle_char(crc);
           crc >>= 8;
       }
    }
    if (frameend == ZCRCW)
        zsend_byte(XON);

    return;
}

/* receive data,with 16bits CRC check */
static rt_int16_t zrec_data16(rt_uint8_t *buf, rt_uint16_t len)
{
    rt_int16_t c,crc_cnt;
    rt_uint16_t crc;
    rt_err_t res = -RT_ERROR;
    rt_uint8_t *p,flag = 0;

    p = buf;
    crc_cnt = 0;  crc = 0L;
    Rxcount = 0;
    while(buf <= p+len)
    {
        if ((res = zread_byte()) & ~0377)
        {
            if (res == GOTCRCE || res == GOTCRCG ||
                res == GOTCRCQ || res == GOTCRCW)
            {
                  c = res;
                  c = res;
                  crc = updcrc16(res&0377, crc);
                  flag = 1;
                  continue;
            }
            else if (res == GOTCAN)  return ZCAN;
            else if (res == TIMEOUT) return TIMEOUT;
            else return res;

        }
        else
        {
           if (flag)
           {
               crc = updcrc16(res, crc);
               crc_cnt++;
               if (crc_cnt < 2) continue;
               if ((crc & 0xffff))
               {
#ifdef ZDEBUG
                     rt_kprintf("error code: CRC16 error \r\n");
#endif
                     return -RT_ERROR;
               }
               return c;
           }
           else
           {
              *buf++ = res;
              Rxcount++;
              crc = updcrc16(res, crc);
           }
        }
    }

    return -RT_ERROR;
}

/* receive data,with 32bits CRC check */
static rt_int16_t zrec_data32(rt_uint8_t *buf, rt_int16_t len)
{
    rt_int16_t c,crc_cnt;
    rt_uint32_t crc;
    rt_err_t res = -RT_ERROR;
    rt_uint8_t *p,flag = 0;
    p = buf;
    crc_cnt = 0;   crc = 0xffffffffL;
    Rxcount = 0;
    while (buf <= p+len)
    {
        if ((res = zread_byte()) & ~0377)
        {
            if (res == GOTCRCE || res == GOTCRCG ||
                res == GOTCRCQ || res == GOTCRCW)
            {
                  c = res;
                  crc = updcrc32(res&0377, crc);
                  flag = 1;
                  continue;
            }
            else if (res == GOTCAN)  return ZCAN;
            else if (res == TIMEOUT) return TIMEOUT;
            else return res;

        }
        else
        {
           if (flag)
           {
               crc = updcrc32(res, crc);
               crc_cnt++;
               if (crc_cnt < 4) continue;
               if ((crc & 0xDEBB20E3))
               {
#ifdef ZDEBUG
                     rt_kprintf("error code: CRC32 error \r\n");
#endif
                     return -RT_ERROR;
               }
               return c;
           }
           else
           {
              *buf++ = res;
              Rxcount++;
              crc = updcrc32(res, crc);
           }
        }
    }

    return -RT_ERROR;
}
/* receive data,with RLE encoded,32bits CRC check */
static rt_int16_t zrec_data32r(rt_uint8_t *buf, rt_int16_t len)
{
    rt_int16_t c,crc_cnt;
    rt_uint32_t crc;
    rt_err_t res = -RT_ERROR;
    rt_uint8_t *p,flag = 0;

    crc_cnt = 0; crc = 0xffffffffL;
    Rxcount = 0;
    p = buf;
    while (buf <= p+len)
    {
        if ((res = zread_byte()) & ~0377)
        {
            if (res == GOTCRCE || res == GOTCRCG ||
                res == GOTCRCQ || res == GOTCRCW)
            {
                  c = res;
                  crc = updcrc32(res&0377, crc);
                  flag = 1;
                  continue;
            }
            else if (res == GOTCAN)  return ZCAN;
            else if (res == TIMEOUT) return TIMEOUT;
            else return res;

        }
        else
        {
           if (flag)
           {
               crc = updcrc32(res, crc);
               crc_cnt++;
               if (crc_cnt < 4) continue;
               if ((crc & 0xDEBB20E3))
               {
#ifdef ZDEBUG
                     rt_kprintf("error code: CRC32 error \r\n");
#endif
                     return -RT_ERROR;
               }
               return c;
           }
           else
           {
               crc = updcrc32(res, crc);
               switch (c)
               {
               case 0:
                    if (res == ZRESC)
                    {
                        c = -1;  continue;
                    }
                    *buf++ = res;
                    Rxcount++;
                    continue;
               case -1:
                    if (res >= 040 && res < 0100)
                    {
                        c = res - 035; res = 040;
                        goto spaces;
                    }
                    if (res == 0100)
                    {
                        c = 0;
                        *buf++ = ZRESC;
                        Rxcount++;
                        continue;
                    }
                    c = res;  continue;
               default:
                    c -= 0100;
                    if (c < 1)
                        goto end;
spaces:
                    if ((buf + c) > p+len)
                        goto end;
                    while ( --res >= 0)
                    {
                        *buf++ = res;
                        Rxcount++;
                    }
                    c = 0;  continue;
                }
           }
        }   // if -else

    }
end:
    return -RT_ERROR;
}
rt_int16_t zget_data(rt_uint8_t *buf, rt_uint16_t len)
{
    rt_int16_t res = -RT_ERROR;

    if (RxCRC == 0)
    {
         res = zrec_data16(buf,len);
    }
    else if (RxCRC == 1)
    {
        res = zrec_data32(buf, len);
    }
    else if (RxCRC == 2)
    {
        res = zrec_data32r(buf, len);
    }

    return res;
}
/* get type and cmd of header, fix lenght */
rt_int16_t zget_header(rt_uint8_t *hdr)
{
    rt_int16_t c,prev_char;
    rt_uint32_t bit;
    rt_uint16_t get_can,step_out;

    bit = get_device_baud();                 /* get console baud rate */
    Rxframeind = header_type = 0;
    step_out = 0;
    prev_char = 0xff;
    for (;;)
    {
        c = zread_line(100);
        switch(c)
        {
        case 021:
        case 0221:
             if (prev_char == CAN)   break;
             if (prev_char == ZCRCW)  goto start_again;
             break;
        case RCDO:
             goto end;
        case TIMEOUT:
             if (prev_char == CAN) break;
             if (prev_char == ZCRCW)
             {
                 c = -RT_ERROR; goto end;
             }
             goto end;
        case ZCRCW:
             if (prev_char == CAN) goto start_again;
             break;
        case CAN:
get_can:
             if (++get_can > 5)
             {
                 c = ZCAN; goto end;
             }
             break;
        case ZPAD:
             if (prev_char == CAN)   break;
             if (prev_char == ZCRCW) goto start_again;
             step_out = 1;
             break;
        default:
             if (prev_char == CAN)   break;
             if (prev_char == ZCRCW) goto start_again;
start_again:
             if (--bit == 0)
             {
                 c = GCOUNT; goto end;
             }
             get_can = 0;
             break;
        }
        prev_char = c;
        if (step_out) break;    /* exit loop */
    }
    step_out = get_can = 0;
    for (;;)
    {
        c = zxor_read();
        switch(c)
        {
        case ZPAD:
             break;
        case RCDO:
        case TIMEOUT:
             goto end;
        case ZDLE:
             step_out = 1;
             break;
        default:
             goto start_again;
        }
        if (step_out) break;
    }

    Rxframeind = c = zxor_read();
    switch (c)
    {
    case ZBIN32:
         RxCRC = 1;  c = zget_bin_fcs(hdr); break;
    case ZBINR32:
         RxCRC = 2;  c = zget_bin_fcs(hdr); break;
    case ZBIN:
         RxCRC = 0;  c = zget_bin_header(hdr); break;
    case ZHEX:
         RxCRC = 0;  c = zget_hex_header(hdr); break;
    case CAN:
        goto get_can;
    case RCDO:
    case TIMEOUT:
        goto end;
    default:
        goto start_again;
    }
end:
    return c;
}

/* receive a binary header */
static rt_int16_t zget_bin_header(rt_uint8_t *hdr)
{
    rt_int16_t res, i;
    rt_uint16_t crc;

    if ((res = zread_byte()) & ~0377)
        return res;
    header_type = res;
    crc = updcrc16(res, 0);

    for (i=0;i<4;i++)
    {
        if ((res = zread_byte()) & ~0377)
            return res;
        crc = updcrc16(res, crc);
        *hdr++ = res;
    }
    if ((res = zread_byte()) & ~0377)
        return res;
    crc = updcrc16(res, crc);
    if ((res = zread_byte()) & ~0377)
        return res;
    crc = updcrc16(res, crc);
    if (crc & 0xFFFF)
    {
        rt_kprintf("CRC error\n");
        return -RT_ERROR;
    }

    return header_type;
}

/* receive a binary header,with 32bits FCS */
static rt_int16_t zget_bin_fcs(rt_uint8_t *hdr)
{
    rt_int16_t res, i;
    rt_uint32_t crc;

    if ((res = zread_byte()) & ~0377)
        return res;
    header_type = res;
    crc = 0xFFFFFFFFL;
    crc = updcrc32(res, crc);

    for (i=0;i<4;i++)    /* 4headers */
    {
        if ((res = zread_byte()) & ~0377)
            return res;
        crc = updcrc32(res, crc);
        *hdr++ = res;

    }
    for (i=0;i<4;i++)   /* 4bytes crc */
    {
        if ((res = zread_byte()) & ~0377)
            return res;
        crc = updcrc32(res, crc);

    }
    if (crc != 0xDEBB20E3)
    {
#ifdef ZDEBUG
        rt_kprintf("CRC error\n");
#endif
        return -RT_ERROR;
    }

    return header_type;
}


/* receive a hex style header (type and position) */
rt_int16_t zget_hex_header(rt_uint8_t *hdr)
{
    rt_int16_t res,i;
    rt_uint16_t crc;

    if ((res = zget_hex()) < 0)
        return res;
    header_type = res;
    crc = updcrc16(res, 0);

    for (i=0;i<4;i++)
    {
        if ((res = zget_hex()) < 0)
            return res;
        crc = updcrc16(res, crc);
        *hdr++ = res;
    }
    if ((res = zget_hex()) < 0)
        return res;
    crc = updcrc16(res, crc);
    if ((res = zget_hex()) < 0)
        return res;
    crc = updcrc16(res, crc);
    if (crc & 0xFFFF)
    {
#ifdef ZDEBUG
        rt_kprintf("error code : CRC error\r\n");
#endif
        return -RT_ERROR;
    }
    res = zread_line(100);
    if (res < 0)
        return res;
    res = zread_line(100);
    if (res < 0)
        return res;

    return header_type;
}

/* convert to ascii */
static void zsend_ascii(rt_uint8_t c)
{
    const char hex[] = "0123456789abcdef";

    zsend_line(hex[(c&0xF0)>>4]);
    zsend_line(hex[(c)&0xF]);

    return;
}

/*
 * aend character c with ZMODEM escape sequence encoding.
 */
void zsend_zdle_char(rt_uint16_t ch)
{
    rt_uint16_t res;

    res = ch & 0377;
    switch (res)
    {
    case 0377:
        zsend_byte(res);
        break;
    case ZDLE:
        zsend_byte(ZDLE);
        res ^= 0100;
        zsend_byte(res);
        break;
    case 021:
    case 023:
    case 0221:
    case 0223:
        zsend_byte(ZDLE);
        res ^= 0100;
        zsend_byte(res);
        break;
    default:
        zsend_byte(res);
    }
}

/* decode two lower case hex digits into an 8 bit byte value */
static rt_int16_t zget_hex(void)
{
    rt_int16_t res,n;

    if ((res = zxor_read()) < 0)
        return res;
    n = res - '0';
    if (n > 9)
        n -= ('a' - ':');
    if (n & ~0x0f)
        return -RT_ERROR;
    if ((res = zxor_read()) < 0)
        return res;
    res -= '0';
    if (res > 9)
        res -= ('a' - ':');
    if (res & ~0x0f)
        return -RT_ERROR;
    res += (n<<4);

    return res;
}


/*
 * read a byte, checking for ZMODEM escape encoding
 *  including CAN*5 which represents a quick abort
 */
rt_int16_t zread_byte(void)
{
    register int res;

again:
    /* Quick check for non control characters */
    if ((res = zread_line(100)) & 0140)
        return res;
    switch (res)
    {
    case ZDLE:
        break;
    case 023:
    case 0223:
    case 021:
    case 0221:
        goto again;
    default:
        return res;
    }
again2:
    if ((res = zread_line(100)) < 0)
        return res;
    if (res == CAN && (res = zread_line(100)) < 0)
        return res;
    if (res == CAN && (res = zread_line(100)) < 0)
        return res;
    if (res == CAN && (res = zread_line(100)) < 0)
        return res;
    switch (res)
    {
    case CAN:
         return GOTCAN;
    case ZCRCE:
    case ZCRCG:
    case ZCRCQ:
    case ZCRCW:
         return (res | GOTOR);
    case ZRUB0:
         return 0177;
    case ZRUB1:
         return 0377;
    case 023:
    case 0223:
    case 021:
    case 0221:
         goto again2;
    default:
         if ((res & 0140) ==  0100)
            return (res ^ 0100);
         break;
    }

    return -RT_ERROR;
}

/*
 * @read a character from the modem line with timeout.
 * @eat parity, XON and XOFF characters.
 */
rt_int16_t zxor_read(void)
{
    rt_int16_t res;

    for (;;)
    {
        if ((res = zread_line(100)) < 0)
            return res;
        switch (res &= 0177) {
        case XON:
        case XOFF:
            continue;
        case '\r':
        case '\n':
        case ZDLE:
        default:
            return res;
        }
    }

}

/* put file posistion into the header*/
void zput_pos(rt_uint32_t pos)
{
    tx_header[ZP0] = pos;
    tx_header[ZP1] = pos>>8;
    tx_header[ZP2] = pos>>16;
    tx_header[ZP3] = pos>>24;

    return;
}

/* Recover a long integer from a header */
void zget_pos(rt_uint32_t pos)
{
    Rxpos = (rx_header[ZP3] & 0377);
    Rxpos = (Rxpos << 8) | (rx_header[ZP2] & 0377);
    Rxpos = (Rxpos << 8) | (rx_header[ZP1] & 0377);
    Rxpos = (Rxpos << 8) | (rx_header[ZP0] & 0377);

    return;
}

/* end of zcore.c */
