home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 112.lha / BModem / dlink.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-20  |  14.7 KB  |  532 lines

  1. /* dlink.c - x/ymodem transfer routines for bmodem */
  2. /*
  3.     by David Betz, BYTE Magazine/BIX
  4. */
  5.  
  6. #include <stdio.h>
  7. #include "dlink.h"
  8.  
  9. /* useful definitions */
  10. #define TRUE    1
  11. #define FALSE    0
  12.  
  13. /* protocol characters */
  14. #define SOH    0x01    /* start of a 128 byte block */
  15. #define STX    0x02    /* start of a 1024 byte block */
  16. #define EOT    0x04    /* end of a complete file */
  17. #define ACK    0x06    /* positive acknowledgement */
  18. #define NAK    0x15    /* negative acknowledgement */
  19. #define CAN    0x18    /* cancel transfer */
  20. #define CRC    'C'    /* CRC request (instead of NAK to start a transfer) */
  21.  
  22. /* useful constants */
  23. #define DT_TIME    -1    /* return value for timeout */
  24. #define RETRY    10    /* number of times to retry */
  25.  
  26. /* timeout values (in seconds) */
  27. #define STIME    6    /* timeout between characters of a packet */
  28. #define ITIME    5    /* timeout sending initial NAK or 'C' */
  29. #define XTIME    60    /* timeout waiting for initial NAK */
  30. #define LTIME    20    /* timeout waiting for an ACK/NAK */
  31. #define FTIME    1    /* timeout for flushing the line */
  32.  
  33. /* error messages */
  34. char *errstring[] = {
  35.     "OK",
  36.     "INIT",    /* failed during initialization */
  37.     "DATA",    /* failed sending/receiving a data packet */
  38.     "EOT",    /* failed sending/receiving EOT */
  39.     "CANCEL",    /* transfer canceled by other side */
  40.     "EOF",    /* reached end of file */
  41.     "DONE",    /* done with batch receive */
  42.     "HEAD"    /* bad header */
  43. };
  44.  
  45. /* global variables */
  46. int blknum;        /* current block number */
  47.  
  48. /* local variables */
  49. static int crcmode;    /* TRUE for CRC mode, FALSE for checksum */
  50. static int timeout;    /* current receive timeout */
  51. static int needack;    /* need an ACK for the previous block */
  52. static int nak;        /* NAK character on receive */
  53.  
  54. /* crctab calculated by Mark G. Mendel, Network Systems Corporation */
  55. static unsigned short crctab[256] = {
  56.     0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
  57.     0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
  58.     0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
  59.     0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
  60.     0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
  61.     0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
  62.     0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
  63.     0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
  64.     0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
  65.     0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
  66.     0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
  67.     0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
  68.     0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
  69.     0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
  70.     0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
  71.     0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
  72.     0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
  73.     0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
  74.     0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
  75.     0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
  76.     0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
  77.     0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
  78.     0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
  79.     0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
  80.     0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
  81.     0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
  82.     0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
  83.     0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
  84.     0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
  85.     0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
  86.     0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
  87.     0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
  88. };
  89.  
  90. /*
  91.  * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. 
  92.  *  NOTE: First argument is referenced twice.
  93.  *        Second argument must be in range 0 to 255.
  94.  * 
  95.  * Programmers may incorporate any or all code into their programs, 
  96.  * giving proper credit within the source. Publication of the 
  97.  * source routines is permitted so long as proper credit is given 
  98.  * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, 
  99.  * Omen Technology.
  100.  */
  101.  
  102. #define updcrc(crc,ch) (crctab[((crc >> 8) & 0xFF)] ^ (crc << 8) ^ ch)
  103.  
  104. /* xy_sinit - initialize to send data using x/ymodem */
  105. int xy_sinit()
  106. {
  107.     blknum = 0;
  108.     return (startxfr());
  109. }
  110.  
  111. /* xy_sndhdr - send a ymodem batch header */
  112. int xy_sndhdr(name,size)
  113.   char *name;        /* filename */
  114.   long size;        /* file size (in bytes) */
  115. {
  116.     int sts;
  117.     if ((sts = xy_sinit()) == ER_OK)
  118.     sts = sendhdr(name,size);
  119.     return (sts);
  120. }
  121.  
  122. /* xy_sndblk - send a data block using x/ymodem */
  123. int xy_sndblk(buf,len)
  124.   unsigned char *buf;    /* data buffer */
  125.   int len;        /* data length (must be either 128 or 1024) */
  126. {
  127.     return (sndblk(++blknum,buf,len));
  128. }
  129.  
  130. /* xy_sndeot - end a data transfer using x/ymodem */
  131. int xy_sndeot()
  132. {
  133.     unsigned char buf[1];
  134.     buf[0] = EOT;        /* make an EOT packet */
  135.     return (sndpkt(buf,1));    /* send it */
  136. }
  137.  
  138. /* xy_sndend - send an empty header to end a batch send */
  139. int xy_sndend()
  140. {
  141.     return (xy_sndhdr("",-1L));
  142. }
  143.  
  144. /* xy_rinit - initialize to receive data using x/ymodem */
  145. int xy_rinit(usecrc)
  146.   int usecrc;        /* TRUE for CRC transfers, FALSE for checksum */
  147. {
  148.     timeout = ITIME;        /* use shorter initial timeout */
  149.     nak = (usecrc ? CRC : NAK);    /* initial NAK sets crc/checksum mode */
  150.     crcmode = usecrc;        /* remember if crc is in use */
  151.     blknum = 0;            /* start at block zero (in case of header) */
  152.     needack = FALSE;        /* don't need an ACK the first time */
  153.     md_put(nak);        /* send the initial NAK (or CRC) */
  154.     md_iflush(0);
  155.     return (ER_OK);        /* return successfully */
  156. }
  157.  
  158. /* xy_rcvhdr - receive a ymodem batch header */
  159. int xy_rcvhdr(usecrc,name,psize)
  160.   int usecrc;        /* TRUE for CRC transfers, FALSE for checksum */
  161.   char *name;        /* file name (output) */
  162.   long *psize;        /* file size (output) */
  163. {
  164.     unsigned char buf[LBLKLEN];
  165.     int bc,sts;
  166.     if ((sts = xy_rinit(usecrc)) != ER_OK)    /* initialize */
  167.     return (sts);
  168.     if ((sts = rcvblk(blknum,buf,&bc)) != ER_OK)/* receive the header */
  169.     return (sts);
  170.     return (processhdr(buf,bc,name,psize));    /* process the header */
  171. }
  172.  
  173. /* xy_rcvblk - receive a data block using x/ymodem */
  174. int xy_rcvblk(buf,plen)
  175.   unsigned char *buf;    /* data buffer (output) */
  176.   int *plen;        /* data length (output) */
  177. {
  178.     if (needack)            /* send ACK for previous block */
  179.     xy_sndack();
  180.     return (rcvblk(++blknum,buf,plen));    /* receive the next block */
  181. }
  182.  
  183. /* xy_sndack - send an ack for a header packet */
  184. xy_sndack()
  185. {
  186.     md_put(ACK);    /* send an ACK */
  187.     md_iflush(0);    /* flush the input buffer */
  188. }
  189.  
  190. /* xy_cancel - cancel a transfer */
  191. xy_cancel()
  192. {
  193.     md_iflush(1);    /* flush the input buffer */
  194.     md_put(CAN);    /* cancel the transfer */
  195.     md_put(CAN); md_put(CAN); md_put(CAN);
  196. }
  197.  
  198. /* startxfr - wait for the initial NAK or 'C' to start transfer */
  199. static int startxfr()
  200. {
  201.     int lastch,ch;
  202.  
  203.     /* wait for the initial NAK or 'C' */
  204.     for (lastch = -1; ; lastch = ch) {
  205.     ch = md_get(XTIME);        /* get the next character */
  206.     if (ch == DT_TIME) {        /* check for timeout */
  207.         log("timeout waiting for initial NAK");
  208.         return (ER_INIT);
  209.     }
  210.     else if (ch == NAK) {        /* start of checksum transfer */
  211.         crcmode = FALSE;
  212.         return (ER_OK);
  213.     }
  214.     else if (ch == CRC) {        /* start of CRC transfer */
  215.         crcmode = TRUE;
  216.         return (ER_OK);
  217.     }
  218.     else if (ch == CAN) {        /* abort transfer on CAN/CAN */
  219.         if (lastch == CAN)
  220.         return (ER_CANCEL);
  221.     }
  222.     else                /* garbage character */
  223.         log("expecting initial NAK, ch=%02x",ch);
  224.     }
  225. }
  226.  
  227. /* sendhdr - send block zero header */
  228. static int sendhdr(name,size)
  229.   char *name; long size;
  230. {
  231.     unsigned char buf[SBLKLEN],*bp;
  232.     int bc;
  233.  
  234.     /* initialize the block */
  235.     for (bp = buf, bc = SBLKLEN; --bc >= 0; )
  236.     *bp++ = '\0';
  237.     blknum = 0;
  238.  
  239.     /* insert the filename and size */
  240.     if (size >= 0)
  241.     sprintf(buf,"%s%c%ld",name,'\0',size);
  242.     else
  243.     strcpy(buf,name);
  244.  
  245.     /* send the block */
  246.     return (sndblk(blknum,buf,SBLKLEN));
  247. }
  248.  
  249. /* sndblk - send a data packet */
  250. static int sndblk(n,buf,len)
  251.   int n;        /* block number */
  252.   unsigned char *buf;    /* data buffer */
  253.   int len;        /* data length (either SBLKLEN or LBLKLEN) */
  254. {
  255.     unsigned char packet[LBLKLEN+5],*p;
  256.     unsigned int chk;
  257.  
  258.     /* setup the packet header */
  259.     p = packet;
  260.     *p++ = (len == SBLKLEN ? SOH : STX);
  261.     *p++ = n;
  262.     *p++ = ~n;
  263.  
  264.     /* compute the block check code */
  265.     if (crcmode) {            /* compute the CRC */
  266.     for (chk = 0; --len >= 0; )
  267.         chk = updcrc(chk,(*p++ = *buf++));
  268.     chk = updcrc(chk,'\0');
  269.     chk = updcrc(chk,'\0');
  270.     *p++ = chk >> 8;
  271.     *p++ = chk;
  272.     }
  273.     else {                /* compute the checksum */
  274.     for (chk = 0; --len >= 0; )
  275.         chk += (*p++ = *buf++);
  276.     *p++ = chk;
  277.     }
  278.  
  279.     /* send the packet */
  280.     return (sndpkt(packet,p-packet));
  281. }
  282.  
  283. /* sndpkt - send a packet and wait for a response */
  284. static int sndpkt(packet,plength)
  285.   unsigned char *packet; int plength;
  286. {
  287.     int retries,lastch,ch;
  288.  
  289.     /* send data and wait for an ACK */
  290.     for (retries = RETRY; retries > 0; --retries) {
  291.  
  292.     /* send the data */
  293.     md_write(packet,plength);
  294.     md_iflush(0);
  295.  
  296.     /* wait for ACK, NAK or CAN/CAN */
  297.     for (lastch = -1; ; lastch = ch) {
  298.         ch = md_get(XTIME);        /* get the next character */
  299.         if (ch == DT_TIME) {    /* check for timeout */
  300.         log("timeout waiting for ACK/NAK/CAN");
  301.         break;
  302.         }
  303.         else if (ch == ACK)        /* return on ACK */
  304.         return (ER_OK);
  305.         else if (ch == NAK) {    /* send packet again on NAK */
  306.         log("received NAK");
  307.         break;
  308.         }
  309.         else if (ch == CAN) {    /* cancel transfer on CAN/CAN */
  310.         if (lastch == CAN)
  311.             return (ER_CANCEL);
  312.         }
  313.         else            /* garbage */
  314.         log("expecting ACK/NAK/CAN, ch=%02x",ch);
  315.     }
  316.     }
  317.  
  318.     /* retry count ran out */
  319.     return (plength == 1 ? ER_EOT : ER_DATA);
  320. }
  321.  
  322. /* processhdr - process a received header */
  323. static int processhdr(buf,len,name,psize)
  324.   char *buf;        /* header */
  325.   int len;        /* header length */
  326.   char *name;        /* file name (output) */
  327.   long *psize;        /* file size (output) */
  328. {
  329.     extern long atol();
  330.     int i,j;
  331.  
  332.     /* check for end of batch */
  333.     if (buf[0] == '\0') {
  334.     xy_sndack();
  335.     return (ER_DONE);
  336.     }
  337.  
  338.     /* find the filename */
  339.     for (i = 0; i < len; )
  340.     if ((*name++ = buf[i++]) == '\0')
  341.         break;
  342.     if (i >= len)
  343.     return (ER_HEAD);
  344.  
  345.     /* find the file size */
  346.     for (j = i; j < len; ++j)
  347.     if (buf[j] < '0' || buf[j] > '9')
  348.         break;
  349.     *psize = (i < j ? atol(&buf[i]) : -1L);
  350.  
  351.     /* return successfully */
  352.     return (ER_OK);
  353. }
  354.  
  355. /* rcvblk - receive a data block */
  356. static int rcvblk(n,buf,plen)
  357.   int n;        /* block number */
  358.   unsigned char *buf;    /* data buffer */
  359.   int *plen;        /* data length (output) */
  360. {
  361.     int nn,sts;
  362.  
  363.     /* receive a packet */
  364.     while ((sts = rcvpkt(&nn,buf,plen)) == ER_OK) {
  365.  
  366.     /* check the block number */
  367.     if (nn == (n & 0xFF)) {
  368.         timeout = LTIME;    /* use the longer timeout now */
  369.         nak = NAK;        /* and the normal NAK character */
  370.         needack = TRUE;    /* need an ACK for this block */
  371.         return (ER_OK);
  372.     }
  373.     else if (nn == ((n - 1) & 0xFF)) {
  374.         log("duplicate block");
  375.         xy_sndack();
  376.     }
  377.     else {
  378.         log("sequence error, n=%d",n);
  379.         sts = ER_DATA;
  380.         xy_cancel();
  381.         break;
  382.     }
  383.     }
  384.  
  385.     /* return failure status */
  386.     return (sts);
  387. }
  388.  
  389. /* rcvpkt - receive a data packet */
  390. static int rcvpkt(pn,buf,plen)
  391.   int *pn;        /* block number (output) */
  392.   unsigned char *buf;    /* data buffer */
  393.   int *plen;        /* data length (output) */
  394. {
  395.     int bc,ch,retries,sts;
  396.     unsigned char *bp;
  397.  
  398.     /* receive data */
  399.     for (retries = RETRY; retries > 0; --retries) {
  400.  
  401.     /* receive the packet header */
  402.     switch (sts = rcvhdr(pn,plen)) {
  403.     case ER_EOF:
  404.     case ER_CANCEL:
  405.         return (sts);
  406.     case ER_DATA: /* really timeout */
  407.         if (crcmode && blknum == 1 && retries == 6) {
  408.         log("switching to checksum mode");
  409.         crcmode = FALSE;
  410.         nak = NAK;
  411.         }
  412.         md_put(nak);
  413.         continue;
  414.     }
  415.  
  416.     /* receive the data */
  417.     for (bp = buf, bc = *plen; --bc >= 0; *bp++ = ch)
  418.         if ((ch = md_get(STIME)) == DT_TIME)
  419.         break;
  420.     if (bc >= 0) {
  421.         log("timeout within packet");
  422.         md_put(nak);
  423.         continue;
  424.     }
  425.  
  426.     /* check the block check code */
  427.     if (rcvchk(buf,*plen) != ER_OK) {
  428.         md_put(nak);
  429.         continue;
  430.     }
  431.  
  432.     /* return successfully */
  433.     return (ER_OK);
  434.     }
  435.  
  436.     /* retry count ran out */
  437.     return (ER_DATA);
  438. }
  439.  
  440. /* rcvhdr - receive a packet header */
  441. static int rcvhdr(pn,plen)
  442.   int *pn;    /* block number (output) */
  443.   int *plen;    /* packet length (output) */
  444. {
  445.     int ch,lastch;
  446.  
  447.     /* wait for next packet header */
  448.     for (lastch = -1; ; lastch = ch) {
  449.  
  450.     /* wait for SOH, STX or CAN/CAN */
  451.     ch = md_get(timeout);    /* get next character */
  452.     if (ch == DT_TIME) {    /* check for timeout */
  453.         log("timeout waiting for SOH/STX");
  454.         return (ER_DATA);
  455.     }
  456.     else if (ch == EOT) {    /* end of transfer */
  457.         xy_sndack();
  458.         return (ER_EOF);
  459.     }
  460.     else if (ch == SOH)    /* start of a short packet */
  461.         *plen = SBLKLEN;
  462.     else if (ch == STX)    /* start of a long packet */
  463.         *plen = LBLKLEN;
  464.     else if (ch == CAN) {    /* two cancels abort transfer */
  465.         if (lastch == CAN)
  466.         return (ER_CANCEL);
  467.         continue;
  468.     }
  469.     else {    /* garbage */
  470.         log("expecting SOH/STX, ch=%02x",ch);
  471.         md_iflush(1);
  472.         continue;
  473.     }
  474.  
  475.     /* get the block numbers */
  476.     if ((*pn = md_get(STIME)) == DT_TIME
  477.     ||  (ch = md_get(STIME)) == DT_TIME) {
  478.         log("timeout within packet");
  479.         return (ER_DATA);
  480.     }
  481.  
  482.     /* check the block numbers */
  483.     if (*pn + ch != 0xFF) {
  484.         log("block check failed, n1=%02x, n2=%02x",*pn,ch);
  485.         continue;
  486.     }
  487.  
  488.     /* got a valid packet header */
  489.     return (ER_OK);
  490.     }
  491. }
  492.  
  493. /* rcvchk - receive and check the checksum or CRC */
  494. static int rcvchk(buf,len)
  495.   unsigned char *buf;    /* data buffer */
  496.   int len;        /* data length */
  497. {
  498.     unsigned int chk;
  499.     int lastch,ch;
  500.  
  501.     /* receive and check the checksum or CRC */
  502.     if (crcmode) {                /* CRC */
  503.     if ((lastch = md_get(STIME)) == DT_TIME    /* two bytes */
  504.     ||  (ch = md_get(STIME)) == DT_TIME) {
  505.         log("timeout within packet");
  506.         return (ER_DATA);
  507.     }
  508.     for (chk = 0; --len >= 0; )
  509.         chk = updcrc(chk,*buf++);
  510.     chk = updcrc(chk,lastch);
  511.     if (updcrc(chk,ch) & 0xFFFF) {
  512.         log("CRC error");
  513.         return (ER_DATA);
  514.     }
  515.     }
  516.     else {                    /* checksum */
  517.     if ((ch = md_get(STIME)) == DT_TIME) {    /* one byte */
  518.         log("timeout within packet");
  519.         return (ER_DATA);
  520.     }
  521.     for (chk = 0; --len >= 0; )
  522.         chk += *buf++;
  523.     if ((chk & 0xFF) != ch) {
  524.         log("checksum error");
  525.         return (ER_DATA);
  526.     }
  527.     }
  528.  
  529.     /* return successfully */
  530.     return (ER_OK);
  531. }
  532.