home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
TELECOM
/
xyz.lzh
/
ftxy.c
< prev
next >
Wrap
Text File
|
1995-08-18
|
40KB
|
1,448 lines
/*
Printed form of this source is Copyright (C) 1995 Coriolis
Group, Inc. All rights reserved. Individual users may
make printed copies for their own personal use.
All other forms are Copyright (C) 1995 Tim Kientzle. All
rights reserved.
Redistribution in source or binary form is permitted only under
the following conditions:
1. If you own a copy of `The Working Programmer's Guide To Serial
Protocols,' then you may redistribute this code as part of
a complete application program under the conditions
described in that book. (See pages xiv, xv.) In any case,
you must abide by terms 4-7 below.
2. Otherwise, if you have received this code as a part of an
application program, it may only be redistributed with the
complete source of that program, under whatever conditions
apply to redistribution of that program as a whole.
3. If you have received this source code by some other means,
you may not redistribute it without explicit written
permission from Tim Kientzle.
4. All advertising materials mentioning features or use of this
software must prominently display the following acknowledgement:
This product is partially based on source code appearing in
`The Working Programmer's Guide to Serial Protocols,'
Copyright (C) 1995 Coriolis Group, Inc. and Tim Kientzle.
5. All programs using this source code must display the above
acknowledgement prominently in the program documentation
and user interface.
6. Neither the name of the Tim Kientzle nor the Coriolis Group, Inc.,
may be used to endorse or promote products derived from this
software without specific prior written permission.
7. Any redistribution in source form must retain the above copyright
notice, this list of conditions, and the disclaimer below.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL TIM KIENTZLE OR THE CORIOLIS GROUP BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define SOH (0x01)
#define STX (0x02)
#define EOT (0x04)
#define ACK (0x06)
#define NAK (0x15)
#define CAN (0x18)
#define SUB (0x1A)
#define StsRet(expr) \
do{ \
int tmpErrorVal= (expr); \
if(tmpErrorVal!=xyOK)return StsWarn(tmpErrorVal); \
}while(FALSE)
#define debugWarn (1)
#define debugPacket (2)
#define debugAttr (32)
#define debugInit (64)
#define StsWarn(s)((pXY->debug)?XYDebugWarn(pXY,(s),__FILE__,__LINE__):(s))
#define ChooseLetter(f,ec,eu,dc,du) \
((f).enabled?((f).certain?ec:eu):((f).certain?dc:du))
#include <stddef.h>
#include <stdio.h>
#ifdef _UCC
#include <stdlib.h>
#include <string.h>
#else
int sprintf ();
#endif
#include <time.h>
#include "ftdebug.h"
#include "ftprog.h"
#include "ftdisk.h"
#include "ftserial.h"
#include "ftxy.h"
#ifndef TRUE
#define TRUE (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef NULL
#define NULL ((void *)0)
#endif
#define STATIC
typedef unsigned char BYTE;
typedef struct {
int enabled;
int certain;
} CAPABILITY;
enum {
stsNegotiating = progNegotiating,
stsSending = progSending,
stsReceiving = progReceiving,
stsEnding = progEnding,
stsDone = progDone,
stsEOF = progEOF,
stsNewFile = progNewFile,
stsFailed = progFailed,
stsCancelled = progCancelled
};
typedef struct {
int timeout;
int retries;
volatile int userCancel;
int packetNumber;
unsigned long transferred;
long fileSize;
CAPABILITY crc, longPacket, batch, G;
DEBUG debug;
SERIAL_PORT port;
DISKFILE f;
const char **filenames;
int currentFileName;
int numFileNames;
int fileType;
PROGRESS progress;
} XYMODEM_PRIVATE;
STATIC void
XYNewFile (XYMODEM_PRIVATE *pXY)
{
pXY->fileSize = 0;
pXY->transferred = 0;
}
enum {
xyOK = 0,
xyFail,
xyFailed,
xyBadPacket,
xyEOT,
xyEndOfSession,
xyEOF,
xyTimeout
};
STATIC int XYDebugWarn
(XYMODEM_PRIVATE *pXY, const int s, const char *file, const int line) {
const char *msg;
if (s == xyOK)
return s;
DebugBeginInternal (pXY->debug, debugWarn, file, line);
DebugString (pXY->debug, "!?!?!?:");
switch (s) {
case xyFail:
msg = "xyFail";
break;
case xyFailed:
msg = "xyFailed";
break;
case xyBadPacket:
msg = "xyBadPacket";
break;
case xyEOT:
msg = "xyEOT";
break;
case xyEndOfSession:
msg = "xyEndOfSession";
break;
case xyEOF:
msg = "xyEOF";
break;
case xyTimeout:
msg = "xyTimeout";
break;
default:
DebugString (pXY->debug, "Error ");
DebugInt (pXY->debug, s);
msg = "";
}
DebugString (pXY->debug, msg);
DebugEnd (pXY->debug);
return s;
}
STATIC unsigned int xyCrcTable[256];
STATIC void
XYInitCrc16 (void)
{
static int crcDone = 0;
unsigned i, j, crc;
if (crcDone)
return;
for (i = 0; i < 256; i++) {
crc = (i << 8);
for (j = 0; j < 8; j++)
crc = (crc << 1) ^ ((crc & 0x8000) ? 0x1021 : 0);
xyCrcTable[i] = crc & 0xffff;
}
crcDone = 1;
}
STATIC unsigned
XYCrc16 (void *buff, unsigned int length,
unsigned short crc)
{
unsigned char *p = buff;
while (length-- > 0)
crc = xyCrcTable[((crc >> 8) ^ *p++) & 0xFF] ^ (crc << 8);
return crc & 0xFFFF;
}
STATIC unsigned XYCrc16Constant
(unsigned char c, unsigned int length, unsigned short crc) {
while (length-- > 0)
crc = xyCrcTable[((crc >> 8) ^ c) & 0xFF] ^ (crc << 8);
return crc & 0xFFFF;
}
STATIC void XYTimeToTm
(long s, struct tm *pT) {
long m, h;
int d, M, y;
if (s <= 0) {
time_t t = time (NULL);
*pT = *localtime (&t);
return;
}
m = s / 60;
h = m / 60;
d = h / 24;
y = d / 365;
s %= 60;
m %= 60;
h %= 24;
d %= 365;
d -= (y + 1) / 4;
if (d < 0) {
y--;
d += 365;
}
pT->tm_sec = s;
pT->tm_min = m;
pT->tm_hour = h;
pT->tm_yday = d;
if (((y - 2) % 4 != 0) && (d >= 59))
d++;
if (d >= 60)
d++;
M = (d > 214) ? 7 : 0 + ((d % 214) / 61) * 2 + ((d % 214) % 61) / 31;
d = ((d % 214) % 61) % 31 + 1;
pT->tm_mday = d;
pT->tm_mon = M;
pT->tm_year = y + 70;
pT->tm_isdst = -1;
pT->tm_wday = -1;
}
STATIC long XYTime
(struct tm *pT) {
static const int mon[] =
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
int y = pT->tm_year - 70, M = pT->tm_mon;
int d = pT->tm_mday - 1 + mon[M];
if (((y + 2) % 4 != 0) & (M > 1))
d--;
d += (y + 1) / 4;
return (((((long) y) * 365 + d) * 24 + pT->tm_hour) * 60 + pT->tm_min) * 60
+ pT->tm_sec;
}
STATIC int XYSerialReadWithTimeout
(XYMODEM_PRIVATE *pXY, int timeout, BYTE *pBuffer, unsigned long length) {
int s, returnVal;
if (pXY->userCancel)
return StsWarn (xyFail);
s = SerialReadWithTimeout (pXY->port, timeout, pBuffer, &length);
switch (s) {
case serialOK:
returnVal = xyOK;
break;
case serialTimeout:
returnVal = xyTimeout;
break;
case serialUserCancel:
pXY->userCancel = TRUE;
returnVal = xyFail;
break;
case serialFrame:
returnVal = xyBadPacket;
break;
default:
return StsWarn (xyFailed);
}
if (pXY->userCancel)
return StsWarn (xyFail);
return returnVal;
}
STATIC int XYSerialSend
(XYMODEM_PRIVATE *pXY, const BYTE *pBuffer, unsigned int length) {
int s, returnVal;
if (pXY->userCancel)
return StsWarn (xyFail);
s = SerialSend (pXY->port, pBuffer, length);
switch (s) {
case serialOK:
returnVal = xyOK;
break;
case serialUserCancel:
pXY->userCancel = TRUE;
returnVal = xyFail;
break;
default:
return StsWarn (xyFailed);
}
if (pXY->userCancel)
return StsWarn (xyFail);
return StsWarn (returnVal);
}
STATIC int XYSerialWaitForSentBytes
(XYMODEM_PRIVATE *pXY) {
int s, returnVal;
if (pXY->userCancel)
return StsWarn (xyFail);
s = SerialWaitForSentBytes (pXY->port);
switch (s) {
case serialOK:
returnVal = xyOK;
break;
case serialUserCancel:
pXY->userCancel = TRUE;
returnVal = xyFail;
break;
default:
return StsWarn (xyFailed);
}
if (pXY->userCancel)
return StsWarn (xyFail);
return StsWarn (returnVal);
}
STATIC int XYSendByte
(XYMODEM_PRIVATE *pXY, BYTE b) {
return StsWarn (XYSerialSend (pXY, &b, 1));
}
STATIC int XYSerialReadByte
(XYMODEM_PRIVATE *pXY, int timeout, BYTE *pByte) {
return StsWarn (XYSerialReadWithTimeout (pXY, timeout, pByte, 1));
}
STATIC int
XYGobble (XYMODEM_PRIVATE *pXY, int timeout)
{
int err;
BYTE junk[50];
do {
err = XYSerialReadWithTimeout (pXY, timeout, junk, sizeof (junk));
if (err == xyBadPacket)
err = xyOK;
} while (err == xyOK);
if (err == xyTimeout)
return xyOK;
return StsWarn (err);
}
STATIC int XYFileReadOpenNext
(XYMODEM_PRIVATE *pXY) {
int err;
while (1) {
if (pXY->currentFileName == pXY->numFileNames)
return xyEndOfSession;
err = DiskReadOpen (&pXY->f, pXY->filenames[pXY->currentFileName++],
pXY->fileType);
switch (err) {
case diskOK:
DiskFileSize (pXY->f, &(pXY->fileSize));
return xyOK;
case diskCantRead:
break;
case diskNoSuchFile:
break;
default:
return xyFail;
}
}
}
STATIC int
XYFileRead (XYMODEM_PRIVATE *pXY,
BYTE *pBuffer, unsigned long *pLength)
{
int returnVal;
switch (DiskRead (pXY->f, pBuffer, *pLength, pLength)) {
case diskOK:
returnVal = xyOK;
break;
case diskEOF:
returnVal = xyEOF;
break;
default:
returnVal = xyFail;
break;
}
return returnVal;
}
STATIC int
XYFileReadClose (XYMODEM_PRIVATE *pXY)
{
int returnVal;
switch (DiskReadClose (pXY->f)) {
case diskOK:
returnVal = xyOK;
break;
default:
returnVal = xyFail;
break;
}
pXY->f = NULL;
return returnVal;
}
STATIC int XYFileWriteOpen
(XYMODEM_PRIVATE *pXY, BYTE *pBuffer, unsigned length) {
const char *fileName = (char *) pBuffer;
long fileMode = -1;
struct tm fileDate;
((void) length);
if (fileName == NULL)
fileName = "xymodem.000\0\0";
{
time_t t = time (NULL);
fileDate = *localtime (&t);
}
{
const char *p = fileName;
p += strlen (p) + 1;
pXY->fileSize = -1;
if (*p) {
pXY->fileSize = atoi (p);
while ((*p) && (*p != ' '))
p++;
while (*p == ' ')
p++;
}
if (*p) {
long fileDateSeconds = 0;
while ((*p) && (*p >= '0') && (*p <= '7')) {
fileDateSeconds = fileDateSeconds * 8 + (*p) - '0';
p++;
}
XYTimeToTm (fileDateSeconds, &fileDate);
while ((*p) && (*p != ' '))
p++;
while (*p == ' ')
p++;
}
if (*p) {
fileMode = 0;
while ((*p) && (*p >= '0') && (*p <= '7')) {
fileMode = fileMode * 8 + (*p) - '0';
p++;
}
while ((*p) && (*p != ' '))
p++;
while (*p == ' ')
p++;
}
}
if (DiskWriteInit (&pXY->f, pXY->debug))
return StsWarn (xyFail);
if (DiskWriteName (pXY->f, fileName))
return StsWarn (xyFail);
if (pXY->fileSize >= 0)
if (DiskWriteSize (pXY->f, pXY->fileSize))
return StsWarn (xyFail);
if (DiskWriteDate (pXY->f, &fileDate))
return StsWarn (xyFail);
if (DiskWriteMode (pXY->f, fileMode))
return StsWarn (xyFail);
if (DiskWriteType (pXY->f, diskFileUnknown))
return StsWarn (xyFail);
if (DiskWriteOpen (pXY->f))
return StsWarn (xyFail);
return xyOK;
}
STATIC int XYFileWrite
(XYMODEM_PRIVATE *pXY, const BYTE *pBuffer, unsigned length) {
switch (DiskWrite (pXY->f, pBuffer, length)) {
case diskOK:
return xyOK;
default:
return xyFail;
}
}
STATIC int
XYFileWriteClose (XYMODEM_PRIVATE *pXY)
{
int returnVal;
switch (DiskWriteClose (pXY->f)) {
case diskOK:
returnVal = xyOK;
break;
default:
returnVal = xyFail;
break;
}
pXY->f = NULL;
return returnVal;
}
STATIC void
XYProgress (XYMODEM_PRIVATE *pXY, int status)
{
const char *protocol;
char protoName[15];
if (pXY->debug) {
protoName[0] = ChooseLetter (pXY->crc, 'C', 'c', '-', ' ');
protoName[1] = ChooseLetter (pXY->longPacket, 'K', 'k', '-', ' ');
protoName[2] = ChooseLetter (pXY->batch, 'B', 'b', '-', ' ');
protoName[3] = ChooseLetter (pXY->G, 'G', 'g', '-', ' ');
protoName[4] = 0;
protocol = protoName;
} else {
if (pXY->batch.enabled) {
if (pXY->G.enabled)
protocol = "YModem-G";
else
protocol = "YModem";
} else {
if (pXY->longPacket.enabled)
protocol = "XModem-K";
else if (pXY->crc.enabled)
protocol = "XModem-CRC";
else
protocol = "XModem";
}
}
ProgressProtocol (pXY->progress, protocol);
if (pXY->f) {
const char *fileName = NULL;
int fileType = 0;
DiskFileName (pXY->f, &fileName);
ProgressFileName (pXY->progress, fileName);
DiskFileType (pXY->f, &fileType);
ProgressFileType (pXY->progress, fileType);
} else {
ProgressFileName (pXY->progress, NULL);
}
ProgressFileSize (pXY->progress, pXY->fileSize);
ProgressFilePosition (pXY->progress, pXY->transferred);
ProgressReport (pXY->progress, status);
}
STATIC int
XYSendCAN (XYMODEM_PRIVATE *pXY)
{
static const BYTE cancel[] =
{CAN, CAN, CAN, CAN, CAN, 8, 8, 8, 8, 8};
XYSerialSend (pXY, cancel, sizeof (cancel) / sizeof (cancel[0]));
if (pXY->debug) {
DebugBegin (pXY->debug, debugWarn | debugPacket);
DebugString (pXY->debug, "Sent CAN CAN to cancel transfer.");
DebugEnd (pXY->debug);
}
return xyOK;
}
STATIC int
XYSendPacket (XYMODEM_PRIVATE *pXY,
BYTE *pBuffer, unsigned length)
{
int i;
if (length <= 128)
StsRet (XYSendByte (pXY, SOH));
else
StsRet (XYSendByte (pXY, STX));
StsRet (XYSendByte (pXY, pXY->packetNumber));
StsRet (XYSendByte (pXY, ~pXY->packetNumber));
StsRet (XYSerialSend (pXY, pBuffer, length));
for (i = length; i < 128; i++)
StsRet (XYSendByte (pXY, SUB));
if (i > 128)
for (; i < 1024; i++)
StsRet (XYSendByte (pXY, SUB));
if (pXY->crc.enabled) {
int crc = 0;
crc = XYCrc16 (pBuffer, length, 0);
if (length < 128)
crc = XYCrc16Constant (SUB, 128 - length, crc);
else if (length > 128)
crc = XYCrc16Constant (SUB, 1024 - length, crc);
StsRet (XYSendByte (pXY, crc >> 8));
StsRet (XYSerialWaitForSentBytes (pXY));
StsRet (XYGobble (pXY, 0));
StsRet (XYSendByte (pXY, crc));
} else {
int checksum = 0;
for (i = 0; i < length; i++)
checksum += pBuffer[i];
if (i > 128)
checksum += (1024 - i) * SUB;
else
checksum += (128 - i) * SUB;
StsRet (XYSerialWaitForSentBytes (pXY));
StsRet (XYGobble (pXY, 0));
StsRet (XYSendByte (pXY, checksum));
}
if (pXY->debug) {
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Sent packet: Number:");
DebugInt (pXY->debug, pXY->packetNumber);
DebugString (pXY->debug, " Length:");
DebugUInt (pXY->debug, length);
DebugString (pXY->debug, " ``");
if (length < 40) {
DebugStringCount (pXY->debug, (const char *) pBuffer, length);
DebugString (pXY->debug, "''");
} else {
DebugStringCount (pXY->debug, (const char *) pBuffer, 39);
DebugString (pXY->debug, "...");
}
DebugEnd (pXY->debug);
}
return xyOK;
}
STATIC int
XYSendReadAckNak (XYMODEM_PRIVATE *pXY, BYTE *pResponse)
{
int err;
int canCount = 0;
do {
err = XYSerialReadByte (pXY, pXY->timeout * 5, pResponse);
switch (err) {
case xyOK:
if (*pResponse == CAN) {
DebugBegin (pXY->debug, debugWarn);
DebugString (pXY->debug,
"Received CAN CAN while waiting on response to packet");
DebugEnd (pXY->debug);
if (++canCount >= 2)
return StsWarn (xyFailed);
} else
canCount = 0;
break;
case xyBadPacket:
*pResponse = 0;
break;
default:
return StsWarn (err);
}
} while ((*pResponse != ACK) && (*pResponse != NAK));
if (pXY->debug) {
DebugBegin (pXY->debug, debugPacket);
if (*pResponse == ACK)
DebugString (pXY->debug, "Received ACK for packet ");
else
DebugString (pXY->debug, "Received NAK for packet ");
DebugInt (pXY->debug, pXY->packetNumber);
DebugEnd (pXY->debug);
}
return StsWarn (err);
}
STATIC int
XYReceivePacket (XYMODEM_PRIVATE *pXY,
int *pPacketNumber, BYTE *pBuffer, unsigned *pLength)
{
BYTE startOfPacket = 0;
BYTE packet = 0;
BYTE packetCheck = 0;
int err;
do {
err = XYSerialReadByte (pXY, pXY->timeout, &packetCheck);
if (err != xyBadPacket)
StsRet (err);
} while (err == xyBadPacket);
if (packetCheck == EOT)
return xyEOT;
do {
startOfPacket = packet;
packet = packetCheck;
err = XYSerialReadByte (pXY, pXY->timeout, &packetCheck);
if (err == xyBadPacket) {
startOfPacket = packet = packetCheck = 0;
} else
StsRet (err);
if ((packetCheck == CAN) && (packet == CAN)) {
DebugBegin (pXY->debug, debugWarn | debugPacket);
DebugString (pXY->debug, "Received CAN CAN while waiting for packet");
DebugEnd (pXY->debug);
return StsWarn (xyFailed);
}
} while (((startOfPacket != SOH) && (startOfPacket != STX))
|| (((packet ^ packetCheck) & 0xFF) != 0xFF));
if (startOfPacket == SOH)
*pLength = 128;
else
*pLength = 1024;
StsRet (XYSerialReadWithTimeout (pXY, 2, pBuffer, *pLength));
if (pXY->crc.enabled) {
unsigned crc = XYCrc16 (pBuffer, *pLength, 0);
BYTE crcByte;
int receivedCrc;
StsRet (XYSerialReadByte (pXY, 2, &crcByte));
receivedCrc = (crcByte & 0xFF) << 8;
StsRet (XYSerialReadByte (pXY, 2, &crcByte));
receivedCrc |= (crcByte & 0xFF);
if (crc != receivedCrc)
return StsWarn (xyBadPacket);
} else {
unsigned checksum = 0;
BYTE receivedChecksum;
int length = *pLength;
while (length-- > 0)
checksum += *pBuffer++;
checksum &= 0xFF;
StsRet (XYSerialReadByte (pXY, 2, &receivedChecksum));
if (checksum != receivedChecksum)
return StsWarn (xyBadPacket);
}
if (pXY->debug) {
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Received packet #");
DebugInt (pXY->debug, packet);
DebugString (pXY->debug, " length:");
DebugUInt (pXY->debug, *pLength);
DebugEnd (pXY->debug);
}
*pPacketNumber = packet;
pXY->crc.certain = TRUE;
if (*pLength > 128) {
pXY->longPacket.enabled = TRUE;
pXY->longPacket.certain = TRUE;
}
return xyOK;
}
STATIC int XYSendPacketReliable
(XYMODEM_PRIVATE *pXY, BYTE *pBuffer, unsigned int length) {
int err;
BYTE response = ACK;
do {
StsRet (XYSendPacket (pXY, pBuffer, length));
if (pXY->G.enabled)
return xyOK;
err = XYSendReadAckNak (pXY, &response);
if (err == xyTimeout)
return StsWarn (xyFail);
StsRet (err);
} while (response != ACK);
pXY->crc.certain = TRUE;
if (length > 128) {
pXY->longPacket.enabled = TRUE;
pXY->longPacket.certain = TRUE;
}
return xyOK;
}
STATIC int
XYSendEOTReliable (XYMODEM_PRIVATE *pXY)
{
BYTE b;
int retries = pXY->retries;
int err;
do {
StsRet (XYSendByte (pXY, EOT));
err = XYSerialReadByte (pXY, pXY->timeout, &b);
switch (err) {
case xyOK:
if (pXY->debug) {
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Response to EOT: ");
switch (b) {
case ACK:
DebugString (pXY->debug, "ACK");
break;
case NAK:
DebugString (pXY->debug, "NAK");
break;
default:
DebugChar (pXY->debug, b);
break;
}
DebugEnd (pXY->debug);
}
if (b == ACK)
return xyOK;
else
StsRet (XYGobble (pXY, 3));
break;
case xyTimeout:
case xyBadPacket:
break;
default:
return StsWarn (err);
}
if (retries-- == 0)
return StsWarn (xyFail);
} while (TRUE);
}
STATIC int XYReceivePacketReliable
(XYMODEM_PRIVATE *pXY, int *pPacket, BYTE *pBuffer, unsigned *pLength) {
int err;
int eotCount = 0;
int retries = pXY->retries;
do {
err = XYReceivePacket (pXY, pPacket, pBuffer, pLength);
if (err == xyEOT) {
if (pXY->G.enabled)
return xyEOT;
eotCount += 3;
} else if (err == xyTimeout) {
if (eotCount > 0)
eotCount++;
else if (pXY->G.enabled)
return StsWarn (xyFail);
} else if (err == xyBadPacket) {
eotCount = 0;
if (pXY->G.enabled)
return StsWarn (xyFail);
} else
return StsWarn (err);
if (eotCount >= 6)
return StsWarn (xyEOT);
StsRet (XYSendByte (pXY, NAK));
} while (retries-- > 0);
return StsWarn (xyFail);
}
STATIC int
XYSendPacketZero (XYMODEM_PRIVATE *pXY)
{
const char *fileName = NULL;
long fileSize = -1;
int fileType = diskFileUnknown;
long fileMode = -1;
struct tm fileDate;
BYTE data[1024];
unsigned length = 128;
char *p;
if (pXY->f) {
DiskFileName (pXY->f, &fileName);
fileSize = pXY->fileSize;
DiskFileType (pXY->f, &fileType);
DiskFileMode (pXY->f, &fileMode);
DiskFileDate (pXY->f, &fileDate);
}
memset (data, 0, sizeof (data));
p = (char *) data;
if (fileName && fileName[0]) {
strcpy (p, fileName);
p += strlen (p) + 1;
if ((fileSize >= 0) && (fileType == diskFileBinary)) {
sprintf (p, "%ld", fileSize);
p += strlen (p);
sprintf (p, " %lo", XYTime (&fileDate));
p += strlen (p);
if (fileMode >= 0) {
sprintf (p, " %lo", fileMode);
p += strlen (p);
}
}
}
if (p >= ((char *) data) + 128)
length = 1024;
pXY->packetNumber = 0;
return StsWarn (XYSendPacket (pXY, data, length));
}
STATIC int XYSendReadHandshake
(XYMODEM_PRIVATE *pXY, int timeout, BYTE *pResponse) {
int err;
int canCount = 0;
while (TRUE) {
err = XYSerialReadByte (pXY, timeout, pResponse);
if (err == xyBadPacket)
continue;
if (err != xyOK)
return StsWarn (err);
if (*pResponse == CAN) {
if (++canCount >= 2) {
DebugBegin (pXY->debug, debugWarn | debugPacket);
DebugString (pXY->debug,
"Received CAN CAN while waiting for startup handshake");
DebugEnd (pXY->debug);
return StsWarn (xyFailed);
}
} else
canCount = 0;
switch (*pResponse) {
case 'G':
if ((pXY->crc.certain) && (!pXY->crc.enabled))
break;
if ((pXY->batch.certain) && (!pXY->batch.enabled))
break;
if ((pXY->G.certain) && (!pXY->G.enabled))
break;
pXY->crc.enabled = TRUE;
pXY->batch.enabled = TRUE;
pXY->G.enabled = TRUE;
return xyOK;
case 'C':
if (!pXY->G.certain)
pXY->G.enabled = FALSE;
if (pXY->G.enabled)
break;
if (pXY->crc.enabled)
return xyOK;
if (!pXY->crc.certain)
pXY->crc.enabled = TRUE;
if (!pXY->crc.enabled)
break;
if (!pXY->batch.certain)
pXY->batch.enabled = TRUE;
if (!pXY->longPacket.certain)
pXY->longPacket.enabled = pXY->batch.enabled;
return xyOK;
case NAK:
if (!pXY->G.certain)
pXY->G.enabled = FALSE;
if (pXY->G.enabled)
break;
if (!pXY->crc.enabled)
return xyOK;
if (!pXY->crc.certain)
pXY->crc.enabled = FALSE;
if (pXY->crc.enabled)
break;
if (!pXY->batch.certain)
pXY->batch.enabled = FALSE;
if (pXY->batch.enabled)
break;
if (!pXY->longPacket.certain)
pXY->longPacket.enabled = FALSE;
return xyOK;
case ACK:
return xyOK;
default:
break;
}
}
}
STATIC int XYSendFirstPacket
(XYMODEM_PRIVATE *pXY, BYTE *pBuffer, int bufferLength) {
int totalRetries = pXY->retries;
int retries = pXY->retries / 3 + 1;
BYTE firstHandshake;
BYTE acknowledge;
BYTE handshake;
int handshakeErr = xyOK;
unsigned long dataLength = 0;
((void) bufferLength);
do {
handshakeErr = XYSendReadHandshake (pXY, pXY->timeout * 5, &firstHandshake);
if (handshakeErr == xyTimeout)
return StsWarn (xyFail);
StsRet (handshakeErr);
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Initial handshake ");
DebugChar (pXY->debug, firstHandshake);
DebugEnd (pXY->debug);
} while (firstHandshake == ACK);
do {
if (pXY->batch.enabled) {
StsRet (XYSendPacketZero (pXY));
} else {
if (dataLength == 0) {
dataLength = (pXY->longPacket.enabled) ? 1024 : 128;
StsRet (XYFileRead (pXY, pBuffer, &dataLength));
}
pXY->packetNumber = 1;
XYProgress (pXY, stsNewFile);
StsRet (XYSendPacket (pXY, pBuffer, dataLength));
}
StsRet (XYSendReadHandshake (pXY, pXY->timeout, &acknowledge));
if ((acknowledge == ACK) && (pXY->batch.enabled)) {
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Received ACK for batch file header");
DebugEnd (pXY->debug);
do {
handshakeErr = XYSendReadHandshake (pXY, pXY->timeout, &handshake);
if (handshakeErr == xyOK) {
DebugBegin (pXY->debug, debugWarn);
DebugString (pXY->debug, "Second handshake ");
DebugChar (pXY->debug, handshake);
if (handshake != firstHandshake)
DebugString (pXY->debug, " doesn't match initial handshake");
DebugEnd (pXY->debug);
}
} while ((handshakeErr == xyOK) && (handshake != firstHandshake));
if ((handshakeErr != xyOK) && (handshakeErr != xyTimeout))
return StsWarn (handshakeErr);
} else if ((pXY->G.enabled) && (acknowledge == 'G')) {
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Saw second 'G' handshake");
DebugEnd (pXY->debug);
acknowledge = ACK;
} else {
firstHandshake = acknowledge;
}
if ((acknowledge != ACK)
|| (pXY->batch.enabled && (handshakeErr != xyOK))
) {
if (retries-- == 0) {
if (!pXY->batch.certain)
pXY->batch.enabled = !pXY->batch.enabled;
if (!pXY->longPacket.certain)
pXY->longPacket.enabled = pXY->batch.enabled;
retries = 2;
}
if (totalRetries-- == 0)
return StsWarn (xyFail);
}
} while ((acknowledge != ACK)
|| (pXY->batch.enabled && (handshakeErr != xyOK))
);
pXY->batch.certain = TRUE;
pXY->G.certain = TRUE;
if (pXY->packetNumber == 0)
XYProgress (pXY, stsNewFile);
if ((pXY->packetNumber == 0) && (dataLength > 0)) {
pXY->packetNumber++;
StsRet (XYSendPacketReliable (pXY, pBuffer, dataLength));
pXY->transferred += dataLength;
}
return xyOK;
}
STATIC int
XYSendFile (XYMODEM_PRIVATE *pXY)
{
BYTE data[1024];
unsigned long dataLength;
int err = xyOK;
XYProgress (pXY, stsNegotiating);
StsRet (XYSendFirstPacket (pXY, data, sizeof (data) / sizeof (data[0])));
while (err == xyOK) {
unsigned packetLength;
BYTE *p = data;
dataLength = (pXY->longPacket.enabled) ? 1024 : 128;
err = XYFileRead (pXY, data, &dataLength);
packetLength = (dataLength > 767) ? 1024 : 128;
while ((err == xyOK) && (dataLength > 0)) {
pXY->packetNumber++;
XYProgress (pXY, stsSending);
if (packetLength > dataLength)
packetLength = dataLength;
StsRet (XYSendPacketReliable (pXY, p, packetLength));
pXY->transferred += packetLength;
dataLength -= packetLength;
p += packetLength;
}
}
if (err == xyEOF) {
err = XYSendEOTReliable (pXY);
XYProgress (pXY, stsEOF);
}
return StsWarn (xyOK);
}
STATIC int
XYReceiveFallback (XYMODEM_PRIVATE *pXY)
{
if (pXY->G.enabled)
pXY->G.enabled = FALSE;
else if (pXY->crc.enabled)
pXY->crc.enabled
= pXY->batch.enabled
= pXY->longPacket.enabled
= FALSE;
else
pXY->G.enabled = pXY->batch.enabled
= pXY->longPacket.enabled = pXY->crc.enabled = TRUE;
return xyOK;
}
STATIC int
XYReceiveSendHandshake (XYMODEM_PRIVATE *pXY)
{
BYTE handshake;
if (pXY->G.enabled)
handshake = 'G';
else if (pXY->crc.enabled)
handshake = 'C';
else
handshake = NAK;
if (pXY->debug) {
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Sending handshake ");
DebugChar (pXY->debug, handshake);
DebugEnd (pXY->debug);
}
return StsWarn (XYSendByte (pXY, handshake));
}
STATIC int
XYReceiveFile (XYMODEM_PRIVATE *pXY)
{
BYTE data[1024];
unsigned dataLength;
int err = xyOK;
int packetNumber;
int retries = pXY->retries / 2 + 1;
int totalRetries = (pXY->retries * 3) / 2 + 1;
XYNewFile (pXY);
XYProgress (pXY, stsNegotiating);
do {
if (--retries == 0) {
XYReceiveFallback (pXY);
XYProgress (pXY, stsNegotiating);
retries = (pXY->retries / 3);
}
if (totalRetries-- == 0)
return StsWarn (xyFail);
StsRet (XYReceiveSendHandshake (pXY));
XYProgress (pXY, stsNewFile);
err = XYReceivePacket (pXY, &packetNumber, data, &dataLength);
if (err == xyEOT) {
StsRet (XYGobble (pXY, pXY->timeout / 2));
err = xyTimeout;
}
if (err == xyBadPacket) {
StsRet (XYGobble (pXY, pXY->timeout / 3));
}
if ((packetNumber != 0) && (packetNumber != 1)) {
err = xyBadPacket;
}
} while ((err == xyTimeout) || (err == xyBadPacket));
StsRet (err);
if (packetNumber == 0) {
pXY->batch.enabled = TRUE;
} else {
if (pXY->batch.certain && pXY->batch.enabled)
return StsWarn (xyFail);
pXY->batch.enabled = FALSE;
pXY->G.enabled = FALSE;
}
pXY->batch.certain = TRUE;
if (pXY->G.enabled || !pXY->batch.enabled)
pXY->G.certain = TRUE;
if (packetNumber == 0) {
if (data[0] == 0) {
if (!pXY->G.enabled)
StsRet (XYSendByte (pXY, ACK));
return xyEndOfSession;
}
StsRet (XYFileWriteOpen (pXY, data, dataLength));
if (!pXY->G.enabled)
StsRet (XYSendByte (pXY, ACK));
StsRet (XYReceiveSendHandshake (pXY));
err = XYReceivePacketReliable (pXY, &packetNumber, data, &dataLength);
} else {
StsRet (XYFileWriteOpen (pXY, NULL, 0));
}
pXY->packetNumber = 1;
pXY->transferred = 0;
XYProgress (pXY, stsReceiving);
if ((err == xyOK) && (packetNumber == 1)) {
if ((pXY->fileSize > 0)
&& (dataLength + pXY->transferred > pXY->fileSize))
dataLength = pXY->fileSize - pXY->transferred;
StsRet (XYFileWrite (pXY, data, dataLength));
pXY->transferred += dataLength;
pXY->packetNumber++;
XYProgress (pXY, stsReceiving);
if (pXY->batch.enabled && !pXY->G.enabled && !pXY->G.certain) {
int oldTimeout = pXY->timeout;
pXY->timeout /= 3;
err = XYReceivePacketReliable (pXY, &packetNumber, data, &dataLength);
pXY->timeout = oldTimeout;
if ((err == xyOK) && (packetNumber == 2)) {
pXY->G.enabled = TRUE;
if (pXY->debug) {
DebugBegin (pXY->debug, debugInit);
DebugString (pXY->debug, "Y-G sender detected");
DebugEnd (pXY->debug);
}
} else if (err == xyTimeout) {
StsRet (XYSendByte (pXY, ACK));
err = XYReceivePacketReliable (pXY, &packetNumber, data, &dataLength);
}
} else
err = XYReceivePacketReliable (pXY, &packetNumber, data, &dataLength);
}
while (err == xyOK) {
if (packetNumber == (pXY->packetNumber & 0xFF)) {
if ((pXY->fileSize > 0)
&& (dataLength + pXY->transferred > pXY->fileSize))
dataLength = pXY->fileSize - pXY->transferred;
StsRet (XYFileWrite (pXY, data, dataLength));
pXY->transferred += dataLength;
pXY->packetNumber++;
XYProgress (pXY, stsReceiving);
if (!pXY->G.enabled)
StsRet (XYSendByte (pXY, ACK));
} else if (packetNumber == (pXY->packetNumber - 1) & 0xFF)
StsRet (XYSendByte (pXY, ACK));
else
return StsWarn (xyFail);
err = XYReceivePacketReliable (pXY, &packetNumber, data, &dataLength);
}
if (err == xyEOT) {
err = XYSendByte (pXY, ACK);
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Acknowledging EOT");
DebugEnd (pXY->debug);
XYProgress (pXY, stsEOF);
}
StsRet (XYFileWriteClose (pXY));
return StsWarn (err);
}
STATIC int
XYSendSessionEnd (XYMODEM_PRIVATE *pXY)
{
int err;
BYTE response;
XYNewFile (pXY);
XYProgress (pXY, stsEnding);
do {
err = XYSendReadHandshake (pXY, pXY->timeout, &response);
if (err == xyTimeout)
return StsWarn (xyFail);
StsRet (err);
DebugBegin (pXY->debug, debugPacket);
DebugString (pXY->debug, "Initial handshake ");
DebugChar (pXY->debug, response);
DebugEnd (pXY->debug);
} while (response == ACK);
do {
StsRet (XYSendPacketZero (pXY));
if (pXY->G.enabled)
return xyOK;
do {
err = XYSendReadHandshake (pXY, pXY->timeout, &response);
if (err == xyTimeout)
return StsWarn (xyFail);
StsRet (err);
} while (err != xyOK);
if (pXY->debug) {
DebugBegin (pXY->debug, debugPacket);
if (response == ACK)
DebugString (pXY->debug, "Received ACK for final YModem header");
else {
DebugString (pXY->debug, "Received ");
DebugChar (pXY->debug, response);
DebugString (pXY->debug, " for final YModem header");
}
DebugEnd (pXY->debug);
}
} while (response != ACK);
return xyOK;
}
STATIC int
XYSend (XYMODEM_PRIVATE *pXY)
{
int err;
XYNewFile (pXY);
do {
err = XYFileReadOpenNext (pXY);
if (err == xyOK) {
err = XYSendFile (pXY);
XYFileReadClose (pXY);
XYNewFile (pXY);
}
} while ((err == xyOK) && (pXY->batch.enabled));
if (err == xyEndOfSession) {
err = xyOK;
if (pXY->batch.enabled)
err = XYSendSessionEnd (pXY);
}
if (err == xyFail) {
XYSendCAN (pXY);
err = xyFailed;
}
if (err == xyOK)
XYProgress (pXY, stsDone);
else if (pXY->userCancel)
XYProgress (pXY, stsCancelled);
else
XYProgress (pXY, stsFailed);
return StsWarn (err);
}
STATIC int
XYReceive (XYMODEM_PRIVATE *pXY)
{
int err;
do {
err = XYReceiveFile (pXY);
} while ((err == xyOK) && (pXY->batch.enabled));
if (err == xyEndOfSession)
err = xyOK;
if (err == xyFail) {
XYSendCAN (pXY);
err = xyFailed;
}
if (err == xyOK)
XYProgress (pXY, stsDone);
else if (pXY->userCancel)
XYProgress (pXY, stsCancelled);
else
XYProgress (pXY, stsFailed);
XYGobble (pXY, 2);
return StsWarn (err);
}
int
XYModemInit (XYMODEM *pXYPublic, SERIAL_PORT port)
{
XYMODEM_PRIVATE *pXY;
XYInitCrc16 ();
pXY = malloc (sizeof (*pXY));
if (pXY == NULL)
return 1;
memset (pXY, 0, sizeof (*pXY));
pXY->timeout = 10;
pXY->retries = 10;
pXY->userCancel = 0;
pXY->packetNumber = 0;
pXY->transferred = 0;
pXY->fileSize = 0;
pXY->crc.enabled = TRUE;
pXY->crc.certain = FALSE;
pXY->longPacket.enabled = TRUE;
pXY->longPacket.certain = FALSE;
pXY->batch.enabled = TRUE;
pXY->batch.certain = FALSE;
pXY->G.enabled = FALSE;
pXY->G.certain = FALSE;
pXY->debug = NULL;
pXY->port = NULL;
pXY->f = NULL;
pXY->filenames = NULL;
pXY->currentFileName = 0;
pXY->numFileNames = 0;
pXY->fileType = diskFileUnknown;
pXY->progress = NULL;
pXY->port = port;
*pXYPublic = pXY;
return xyOK;
}
int
XYModemDestroy (XYMODEM xyPublic)
{
free (xyPublic);
return xyOK;
}
int
XYModemSetProtocol (XYMODEM xyPublic, int protocol)
{
XYMODEM_PRIVATE *pXY = xyPublic;
pXY->G.enabled = FALSE;
pXY->crc.enabled = FALSE;
pXY->batch.enabled = FALSE;
pXY->longPacket.enabled = FALSE;
switch (protocol) {
case YMODEMG:
pXY->G.enabled = TRUE;
case YMODEM:
pXY->batch.enabled = TRUE;
case XMODEMK:
pXY->longPacket.enabled = TRUE;
case XMODEMCRC:
pXY->crc.enabled = TRUE;
case XMODEM:
break;
default:
DebugBegin (pXY->debug, debugWarn);
DebugString (pXY->debug, "XYModem initialized with illegal protocol");
DebugEnd (pXY);
return StsWarn (xyFailed);
}
return 0;
}
int
XYModemSetDebug (XYMODEM xyPublic, DEBUG debug)
{
XYMODEM_PRIVATE *pXY = (XYMODEM_PRIVATE *) xyPublic;
pXY->debug = debug;
return xyOK;
}
int
XYModemSetProgress (XYMODEM xyPublic, PROGRESS progress)
{
XYMODEM_PRIVATE *pXY = (XYMODEM_PRIVATE *) xyPublic;
pXY->progress = progress;
return xyOK;
}
int
XYModemSetFileType (XYMODEM xyPublic, int fileType)
{
XYMODEM_PRIVATE *pXY = (XYMODEM_PRIVATE *) xyPublic;
pXY->fileType = fileType;
return xyOK;
}
int
XYModemCancel (XYMODEM xyPublic)
{
XYMODEM_PRIVATE *pXY = (XYMODEM_PRIVATE *) xyPublic;
pXY->userCancel = TRUE;
return xyOK;
}
int XYModemSend
(XYMODEM xyPublic, const char *filenames[], int count) {
XYMODEM_PRIVATE *pXY = (XYMODEM_PRIVATE *) xyPublic;
int returnVal = 0;
pXY->currentFileName = 0;
pXY->filenames = filenames;
pXY->numFileNames = count;
ProgressSending (pXY->progress);
if (XYSend (pXY) != xyOK)
returnVal = 1;
return returnVal;
}
int
XYModemReceive (XYMODEM xyPublic)
{
XYMODEM_PRIVATE *pXY = (XYMODEM_PRIVATE *) xyPublic;
int returnVal = 0;
ProgressReceiving (pXY->progress);
if (XYReceive (pXY) != xyOK)
returnVal = 1;
return returnVal;
}