ymodem_rx.cpp

Go to the documentation of this file.
00001 /*
00002 This program is distributed under the terms of the 'MIT license'. The text
00003 of this licence follows...
00004 
00005 Copyright (c) 2006 J.D.Medhurst (a.k.a. Tixy)
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a copy
00008 of this software and associated documentation files (the "Software"), to deal
00009 in the Software without restriction, including without limitation the rights
00010 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00011 copies of the Software, and to permit persons to whom the Software is
00012 furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00020 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00022 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00023 THE SOFTWARE.
00024 */
00025 
00032 #include "../common/common.h"
00033 #include "ymodem_rx.h"
00034 
00035 
00036 #include <string.h> // for memcpy and memset
00037 
00038 
00042 const unsigned ReceiveTimeout = 1*1000;
00043 
00044 
00045 YModemRx::YModemRx(SerialPort& port)
00046     : YModem(port)
00047     {
00048     }
00049 
00050 
00058 int YModemRx::OutChar(uint8_t c)
00059     {
00060     int result = Port.Out(&c,1,1000);
00061     if(result==0)
00062         return ErrorTimeout;
00063     return result;
00064     }
00065 
00066 
00082 int YModemRx::ReceiveBlock(OutStream& out)
00083     {
00084     uint8_t block[1+2+1024+2];  // buffer to hold data for the block
00085 
00086     int retryCount = 10;        // number of attempts to send the block
00087 do_retry:
00088     // count attenpts...
00089     if(!retryCount--)
00090         return ErrorBlockRetriesExceded;
00091 
00092     size_t blockSize = 128;     // assume 128 byte blocks until we know better
00093     uint8_t* data = block;
00094     size_t size = 1+2+blockSize+1+ExpectCRC;
00095     do
00096         {
00097         // receive some data...
00098         int result = Port.In(data,size,ReceiveTimeout);
00099         if(result<0)
00100             return result;
00101 
00102         // check for timeout...
00103         if(result==0)
00104             {
00105             if(data==block)
00106                 return ErrorTimeout; // nothing received
00107             // not all data received so some may have been lost...
00108             goto retry;
00109             }
00110 
00111         // check for end of transmission...
00112         if(block[0]==EOT)
00113             {
00114             int result = OutChar(ACK);
00115             if(result<0)
00116                 return result;
00117             return out.Out(0,0); // store zero bytes to signal end
00118             }
00119 
00120         // check for 1K blocks...
00121         if(block[0]==STX && blockSize!=1024)
00122             {
00123             blockSize = 1024;
00124             size += 1024-128;
00125             }
00126 
00127         // adjust pointers for next chunk of data...
00128         data += result;
00129         size -= result;
00130         }
00131     while(size);
00132 
00133     if(block[0] != (blockSize==1024 ? STX : SOH))
00134         goto retry; // block start invalid
00135     if(block[1] != (block[2]^0xff))
00136         goto retry; // block number invalid
00137     if(block[1] != (BlockNumber&0xffu))
00138         goto retry; // block number invalid
00139 
00140     if(ExpectCRC)
00141         {
00142         uint16_t crc = CRC16(block+1+2,blockSize);
00143         uint16_t receiveCrc = (block[1+2+blockSize+0]<<8)+block[1+2+blockSize+1];
00144         if(crc!=receiveCrc)
00145             goto retry; // crc invalid
00146         }
00147     else
00148         {
00149         uint8_t sum = Checksum(block+1+2,blockSize);
00150         if(sum!=block[1+2+blockSize])
00151             goto retry; // checksum invalid
00152         }
00153 
00154     {
00155     //  output received data...
00156     int result = out.Out(block+3,blockSize);
00157     if(result<0)
00158         return result;
00159 
00160     // block transferred OK...
00161     if(SendBlockACK)
00162         {
00163         result = OutChar(ACK);
00164         if(result<0)
00165             return result;
00166         }
00167     ++BlockNumber;
00168     return blockSize;
00169     }
00170 
00171 retry:
00172     while(InChar(ReceiveTimeout)>=0) // flush input buffers
00173         {}
00174     int result = OutChar(NAKChar);
00175     if(result<0)
00176         return result;
00177     goto do_retry;
00178     }
00179 
00180 
00189 int YModemRx::ReceiveInitialise(OutStream& out, unsigned timeout)
00190     {
00191     for(;;)
00192         {
00193         int result = OutChar(ModeChar);
00194         if(result!=ErrorTimeout)
00195             {
00196             if(result<0)
00197                 return result;
00198             result = ReceiveBlock(out);
00199             if(result!=ErrorTimeout)
00200                 return result;
00201             }
00202         if(timeout<ReceiveTimeout)
00203             return ErrorTimeout;
00204         timeout -= ReceiveTimeout;
00205         }
00206     }
00207 
00208 
00209 int YModemRx::ReceiveX(OutStream& out, unsigned timeout, bool useCrc)
00210     {
00211     ModeChar = useCrc ? (uint8_t)'C' : (uint8_t)NAK;
00212     ExpectCRC = useCrc;
00213     SendBlockACK = true;
00214     NAKChar = ModeChar;
00215 
00216     // get first block...
00217     BlockNumber = 1;
00218     int result = ReceiveInitialise(out,timeout);
00219     if(result==ErrorTimeout)
00220         return result;
00221 
00222     // get rest of data...
00223     while(result)
00224         {
00225         if(result<0)
00226             goto error;
00227         result = ReceiveBlock(out);
00228         }
00229 
00230     // done...
00231     return 0;
00232 
00233 error:
00234     Cancel();
00235     return result;
00236     }
00237 
00238 
00242 inline YModemRx::OutBlock0::OutBlock0()
00243     : Size(0)
00244     {}
00245 
00246 
00247 int YModemRx::OutBlock0::Open(const char* /*fileName*/, size_t /*size*/)
00248     {
00249     return ErrorOutputStreamError;
00250     }
00251 
00252 
00261 int YModemRx::OutBlock0::Out(const uint8_t* data, size_t size)
00262     {
00263     memcpy(Buffer,data,size);
00264     Size = size;
00265     return 0;
00266     }
00267 
00268 
00277 int YModemRx::OutBlock0::Parse(const char*& fileName, size_t& fileSize)
00278     {
00279     const char* buffer = (char*)Buffer;
00280     size_t bufferSize = Size;
00281 
00282     // fileName is at start of block...
00283     fileName = (char*)buffer;
00284     const char* bufferEnd = buffer+bufferSize;
00285     while(*buffer++)
00286         {
00287         if(buffer>=bufferEnd)
00288             return YModemRx::ErrorReceivedBadData;
00289         }
00290 
00291     // size (as decimal number) follows name string...
00292     size_t size = 0;
00293     while(buffer<bufferEnd)
00294         {
00295         unsigned digit = *buffer++;
00296         if(!digit)
00297             break;
00298         if(digit==' ')
00299             break;
00300         digit -= '0';
00301         if(digit>9)
00302             return YModemRx::ErrorReceivedBadData;
00303         size_t oldSize = size;
00304         size = size*10+digit;
00305         if(size<oldSize)
00306             return YModemRx::ErrorReceivedBadData; // overflow
00307         }
00308     fileSize = size;
00309 
00310     // done...
00311     return 0;
00312     }
00313 
00314 
00315 int YModemRx::ReceiveY(OutStream& out, unsigned timeout, bool gMode)
00316     {
00317     ModeChar = gMode ? 'G' : 'C';
00318     ExpectCRC = true;
00319     SendBlockACK = !gMode;
00320     NAKChar = ModeChar;
00321     unsigned fileCount = 0;
00322     int result;
00323 
00324     for(;;)
00325         {
00326         // get block zero...
00327         YModemRx::OutBlock0 block0;
00328         BlockNumber = 0;
00329         result = ReceiveInitialise(block0,timeout);
00330         if(result==ErrorTimeout && !fileCount)
00331             return result; // sender not ready
00332         if(result<0)
00333             goto error;
00334         if(result!=128)
00335             return 0;
00336 
00337         // process block zero...
00338         const char* fileName;
00339         size_t size;
00340         result = block0.Parse(fileName,size);
00341         if(result<0)
00342             goto error;
00343         if(fileName[0]==0)
00344             return 0; // empty file name indicates end of transfer
00345         if(fileCount)
00346             return ErrorMultipleFilesSent; // we only support single file transfers
00347         result = out.Open(fileName,size);
00348         if(result<0)
00349             goto error;
00350 
00351         // get rest of data...
00352         NAKChar = NAK;
00353         result = OutChar(ModeChar);
00354         while(result)
00355             {
00356             if(result<0)
00357                 goto error;
00358             result = ReceiveBlock(out);
00359             }
00360 
00361         // file received OK.
00362         ++fileCount;
00363         }
00364 error:
00365     Cancel();
00366     return result;
00367     }

Generated by  doxygen 1.6.1