ymodem_tx.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_tx.h"
00034 
00035 
00036 #include <string.h> // for memcpy and memset
00037 
00038 
00042 const unsigned SendTimeout = 11*1000;
00043 
00044 
00054 int YModemTx::ProcessResponse(int c)
00055     {
00056     if(c<0)
00057         return c;
00058     if(c==CAN)
00059         {
00060         if(CancelCount++)
00061             return ErrorTranferTerminatedByReceiver;
00062         return 0;
00063         }
00064     CancelCount = 0;
00065     if(c==ACK)
00066         return 1;
00067     return 0;
00068     }
00069 
00070 
00078 int YModemTx::SendInitialise(unsigned timeout)
00079     {
00080     if(timeout<SendTimeout)
00081         timeout = SendTimeout;
00082 
00083     while(InChar(0)>=0) // flush input buffers
00084         {}
00085 
00086     CancelCount = 0;
00087 
00088     int c;
00089     for(;;)
00090         {
00091         const unsigned timeoutStep = 10;
00092         c = InChar(timeoutStep);
00093         if(c=='G')
00094             {
00095             SendCRC = true;
00096             WaitForBlockACK = false;
00097             break;
00098             }
00099         else if(c=='C')
00100             {
00101             SendCRC = true;
00102             WaitForBlockACK = true;
00103             break;
00104             }
00105         else if(c==NAK)
00106             {
00107             SendCRC = false;
00108             WaitForBlockACK = true;
00109             break;
00110             }
00111         if(c<0 && c!=ErrorTimeout)
00112             return c;
00113         if(timeout<timeoutStep)
00114             return ErrorTimeout;
00115         timeout -= timeoutStep;
00116         }
00117 
00118     ModeChar = c;
00119     return 0;
00120     }
00121 
00122 
00135 int YModemTx::SendBlock(const uint8_t* data, size_t size)
00136     {
00137     uint8_t block[1+2+1024+2];  // buffer to hold data in the block
00138     int retryCount = 10;        // number of attempts to send the block
00139     bool waitForBlockACK = WaitForBlockACK;
00140 
00141 change_mode:
00142 
00143     size_t blockSize = (Use1KBlocks && size>=1024) ? 1024 : 128;
00144     size_t dataSize = size<blockSize ? size : blockSize;    // size of data to send in block
00145 
00146     if(!dataSize)
00147         {
00148         // all bytes sent, so end transfer by sending a single EOT...
00149         block[0] = EOT;
00150         blockSize = 1;
00151         waitForBlockACK = true;
00152         }
00153     else
00154         {
00155         // make block header...
00156         block[0] = blockSize==1024 ? STX : SOH;
00157         block[1] = BlockNumber&0xffu;
00158         block[2] = (~BlockNumber)&0xffu;
00159 
00160         // copy data for block (padding with EOF)...
00161         memcpy(block+3,data,dataSize);
00162         memset(block+3+dataSize,26,blockSize-dataSize);
00163 
00164         // append checksum/crc...
00165         if(SendCRC)
00166             {
00167             uint16_t crc = CRC16(block+3,blockSize);
00168             blockSize += 3;
00169             block[blockSize++] = (uint8_t)(crc>>8);
00170             block[blockSize++] = (uint8_t)crc;
00171             }
00172         else
00173             {
00174             uint8_t sum = Checksum(block+3,blockSize);
00175             blockSize += 3;
00176             block[blockSize++] = sum;
00177             }
00178         }
00179 
00180 do_retry:
00181     // count attenpts...
00182     if(!retryCount--)
00183         return ErrorBlockRetriesExceded;
00184 
00185     uint8_t* out = block;
00186     size_t outSize = blockSize;
00187     for(;;)
00188         {
00189         // send some data...
00190         int result = Port.Out(out,outSize,1000);
00191         if(result<0)
00192             return result; // return error
00193         if(result==0)
00194             return ErrorTimeout;
00195 
00196         // adjust for data remaining...
00197         out += result;
00198         outSize -= result;
00199 
00200         // end if done...
00201         if(!outSize)
00202             break;
00203 
00204         // poll for signal from receiver...
00205         result = ProcessResponse(InChar(10));
00206         if(result==ErrorTimeout)
00207             continue; // nothing received
00208         if(result<0)
00209             return result; // return error
00210         if(!result)
00211             goto retry; // negative acknowledge received
00212         }
00213 
00214     if(waitForBlockACK)
00215         {
00216         // wait for up to one second for block to be acknowledged...
00217         int c = InChar(1000);
00218         int result = ProcessResponse(c);
00219         if(result<0)
00220             return result; // return error
00221         if(!result)
00222             {
00223             // negagtive acknowledge received...
00224             if(c=='C' && !SendCRC)
00225                 {
00226                 // change to CRC mode if receiver sent 'C', and retry...
00227                 SendCRC = true;
00228                 goto change_mode;
00229                 }
00230             goto retry;
00231             }
00232         }
00233     else
00234         {
00235         // check for receiver sending a cancel byte...
00236         int result = ProcessResponse(InChar(0));
00237         if(result<0)
00238             {
00239             if(result!=ErrorTimeout)
00240                 return result; // return error if it's not a timeout
00241             }
00242         else
00243             {
00244             // ignore other responses
00245             }
00246         }
00247 
00248     // block transferred OK...
00249     ++BlockNumber;
00250     return dataSize;
00251 
00252 retry:
00253     while(InChar(500)>=0) // flush input buffers
00254         {}
00255     goto do_retry;
00256     }
00257 
00258 
00270 int YModemTx::SendData(const uint8_t* data, size_t size)
00271     {
00272     do
00273         {
00274         int result = SendBlock(data, size);
00275         if(result<0)
00276             return result;
00277         data += result;
00278         size -= result;
00279         }
00280     while(size);
00281     return 0;
00282     }
00283 
00284 
00294 int YModemTx::SendAll(InStream& in)
00295     {
00296     BlockNumber = 1; // first block to send is number one
00297     size_t size;
00298     do
00299         {
00300         // get data from input stream...
00301         uint8_t data[1024];
00302         int result = in.In(data,sizeof(data));
00303         if(result<0)
00304             return ErrorInputStreamError;
00305 
00306         // send data...
00307         size = result;
00308         result = SendData(data,size);
00309         if(result<0)
00310             return result;
00311         }
00312     while(size); // end when no more data left
00313     return 0;
00314     }
00315 
00316 
00326 int YModemTx::MakeBlock0(uint8_t* buffer, const char* fileName, size_t fileSize)
00327     {
00328     // setup buffer for block 0...
00329     uint8_t* out = buffer;
00330     uint8_t* outEnd = buffer+128-1;
00331     memset(buffer,0,128);
00332 
00333     // copy file name to block data...
00334     while(out<outEnd)
00335         {
00336         char c = *fileName++;
00337         if(c>='A' && c<='Z')
00338             c += 'a'-'A';   // convert name to lower-case
00339         else if(c=='\\')
00340             c = '/';        // convert back-slash to forward-slash
00341         *out++ = c;
00342         if(!c)
00343             break;          // end of name
00344         }
00345 
00346     // convert fileSize to a decimal number...
00347     char length[sizeof(size_t)*3+1]; // buffer big enough to hold length as decimal number
00348     char* lenEnd = length+sizeof(length);
00349     char* len = lenEnd;
00350     do
00351         {
00352         *--len = '0'+fileSize%10;   // prepend digit to buffer
00353         fileSize /= 10;
00354         }
00355     while(fileSize);                // end when all digits done
00356 
00357     // append file length to block data...
00358     while(out<outEnd && len<lenEnd)
00359         *out++ = *len++;
00360 
00361     // check that buffer was big enough...
00362     if(out>=outEnd)
00363         return ErrorFileNameTooLong;
00364 
00365     return 0; // OK
00366     }
00367 
00368 
00369 YModemTx::YModemTx(SerialPort& port)
00370     : YModem(port)
00371     {
00372     }
00373 
00374 
00375 int YModemTx::SendX(InStream& in, unsigned timeout, bool kMode)
00376     {
00377     Use1KBlocks = kMode;
00378     int result = SendInitialise(timeout);
00379     if(result<0)
00380         return result;
00381     return SendAll(in);
00382     }
00383 
00384 
00385 int YModemTx::SendY(const char* fileName, size_t size, InStream& in, unsigned timeout)
00386     {
00387     Use1KBlocks = true;
00388     uint8_t buffer[128];
00389     int result = MakeBlock0(buffer,fileName,size);
00390     if(result<0)
00391         return result;
00392 
00393     result = SendInitialise(timeout);
00394     if(result<0 && result!=ErrorBlockRetriesExceded)
00395         return result;
00396 
00397     BlockNumber = 0;
00398     result = SendBlock(buffer,sizeof(buffer));
00399     if(result<0)
00400         return result;
00401 
00402     result = InChar(SendTimeout);
00403     if(result<0)
00404         return result;
00405     if(result!=ModeChar)
00406         return ErrorReceiverNotBehaving;
00407 
00408     result = SendAll(in);
00409     if(result<0)
00410         return result;
00411 
00412     result = InChar(SendTimeout);
00413     if(result<0)
00414         return result;
00415     if(result!=ModeChar)
00416         return ErrorReceiverNotBehaving;
00417 
00418     memset(buffer,0,sizeof(buffer));
00419     BlockNumber = 0;
00420     result = SendBlock(buffer,sizeof(buffer));
00421     if(result<0)
00422         return result;
00423 
00424     return 0;
00425     }
00426 

Generated by  doxygen 1.6.1