XMODEM.C
上传用户:sunrenlu
上传日期:2022-06-13
资源大小:1419k
文件大小:15k
源码类别:

操作系统开发

开发平台:

DOS

  1. /*
  2.  * xmodem.exe - terminal session with XMODEM (xsum+CRC) up/download capabilities
  3.  *            - edit xmodem.ini to change desired COM port
  4.  *            - Keystrokes:
  5.  *                              Esc to exit terminal
  6.  *                              PgUp - upload a file (prompts for name)
  7.  *                              PgDn - download a file (prompts for name)
  8.  *
  9.  *            - download function could be shorter but more cryptic
  10.  */
  11. #include <rtos.h>
  12. #include <sio.h>
  13. #include <xmodem.h>
  14. #include <stdio.h>
  15. #include <mem.h>
  16. #include <bios.h>
  17. #include <inifile.h>
  18. #include <kconio.h>
  19. #define COMBASE1 0x3f8
  20. #define COMBASE2 0x2f8
  21. #define COMIRQ1  4
  22. #define COMIRQ2  3
  23. #define NUM_RETRIES 10      /* # of consecutive failures before abort xfer */
  24. int combuf = 256;
  25. /*********************************************************************/
  26. void get_userfilename( char *msg, char *buffer )
  27. {
  28.     cprintf("rn%s : ", msg );
  29.     *buffer = 255;
  30.     kgets( buffer );
  31. }
  32. /*********************************************************************/
  33. /* debug_getbyte - read serial byte if available
  34.  *               - break program if user hits esc
  35.  */
  36. int debug_getbyte( WORD port, BYTE *data )
  37. {
  38.     BYTE b;
  39.     if ( kbhit() ) {
  40.         b = getch();
  41.         if ( b == 27 ) {
  42.             sio_writebyte( port, XM_CAN );
  43.             sio_writebyte( port, 3 );       /* ctrl c */
  44.             rt_halt("user exit from XMODEM mode");
  45.         } else
  46.             sio_writebyte( port, b );
  47.     }
  48.     if ( sio_recv_waiting( port ) ) {
  49.         b = sio_readbyte( port );
  50.         *data = b;
  51.         return( 1 );
  52.     }
  53.     return( 0 );
  54. }
  55. /*********************************************************************/
  56. /* debug_readbyte - busy wait for data from a port
  57.  *                - inherits keyboard ESC feature from debug_getbyte
  58.  */
  59. BYTE debug_readbyte( int port )
  60. {
  61.     BYTE b;
  62.     while ( ! debug_getbyte( port, &b ) )
  63.         /* nothing to do */
  64.         rt_sleep( 0 );
  65.     return( b );
  66. }
  67. /*********************************************************************/
  68. temp_outbyte( int port, BYTE b )
  69. {
  70. //    while ( sio_tran_waiting( port )) rt_yield();
  71.     sio_writebyte( port, b );
  72. }
  73. /*********************************************************************/
  74. BYTE calc_xsum( BYTE *p, int count )
  75. {
  76.     BYTE xsum;
  77.     int i;
  78.     xsum = 0;
  79.     while ( count--  )
  80.         xsum += *p++;
  81.     return( xsum );
  82. }
  83. /*********************************************************************/
  84. WORD calc_crc( BYTE *p, int count )
  85. {
  86.     WORD crc;
  87.     int i;
  88.     crc = 0;
  89.     while ( count--  ) {
  90.         crc = crc ^ ((*p++) <<8);
  91.         for ( i = 0 ; i < 8 ; ++i )
  92.             if ( crc & 0x8000 )
  93.                 crc = crc << 1 ^ 0x1021;
  94.             else
  95.                 crc = crc << 1;
  96.     }
  97.     return( crc );
  98. }
  99. /*********************************************************************/
  100. void upload(WORD port, char *fname)
  101. {
  102.     int  i, count;
  103.     BYTE b;
  104.     char *buffer = NULL;
  105.     FILE *f = NULL;
  106.     BYTE seq;   /* XMODEM sequence number, */
  107.     BYTE xsum;  /* 8 bit checksum */
  108.     WORD xcrc;  /* 16 bit CRC */
  109.     int use_crc = 0;
  110.     DWORD bytes = 0;
  111.     DWORD timer;
  112.     timer = ktime;          /* note start time */
  113.     b = debug_readbyte( port );
  114.     if ( b == XM_CRC ) {
  115.         use_crc = 1;
  116.         b = XM_NAK;     /* start the syphon */
  117.     }
  118.     do {    /* structured exception handler-like */
  119.         /* need a NAK from receiver to start this going */
  120.         if ( b != XM_NAK) {
  121.             cprintf("ERROR: receiver is not listenningrn");
  122.             break;
  123.         }
  124.         if ( ( f = fopen( fname, "rb" ))== NULL ) {
  125.             cprintf("ERROR: cannot open file: %srn", fname );
  126.             /* inform our peer */
  127.             break;
  128.         }
  129.         if ( ( buffer = kcalloc( 1, XM_BUFSIZE)) == NULL ) {
  130.             cprintf("ERROR: out of memoryrn" );
  131.             break;
  132.         }
  133.         seq = 1;
  134.         do {
  135.             /* read some of the file from disk */
  136.             count = fread( buffer, 1, XM_BUFSIZE, f );
  137.             /* reached end of file */
  138.             if ( count == 0 ) break;
  139.             /* set any unread bytes to zero */
  140.             if ( count < XM_BUFSIZE )
  141.                 memset( buffer + count, 26, XM_BUFSIZE - count );   /* Ctrl Z */
  142.             /* look for errors while disk reading */
  143.             while ( debug_getbyte( port, &b ));
  144.             /* next part may have to be retried - communications are not perfect */
  145.             do {
  146.                 cprintf("data packet # %i ", (WORD) seq );
  147.                 temp_outbyte( port, XM_SOH );       /* start */
  148.                 temp_outbyte( port, seq );          /* sequence number */
  149.                 temp_outbyte( port, seq ^ 0xff );   /* and its complement */
  150.                 /* write bytes and compute checksum */
  151.                 if ( use_crc )
  152.                     xcrc = calc_crc( buffer, XM_BUFSIZE);
  153.                 else
  154.                     xsum = calc_xsum( buffer, XM_BUFSIZE);
  155.                 for ( i = 0 ; i < XM_BUFSIZE ; ++i ) {
  156.                     b = buffer[i];
  157.                     temp_outbyte( port, b );
  158.                 }
  159.                 /* write the CRC or checksum */
  160.                 if ( use_crc ) {
  161.                     temp_outbyte( port, xcrc >> 8 );
  162.                     temp_outbyte( port, xcrc & 255 );
  163.                 } else
  164.                     temp_outbyte( port, xsum );
  165.                 /* and now we get the news */
  166.                 b = debug_readbyte( port );
  167.                 switch ( b ) {
  168.                     case XM_NAK : cprintf("NAKrn"); break;
  169.                     case XM_ACK : cprintf("ACKrn"); break;
  170.                     default     : cprintf("%02xrn", b ); break;
  171.                 }
  172.             } while ( b == XM_NAK );
  173.             bytes += XM_BUFSIZE;
  174.             /* now advance to the next packet */
  175.             ++seq;
  176.         } while ( 1 );
  177.     } while ( 0 );
  178.     do {
  179.         temp_outbyte( port, XM_EOT );
  180.         b = debug_readbyte( port );
  181.         switch ( b ) {
  182.             case XM_NAK : cprintf("NAKrn"); break;
  183.             case XM_ACK : cprintf("ACKrn"); break;
  184.             default     : cprintf("%02xrn", b );
  185.         }
  186.     } while ( b == XM_NAK );
  187.     timer = ktime - timer;
  188.     if ( timer > 0 ) {
  189.         cprintf(" %lu bytes in %lu.%lu seconds, %lu bpsn",
  190.             bytes, timer / 1000, timer % 1000,
  191.             bytes*8*1000/timer );
  192.     }
  193.     if ( buffer != NULL ) kfree( buffer );
  194.     if ( f != NULL ) fclose( f );
  195. }
  196. /*********************************************************************/
  197. /* xm_waitbyte - wait a spell = 0
  198.  *             - return early with 1 if a BYTE found for read
  199.  */
  200. xm_waitbyte( WORD port, BYTE *p, DWORD delay_seconds )
  201. {
  202.     DWORD when;
  203.     /* calculate when loop would timeout */
  204.     when = ktime + delay_seconds * 1000;
  205.     if ( when < (delay_seconds * 1000))
  206.         /* timer wrapping - once every 57 days, we'll ignore it in simple example*/
  207.         return( 1 );
  208.     while ( ktime < when ) {
  209.         if ( kbhit() )
  210.             rt_halt("user requested exit while waiting for serial bytes");
  211.         if ( sio_recv_waiting( port ) ) {
  212.             *p = sio_readbyte( port );
  213.             return( 1 );
  214.         }
  215.         rt_sleep( 0 );      /* give up slice */
  216.     }
  217.     /* timed out */
  218.     return( 0 );
  219. }
  220. /*********************************************************************/
  221. void download(WORD port, char *fname)
  222. {
  223.     int  i, count;
  224.     BYTE b;
  225.     int retries;
  226.     char buffer[ XM_BUFSIZE ];
  227.     FILE *f = NULL;
  228.     BYTE seq;       /* XMODEM sequence number */
  229.     BYTE localseq;  /* our expected sequence number */
  230.     BYTE xsum;  /* 8 bit checksum */
  231.     WORD xcrc;  /* 16 bit CRC */
  232.     int use_crc = 0;
  233.     DWORD bytes = 0;
  234.     DWORD timer;
  235.     timer = ktime;          /* note start time */
  236.     /* write out C's waiting for input */
  237.     retries = 10;
  238.     use_crc = 1;
  239.     do {
  240.         do {
  241.             /* write out individual C */
  242.             sio_writebyte( port, XM_CRC );      /* C */
  243.             if ( xm_waitbyte( port, &b, 10 ) ) break;
  244.             if ( retries-- == 0 ) {
  245.                 cprintf("Download gave up after no responsern");
  246.                 return;
  247.             }
  248.         } while ( 1 );
  249.         /* look at sent data */
  250.         if ( b == 3 ) return;   /* control break */
  251.     } while ( b != XM_SOH );
  252.     /* we have a SOH */
  253.     localseq = 1;
  254.     timer = ktime;
  255.     /* try to create output file */
  256.     if ( (f = fopen( fname,"wb")) == NULL ) {
  257.         sio_writebyte( port, XM_CAN );
  258.         rt_halt("unable to open local file for download");
  259.     }
  260.     retries = NUM_RETRIES;
  261.     goto skip_soh;      /* we already have the first byte handy */
  262.     /* we expect packets full of bytes */
  263.     do {
  264.         if ( --retries == 0 ) {
  265.             cprintf("too many retries, giving uprn");
  266.             sio_writebyte( port, XM_CAN );
  267.             break;
  268.         }
  269.         if ( !xm_waitbyte( port, &b, 10 ) ) {
  270.             /* handle timeout */
  271.             cprintf("timeout waiting for start of packetrn");
  272.             sio_writebyte( port, XM_NAK );
  273.             continue;
  274.         }
  275. skip_soh:
  276.         if ( b == XM_CAN )  /* sender requests a cancel */
  277.             break;
  278.         if ( b == XM_EOT ) {/* end of transmission !!! */
  279.             sio_writebyte( port, XM_ACK );  // acknowledge it
  280.             break;
  281.         }
  282.         if ( b != XM_SOH ) {
  283.             cprintf("garbage received at start of packetrn");
  284.             sio_writebyte( port, XM_NAK );
  285.             continue;
  286.         }
  287.         /* looking for sequence number */
  288.         if ( !xm_waitbyte( port, &b, 10 ) ) {
  289.             /* handle timeout */
  290.             cprintf("timeout waiting for sequence numberrn");
  291.             sio_writebyte( port, XM_NAK );
  292.             continue;
  293.         }
  294.         if ( b == (localseq-1 )) {
  295.             /* we already acknowledged it */
  296.             cprintf("received duplicate packetrn");
  297.             sio_writebyte( port, XM_ACK );
  298.             continue;
  299.         }
  300.         if ( b != localseq ) {
  301.             /* either garbage or peer is on a different packet */
  302.             cprintf("sequence number out of order : %u expected : %urn",
  303.                 b, localseq );
  304.             sio_writebyte( port, XM_NAK );
  305.             continue;
  306.         }
  307.         /* look for complement byte */
  308.         if ( !xm_waitbyte( port, &b, 10 ) ) {
  309.             /* handle timeout */
  310.             cprintf("timeout waiting for sequence complementrn");
  311.             sio_writebyte( port, XM_NAK );
  312.             continue;
  313.         }
  314.         if ( b != (~localseq &255 )) {
  315.             /* complement failed */
  316.             cprintf("sequence number corrupt, complement <> seqrn");
  317.             sio_writebyte( port, XM_NAK );
  318.             continue;
  319.         }
  320.         /* get data */
  321.         for ( count = 0 ; count < XM_BUFSIZE ; ++count ) {
  322.              if ( !xm_waitbyte( port, &b, 10 ) ) {
  323.                 /* handle timeout */
  324.                 cprintf("timeout waiting for data item %urn", count+1);
  325.                 sio_writebyte( port, XM_NAK );
  326.                 continue;
  327.             }
  328.             buffer[ count ] = b;
  329.         }
  330.         /* get CRC or checksum */
  331.         if ( !xm_waitbyte( port, &b, 10 ) ) {
  332.             /* handle timeout */
  333.             cprintf("timeout waiting for xsumrn");
  334.             sio_writebyte( port, XM_NAK );
  335.             continue;
  336.         }
  337.         if ( use_crc ) {
  338.             xcrc = b << 8;
  339.             if ( !xm_waitbyte( port, &b, 10 ) ) {
  340.                 /* handle timeout */
  341.                 cprintf("timeout waiting for xsumrn");
  342.                 sio_writebyte( port, XM_NAK );
  343.                 continue;
  344.             }
  345.             xcrc |= (b & 255);
  346.             if ( xcrc != calc_crc( buffer, XM_BUFSIZE)) {
  347.                 cprintf("CRC failedrn");
  348.                 sio_writebyte( port, XM_NAK );
  349.                 continue;
  350.             }
  351.         } else {
  352.             if ( b != calc_xsum( buffer, XM_BUFSIZE)) {
  353.                 cprintf("checksum failedrn");
  354.                 sio_writebyte( port, XM_NAK );
  355.                 continue;
  356.             }
  357.         }
  358.         /* packet was successful */
  359.         if ( fwrite( buffer, sizeof( buffer ), 1, f) != 1 ) {
  360.             cprintf("disk file write failedrn");
  361.             break;
  362.         }
  363.         cprintf("downloaded %u bytesr", bytes );
  364.         sio_writebyte( port, XM_ACK );
  365.         /* now advance to the next packet */
  366.         bytes += XM_BUFSIZE;
  367.         localseq++;
  368.         retries = NUM_RETRIES;
  369.     } while ( 1 );
  370.     timer = ktime - timer;
  371.     if ( timer > 0 ) {
  372.         cprintf(" %lu bytes in %lu.%lu seconds, %lu bpsn",
  373.             bytes, timer / 1000, timer % 1000,
  374.             bytes*8*1000/timer );
  375.     }
  376.     if ( f != NULL ) fclose( f );
  377. }
  378. /***********************************************************************/
  379. /* term_thread - includes all the code to make this work as a terminal */
  380. /*               session while user negotiates upload/download         */
  381. /*             - PgUp initiates upload                                 */
  382. /*             - PgDn initiates download                               */
  383. /***********************************************************************/
  384. void term_thread(DWORD port)
  385. {
  386.     int  i, count;
  387.     BYTE b;
  388.     WORD key;
  389.     static char buffer[ 256 ];
  390.     /*
  391.      * Initialize the COM port
  392.      */
  393.     if ( port == 1 )
  394.         sio_init( 1, COMBASE1, COMIRQ1, combuf, combuf, NULL, 0 );
  395.     else
  396.         sio_init( 2, COMBASE2, COMIRQ2, combuf, combuf, NULL, 0 );
  397.     sio_setup (port, (DWORD)9600, 8, SIO_PARITY_NONE, 2, 0);
  398.     kwindow( 1,2, 80,25 );
  399.     do {    /* structured exception handler-like */
  400.         if ( kbhit() ) {
  401.             key = bioskey( 0 );
  402.             b = key & 255;
  403.             if ( key == 0x4900 ) { /* PgUp */
  404.                 cprintf("uploading ...nr");
  405.                 get_userfilename( "file to upload", buffer );
  406.                 upload( port, buffer );
  407.                 cprintf("...donern");
  408.             } else if ( key == 0x5100 ) {
  409.                 cprintf("downloading ...nr");
  410.                 get_userfilename( "file to write to", buffer );
  411.                 download( port, buffer );
  412.                 cprintf("...donern");
  413.             } else if ( b == 27 ) break;
  414.             else sio_writebyte( port, b );
  415.         }
  416.         if ( sio_recv_waiting( port ) ) {
  417.             b = sio_readbyte( port );
  418.             putch( b );
  419.         }
  420.     } while ( 1 );
  421.     sio_close (port);
  422.     rt_halt("user terminated");
  423. }
  424. /*********************************************************************/
  425. main ()
  426. {
  427.     int comport;
  428.     int msg;
  429.     DWORD data;
  430.     rt_init (200);
  431.     rt_timerfreq( 100 );
  432.     kpreemptive = 1;
  433.     kctrlbreak = 0;     /* ignore ctrl breaks */
  434.     clrscr();
  435.     cprintf("Esc to escape..., PgUp to invoke upload, PgDn for download");
  436.     /* determine the desired com port */
  437.     if ((comport = (int)GetIniDWORD("xmodem.ini","default","comport",-1))==-1){
  438.         comport = 1;
  439.         SetIniDWORD("xmodem.ini","default","comport", comport );
  440.     }
  441.     rt_newthread(term_thread, comport , 8192, 0, "term session" );
  442.     kreadmessage( &msg, &data );
  443. }