/* * Z M . C * ZMODEM protocol primitives * 01-19-87 Chuck Forsberg Omen Technology Inc * * Major overhaul by Rick Huebner for adaptation to Amiga XPR protocol spec */ #include "aztec.h" #include "xproto.h" #include "zmodem.h" #include "defs.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(v,type) register struct Vars *v; register USHORT type; { register UBYTE *hdr = v->Txhdr; register short n; register USHORT crc; #ifdef DEBUG 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,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,crc>>8); zsendline(v,crc); } /* Send ZMODEM HEX header hdr of type type */ void zshhdr(v,type) register struct Vars *v; register USHORT type; { register UBYTE *hdr = v->Txhdr; register short n; register USHORT crc; #ifdef DEBUG 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,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,crc>>8); zputhex(v,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(v,length,frameend) register struct Vars *v; register short length; register USHORT frameend; { register UBYTE *buf = v->Pktbuf; register USHORT crc; #ifdef DEBUG sprintf(v->Msgbuf,"zsdata: length=%d end=%x\n",length,frameend); dlog(v,v->Msgbuf); #endif crc = 0; for (;--length >= 0;) { zsendline(v,*buf); crc = updcrc(((USHORT)(*buf++)), crc); } xsendline(v,ZDLE); xsendline(v,frameend); crc = updcrc(frameend, crc); crc = updcrc(((USHORT)0),crc); crc = updcrc(((USHORT)0),crc); zsendline(v,crc>>8); zsendline(v,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(v,buf,length) register struct Vars *v; register UBYTE *buf; register short length; { register short c, d; register 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 DEBUG sprintf(v->Msgbuf,"zrdata: cnt = %d ret = %x\n",v->Rxcount,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(v) register struct Vars *v; { register short c, cancount; register long n; #ifdef DEBUG 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 DEBUG default: if (c >= -3 && c <= FRTYPES) sprintf(msgbuf,"zgethdr: %s @ %ld\n",frametypes[c+FTOFFSET],v->Rxpos); else sprintf(msgbuf,"zgethdr: Unknown type %d @ %ld\n",c,v->Rxpos); dlog(v,msgbuf); #endif } return c; } /* Receive a binary style header (type and position) */ short zrbhdr(v) register struct Vars *v; { register UBYTE *hdr = v->Rxhdr; register short c, n; register 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(v) register struct Vars *v; { register UBYTE *hdr = v->Rxhdr; register short c, n; register 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(v,c) register struct Vars *v; register short 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(v,c) register struct Vars *v; register short c; { switch (c & 0xFF) { 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(v) register struct Vars *v; { register 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 (n<<4 | c); } /* Read a byte, checking for ZMODEM escape encoding including CAN*5 which represents a quick abort */ short zdlread(v) register struct Vars *v; { register 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 (c | GOTOR); case ZRUB0: return 0x7F; case ZRUB1: return 0xFF; default: if ((c & 0x60) == 0x40) return (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(v) register struct Vars *v; { register 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(v,pos) register struct Vars *v; register 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(v) register struct Vars *v; { register 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; }