/* * File : rz.c * the core functions of implementing zmodem protocol * Change Logs: * Date Author Notes * 2011-03-29 itspy */ #include #include #include #include #include #include #include #include #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>8); zsend_zdle_char(crc); } else if (TxCRC == 1) /* send binary data with 32 bits crc check */ { crc = 0xffffffffL; for (i=0;i>= 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; 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 */