/* * Z M . C * ZMODEM protocol primitives * 01-19-87 Chuck Forsberg Omen Technology Inc * * 29 July 89: * Major overhaul by Rick Huebner for adaptation to Amiga XPR protocol spec * * 28 October 89: * Converted to Lattice C 5.04 */ #include #include #include #include #include #include "xproto.h" #include "zmodem.h" #include "xprzmodem.h" #include "zcrc.h" static char *frametypes[] = { "Carrier Lost", /* -3 */ "TIMEOUT", /* -2 */ "ERROR", /* -1 */ #define FTOFFSET 3 "ZRQINIT", "ZRINIT", "ZSINIT", "ZACK", "ZFILE", "ZSKIP", "ZNAK", "ZABORT", "ZFIN", "ZRPOS", "ZDATA", "ZEOF", "ZFERR", "ZCRC", "ZCHALLENGE", "ZCOMPL", "ZCAN", "ZFREECNT", "ZCOMMAND", "ZSTDERR", "xxxxx" #define FRTYPES 22 /* Total number of frame types in this array */ /* not including psuedo negative entries */ }; /* Send ZMODEM binary header hdr of type type */ void zsbhdr(struct Vars *v,USHORT type) { UBYTE *hdr = v->Txhdr; short n; USHORT crc; #ifdef DEBUGLOG sprintf(v->Msgbuf,"zsbhdr: %s %lx\n",frametypes[type+FTOFFSET],v->Txpos); dlog(v,v->Msgbuf); #endif xsendline(v,ZPAD); xsendline(v,ZDLE); xsendline(v,ZBIN); zsendline(v,(UBYTE)type); crc = updcrc(type, 0); for (n=4; --n >= 0;) { zsendline(v,*hdr); crc = updcrc(((USHORT)(*hdr++)), crc); } crc = updcrc(((USHORT)0),crc); crc = updcrc(((USHORT)0),crc); zsendline(v,(UBYTE)(crc>>8)); zsendline(v,(UBYTE)crc); } /* Send ZMODEM HEX header hdr of type type */ void zshhdr(struct Vars *v,USHORT type) { UBYTE *hdr = v->Txhdr; short n; USHORT crc; #ifdef DEBUGLOG sprintf(v->Msgbuf,"zshhdr: %s %lx\n",frametypes[type+FTOFFSET],v->Rxbytes); dlog(v,v->Msgbuf); #endif sendline(v,ZPAD); sendline(v,ZPAD); sendline(v,ZDLE); sendline(v,ZHEX); zputhex(v,(UBYTE)type); crc = updcrc(type, 0); for (n=4; --n >= 0;) { zputhex(v,*hdr); crc = updcrc(((USHORT)(*hdr++)), crc); } crc = updcrc(((USHORT)0),crc); crc = updcrc(((USHORT)0),crc); zputhex(v,(UBYTE)(crc>>8)); zputhex(v,(UBYTE)crc); /* Make it printable on remote machine */ sendline(v,'\r'); sendline(v,'\n'); /* Uncork the remote in case a fake XOFF has stopped data flow */ if (type != ZFIN) sendline(v,XON); } /* Send binary array buf of length length, with ending ZDLE sequence frameend */ void zsdata(struct Vars *v,short length,USHORT frameend) { UBYTE *buf, *zsdataptr, c; USHORT crc; #ifdef DEBUGLOG sprintf(v->Msgbuf,"zsdata: length=%ld end=%lx\n",(long)length,(long)frameend); dlog(v,v->Msgbuf); #endif buf = v->Pktbuf; zsdataptr = v->Zsdatabuf; crc = 0; for (;--length >= 0;) { switch (c = *buf) { case CR: case CR|0x80: if (v->Lastzsent != '@') goto sendit; /* Fallthrough */ case ZDLE: case DLE: case XON: case XOFF: case DLE|0x80: case XON|0x80: case XOFF|0x80: *zsdataptr++ = ZDLE; c ^= 0x40; sendit: default: *zsdataptr++ = v->Lastzsent = c; } crc = updcrc(((USHORT)(*buf++)), crc); } *zsdataptr++ = ZDLE; *zsdataptr++ = frameend; crc = updcrc(frameend, crc); (*v->io.xpr_swrite)(v->Zsdatabuf,(long)(zsdataptr - v->Zsdatabuf)); crc = updcrc(((USHORT)0),crc); crc = updcrc(((USHORT)0),crc); zsendline(v,(UBYTE)(crc>>8)); zsendline(v,(UBYTE)crc); if (frameend == ZCRCW) xsendline(v,XON); } /* Receive array buf of max length with ending ZDLE sequence and CRC. Returns the ending character or error code. */ short zrdata(struct Vars *v,UBYTE *buf,short length) { short c, d; USHORT crc; crc = v->Rxcount = 0; for (;;) { if ((c = zdlread(v)) & ~0xFF) { crcfoo: switch (c) { case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: crc = updcrc(((d=c)&0xFF), crc); if ((c = zdlread(v)) & ~0xFF) goto crcfoo; crc = updcrc(c, crc); if ((c = zdlread(v)) & ~0xFF) goto crcfoo; crc = updcrc(c, crc); if (crc & 0xFFFF) { strcpy(v->Msgbuf,"Bad data packet CRC "); return ERROR; } #ifdef DEBUGLOG sprintf(v->Msgbuf,"zrdata: cnt = %ld ret = %lx\n",(long)v->Rxcount,(long)d); dlog(v,v->Msgbuf); #endif return d; case GOTCAN: return ZCAN; case TIMEOUT: strcpy(v->Msgbuf,"Data packet timeout "); return c; case RCDO: return c; default: strcpy(v->Msgbuf,"Unrecognizable data packet "); return c; } } if (--length < 0) { strcpy(v->Msgbuf,"Data packet too long "); return ERROR; } ++v->Rxcount; *buf++ = c; crc = updcrc(c, crc); continue; } } /* Read a ZMODEM header to hdr, either binary or hex. On success return type of header. Otherwise return negative on error. */ short zgethdr(struct Vars *v) { short c, cancount; long n; #ifdef DEBUGLOG UBYTE msgbuf[128]; #endif n = v->Baud; /* Max characters before start of frame */ cancount = 5; again: v->Rxframeind = v->Rxtype = 0; switch (c = noxrd7(v)) { case RCDO: case TIMEOUT: goto fifi; case CAN: if (--cancount <= 0) { c = ZCAN; goto fifi; } default: agn2: if (--n <= 0) { strcpy(v->Msgbuf,"Header search garbage count exceeded "); return ERROR; } if (c != CAN) cancount = 5; goto again; case ZPAD: /* This is what we want. */ break; } cancount = 5; splat: switch (c = noxrd7(v)) { case ZPAD: goto splat; case RCDO: case TIMEOUT: goto fifi; default: goto agn2; case ZDLE: /* This is what we want. */ break; } switch (c = noxrd7(v)) { case RCDO: case TIMEOUT: goto fifi; case ZBIN: v->Rxframeind = ZBIN; c = zrbhdr(v); break; case ZHEX: v->Rxframeind = ZHEX; c = zrhhdr(v); break; case CAN: if (--cancount <= 0) { c = ZCAN; goto fifi; } goto agn2; default: goto agn2; } v->Rxpos = rclhdr(v); fifi: switch (c) { case GOTCAN: c = ZCAN; case ZNAK: case ZCAN: case ERROR: case TIMEOUT: case RCDO: sprintf(v->Msgbuf,"%s %s ", frametypes[c+FTOFFSET], (c >= 0) ? "header" : "error"); #ifdef DEBUGLOG default: if (c >= -3 && c <= FRTYPES) sprintf(msgbuf,"zgethdr: %s @ %ld\n",frametypes[c+FTOFFSET],v->Rxpos); else sprintf(msgbuf,"zgethdr: Unknown type %ld @ %ld\n",(long)c,v->Rxpos); dlog(v,msgbuf); #endif } return c; } /* Receive a binary style header (type and position) */ short zrbhdr(struct Vars *v) { UBYTE *hdr = v->Rxhdr; short c, n; USHORT crc; if ((c = zdlread(v)) & ~0xFF) return c; v->Rxtype = c; crc = updcrc(c, 0); for (n=4; --n >= 0;) { if ((c = zdlread(v)) & ~0xFF) return c; crc = updcrc(c, crc); *hdr++ = c; } if ((c = zdlread(v)) & ~0xFF) return c; crc = updcrc(c, crc); if ((c = zdlread(v)) & ~0xFF) return c; crc = updcrc(c, crc); if (crc & 0xFFFF) { strcpy(v->Msgbuf,"Bad Header CRC "); return ERROR; } return v->Rxtype; } /* Receive a hex style header (type and position) */ short zrhhdr(struct Vars *v) { UBYTE *hdr = v->Rxhdr; short c, n; USHORT crc; if ((c = zgethex(v)) < 0) return c; v->Rxtype = c; crc = updcrc(c, 0); for (n=4; --n >= 0;) { if ((c = zgethex(v)) < 0) return c; crc = updcrc(c, crc); *hdr++ = c; } if ((c = zgethex(v)) < 0) return c; crc = updcrc(c, crc); if ((c = zgethex(v)) < 0) return c; crc = updcrc(c, crc); if (crc & 0xFFFF) { strcpy(v->Msgbuf,"Bad Header CRC "); return ERROR; } if (readock(v,1) == '\r') readock(v,1); /* Throw away possible cr/lf */ return v->Rxtype; } /* Send a byte as two hex digits */ void zputhex(struct Vars *v,UBYTE c) { static char digits[] = "0123456789abcdef"; sendline(v,digits[(c>>4) & 0x0F]); sendline(v,digits[c & 0x0F]); } /* Send character c with ZMODEM escape sequence encoding. Escape ZDLE, real DLE, XON, XOFF, and CR following @ (Telenet net escape) */ void zsendline(struct Vars *v,UBYTE c) { switch (c) { case CR: case CR|0x80: if (v->Lastzsent != '@') goto sendit; /* Fallthrough */ case ZDLE: case DLE: case XON: case XOFF: case DLE|0x80: case XON|0x80: case XOFF|0x80: xsendline(v,ZDLE); c ^= 0x40; sendit: default: xsendline(v,v->Lastzsent = c); } } /* Decode two lower case hex digits into an 8 bit byte value */ short zgethex(struct Vars *v) { short c, n; if ((n = noxrd7(v)) < 0) return n; n -= '0'; if (n > 9) n -= ('a' - ':'); if (n & ~0xF) return ERROR; if ((c = noxrd7(v)) < 0) return c; c -= '0'; if (c > 9) c -= ('a' - ':'); if (c & ~0xF) return ERROR; return (short)(n<<4 | c); } /* Read a byte, checking for ZMODEM escape encoding including CAN*5 which represents a quick abort */ short zdlread(struct Vars *v) { short c; if ((c = readock(v,v->Rxtimeout)) != ZDLE) return c; if ((c = readock(v,v->Rxtimeout)) < 0) return c; if (c == CAN && (c = readock(v,v->Rxtimeout)) < 0) return c; if (c == CAN && (c = readock(v,v->Rxtimeout)) < 0) return c; if (c == CAN && (c = readock(v,v->Rxtimeout)) < 0) return c; switch (c) { case CAN: return GOTCAN; case ZCRCE: case ZCRCG: case ZCRCQ: case ZCRCW: return (short)(c | GOTOR); case ZRUB0: return 0x7F; case ZRUB1: return 0xFF; default: if ((c & 0x60) == 0x40) return (short)(c ^ 0x40); break; } strcpy(v->Msgbuf,"Bad ZMODEM escape sequence "); return ERROR; } /* Read a character from the modem line with timeout. Eat parity, XON and XOFF characters. */ short noxrd7(struct Vars *v) { short c; for (;;) { if ((c = readock(v,v->Rxtimeout)) < 0) return c; switch (c &= 0x7F) { case XON: case XOFF: continue; default: return c; } } } /* Store long integer pos in Txhdr */ void stohdr(struct Vars *v,long pos) { v->Txhdr[ZP0] = pos; pos >>= 8; v->Txhdr[ZP1] = pos; pos >>= 8; v->Txhdr[ZP2] = pos; pos >>= 8; v->Txhdr[ZP3] = pos; } /* Recover a long integer from a header */ long rclhdr(struct Vars *v) { long l; l = v->Rxhdr[ZP3]; l = (l << 8) | v->Rxhdr[ZP2]; l = (l << 8) | v->Rxhdr[ZP1]; l = (l << 8) | v->Rxhdr[ZP0]; return l; }