Add Ymodem transmit translated to boosts

This commit is contained in:
2021-08-24 16:21:38 +02:00
parent ac26fc6bdd
commit 2b2cbe655d
16 changed files with 1077 additions and 1806 deletions

View File

@@ -4,12 +4,13 @@ project(TEST)
## Target
set(CMAKE_CXX_STANDARD 11)
set(TEST_SRCS main.cpp TimeoutSerial.cpp XModem.cpp)
set(TEST_SRCS main.cpp TimeoutSerial.cpp xymodem.cpp)
add_definitions(-DBOOST_LOG_DYN_LINK)
add_executable(timeout ${TEST_SRCS})
set(CMAKE_BUILD_TYPE Debug)
## Link libraries
set(BOOST_LIBS date_time system)
set(BOOST_LIBS date_time system log)
find_package(Boost COMPONENTS ${BOOST_LIBS} REQUIRED)
target_link_libraries(timeout ${Boost_LIBRARIES})
find_package(Threads REQUIRED)

View File

@@ -150,33 +150,6 @@ TimeoutSerial.cpp.s:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/TimeoutSerial.cpp.s
.PHONY : TimeoutSerial.cpp.s
XModem.o: XModem.cpp.o
.PHONY : XModem.o
# target to build an object file
XModem.cpp.o:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/XModem.cpp.o
.PHONY : XModem.cpp.o
XModem.i: XModem.cpp.i
.PHONY : XModem.i
# target to preprocess a source file
XModem.cpp.i:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/XModem.cpp.i
.PHONY : XModem.cpp.i
XModem.s: XModem.cpp.s
.PHONY : XModem.s
# target to generate assembly for a file
XModem.cpp.s:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/XModem.cpp.s
.PHONY : XModem.cpp.s
main.o: main.cpp.o
.PHONY : main.o
@@ -216,9 +189,6 @@ help:
@echo "... TimeoutSerial.o"
@echo "... TimeoutSerial.i"
@echo "... TimeoutSerial.s"
@echo "... XModem.o"
@echo "... XModem.i"
@echo "... XModem.s"
@echo "... main.o"
@echo "... main.i"
@echo "... main.s"

44
Thread.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef THREAD_HH
#define THREAD_HH
#include <boost/thread.hpp>
#include <iostream>
using namespace std;
template<typename T>
class Thread {
private:
boost::thread* thread;
bool isRunning;
public:
// Ctor
Thread() : thread(NULL), isRunning(false) {
}
virtual ~Thread() {
kill();
}
void start() {
T* derived = dynamic_cast<T*>(this);
thread = new boost::thread(boost::bind(&Thread::doIt, this, boost::ref(*derived)));
}
void doIt(T& derived) {
}
bool isFinished() {
return isRunning;
}
void join() {
if(thread) {
thread->join();
}
}
void kill() {
if(thread) {
thread->interrupt();
delete thread;
thread = NULL;
}
}
};
#endif

View File

@@ -1,369 +0,0 @@
// This code was taken from: https://github.com/mgk/arduino-xmodem
// (https://code.google.com/archive/p/arduino-xmodem)
// which was released under GPL V3:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// -----------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include "XModem.h"
#ifdef UTEST
#include "CppUTestExt/MockSupport.h"
#endif
const unsigned char XModem::NACK = 21;
const unsigned char XModem::ACK = 6;
const unsigned char XModem::SOH = 1;
const unsigned char XModem::EOT = 4;
const unsigned char XModem::CAN = 0x18;
const int XModem::receiveDelay=7;
const int XModem::rcvRetryLimit = 10;
XModem::XModem(TimeoutSerial *serial)
{
this->serial = serial;
this->dataHandler = NULL;
}
XModem::XModem(TimeoutSerial *serial,
bool (*dataHandler)(unsigned long number, char *buffer, int len))
{
this->serial = serial;
this->dataHandler = dataHandler;
}
bool XModem::dataAvail(int delay)
{
if (this->byte != -1)
return true;
if ((this->byte = this->serial->readChar(delay)) != -1)
return true;
else
return false;
}
int XModem::dataRead(int delay)
{
int b;
if(this->byte != -1)
{
b = this->byte;
this->byte = -1;
return b;
}
return this->serial->readChar(delay);
}
void XModem::dataWrite(char symbol)
{
this->serial->write(&symbol, 1);
}
bool XModem::receiveFrameNo()
{
unsigned char num =
(unsigned char)this->dataRead(XModem::receiveDelay);
unsigned char invnum =
(unsigned char)this->dataRead(XModem::receiveDelay);
this->repeatedBlock = false;
//check for repeated block
if (invnum == (255-num) && num == this->blockNo-1) {
this->repeatedBlock = true;
return true;
}
if(num != this-> blockNo || invnum != (255-num))
return false;
else
return true;
}
bool XModem::receiveData()
{
for(int i = 0; i < 128; i++) {
int byte = this->dataRead(XModem::receiveDelay);
if(byte != -1)
this->buffer[i] = (unsigned char)byte;
else
return false;
}
return true;
}
bool XModem::checkCrc()
{
unsigned short frame_crc = ((unsigned char)this->
dataRead(XModem::receiveDelay)) << 8;
frame_crc |= (unsigned char)this->dataRead(XModem::receiveDelay);
//now calculate crc on data
unsigned short crc = this->crc16_ccitt(this->buffer, 128);
if(frame_crc != crc)
return false;
else
return true;
}
bool XModem::checkChkSum()
{
unsigned char frame_chksum = (unsigned char)this->
dataRead(XModem::receiveDelay);
//calculate chksum
unsigned char chksum = 0;
for(int i = 0; i< 128; i++) {
chksum += this->buffer[i];
}
if(frame_chksum == chksum)
return true;
else
return false;
}
bool XModem::sendNack()
{
this->dataWrite(XModem::NACK);
this->retries++;
if(this->retries < XModem::rcvRetryLimit)
return true;
else
return false;
}
bool XModem::receiveFrames(transfer_t transfer)
{
this->blockNo = 1;
this->blockNoExt = 1;
this->retries = 0;
while (1) {
char cmd = this->dataRead(10);
switch(cmd){
case XModem::SOH:
if (!this->receiveFrameNo()) {
if (this->sendNack())
break;
else
return false;
}
if (!this->receiveData()) {
if (this->sendNack())
break;
else
return false;
};
if (transfer == Crc) {
if (!this->checkCrc()) {
if (this->sendNack())
break;
else
return false;
}
} else {
if(!this->checkChkSum()) {
if (this->sendNack())
break;
else
return false;
}
}
//callback
if(this->dataHandler != NULL &&
this->repeatedBlock == false)
if(!this->dataHandler(this->blockNoExt,
this->buffer, 128)) {
return false;
}
//ack
this->dataWrite(XModem::ACK);
if(this->repeatedBlock == false)
{
this->blockNo++;
this->blockNoExt++;
}
this->retries = 0;
break;
case XModem::EOT:
this->dataWrite(XModem::ACK);
return true;
case XModem::CAN:
//wait second CAN
if(this->dataRead(XModem::receiveDelay) ==
XModem::CAN) {
this->dataWrite(XModem::ACK);
//this->flushInput();
return false;
}
//something wrong
this->dataWrite(XModem::CAN);
this->dataWrite(XModem::CAN);
this->dataWrite(XModem::CAN);
return false;
default:
//something wrong
this->dataWrite(XModem::CAN);
this->dataWrite(XModem::CAN);
this->dataWrite(XModem::CAN);
return false;
}
}
}
void XModem::init()
{
//set preread byte
this->byte = -1;
}
bool XModem::receive()
{
this->init();
for (int i =0; i < 128; i++)
{
this->dataWrite('C');
if (this->dataAvail(1000))
return receiveFrames(Crc);
}
for (int i =0; i < 128; i++)
{
this->dataWrite(XModem::NACK);
if (this->dataAvail(1000))
return receiveFrames(ChkSum);
}
return false;
}
unsigned short XModem::crc16_ccitt(char *buf, int size)
{
unsigned short crc = 0;
while (--size >= 0) {
int i;
crc ^= (unsigned short) *buf++ << 8;
for (i = 0; i < 8; i++)
if (crc & 0x8000)
crc = crc << 1 ^ 0x1021;
else
crc <<= 1;
}
return crc;
}
unsigned char XModem::generateChkSum(const char *buf, int len)
{
//calculate chksum
unsigned char chksum = 0;
for(int i = 0; i< len; i++) {
chksum += buf[i];
}
return chksum;
}
bool XModem::transmitFrames(transfer_t transfer)
{
this->blockNo = 1;
this->blockNoExt = 1;
// use this only in unit tetsing
//memset(this->buffer, 'A', 128);
while(1)
{
//get data
if (this->dataHandler != NULL)
{
if( false ==
this->dataHandler(this->blockNoExt, this->buffer+3,
128))
{
//end of transfer
this->dataWrite(XModem::EOT);
//wait ACK
if (this->dataRead(XModem::receiveDelay) ==
XModem::ACK)
return true;
else
return false;
}
}
else
{
//cancel transfer - send CAN twice
this->dataWrite(XModem::CAN);
this->dataWrite(XModem::CAN);
//wait ACK
if (this->dataRead(XModem::receiveDelay) ==
XModem::ACK)
return true;
else
return false;
}
//SOH
buffer[0] = XModem::SOH;
//frame number
buffer[1] = this->blockNo;
//inv frame number
buffer[2] = (unsigned char)(255-(this->blockNo));
//(data is already in buffer starting at byte 3)
//checksum or crc
if (transfer == ChkSum) {
buffer[3+128] = this->generateChkSum(buffer+3, 128);
this->sendData(buffer, 3+128+1);
} else {
unsigned short crc;
crc = this->crc16_ccitt(this->buffer+3, 128);
buffer[3+128+0] = (unsigned char)(crc >> 8);
buffer[3+128+1] = (unsigned char)(crc);;
this->sendData(buffer, 3+128+2);
}
//TO DO - wait NACK or CAN or ACK
int ret = this->dataRead(XModem::receiveDelay);
switch(ret)
{
case XModem::ACK: //data is ok - go to next chunk
this->blockNo++;
this->blockNoExt++;
continue;
case XModem::NACK: //resend data
continue;
case XModem::CAN: //abort transmision
return false;
}
}
return false;
}
bool XModem::transmit()
{
int retry = 0;
int sym;
this->init();
//wait for CRC transfer
while(retry < 256)
{
if(this->dataAvail(1000))
{
sym = this->dataRead(1); //data is here - no delay
if(sym == 'C')
return this->transmitFrames(Crc);
if(sym == XModem::NACK)
return this->transmitFrames(ChkSum);
}
retry++;
}
return false;
}

View File

@@ -1,81 +0,0 @@
// This code was taken from: https://code.google.com/archive/p/arduino-xmodem
// (https://code.google.com/archive/p/arduino-xmodem)
// which was released under GPL V3:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// -----------------------------------------------------------------------------
#include "TimeoutSerial.h"
typedef enum {
Crc,
ChkSum
} transfer_t;
class XModem {
private:
//delay when receive bytes in frame - 7 secs
static const int receiveDelay;
//retry limit when receiving
static const int rcvRetryLimit;
//holds readed byte (due to dataAvail())
int byte;
//expected block number
unsigned char blockNo;
//extended block number, send to dataHandler()
unsigned long blockNoExt;
//retry counter for NACK
int retries;
//buffer
char buffer[133];
//repeated block flag
bool repeatedBlock;
TimeoutSerial *serial;
int (*recvChar)(int);
void (*sendData)(const char *data, int len);
bool (*dataHandler)(unsigned long number, char *buffer, int len);
unsigned short crc16_ccitt(char *buf, int size);
bool dataAvail(int delay);
int dataRead(int delay);
void dataWrite(char symbol);
bool receiveFrameNo(void);
bool receiveData(void);
bool checkCrc(void);
bool checkChkSum(void);
bool receiveFrames(transfer_t transfer);
bool sendNack(void);
void init(void);
void sendSerialData(const char *data, int len);
int recvSerialChar(int);
bool transmitFrames(transfer_t);
unsigned char generateChkSum(const char *buffer, int len);
public:
static const unsigned char NACK;
static const unsigned char ACK;
static const unsigned char SOH;
static const unsigned char EOT;
static const unsigned char CAN;
XModem(TimeoutSerial *serial);
XModem(TimeoutSerial *serial,bool (*dataHandler)(unsigned long, char*, int));
bool receive();
bool transmit();
};

View File

@@ -41,6 +41,18 @@
"type" : "PATH",
"value" : "/usr/include"
},
{
"name" : "Boost_LOG_LIBRARY_RELEASE",
"properties" :
[
{
"name" : "HELPSTRING",
"value" : ""
}
],
"type" : "STRING",
"value" : "/usr/lib/x86_64-linux-gnu/libboost_log.so.1.71.0"
},
{
"name" : "Boost_SYSTEM_LIBRARY_RELEASE",
"properties" :
@@ -1231,7 +1243,7 @@
}
],
"type" : "INTERNAL",
"value" : "[/usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake][cfound components: date_time system ][v1.71.0()]"
"value" : "[/usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake][cfound components: date_time system log ][v1.71.0()]"
},
{
"name" : "FIND_PACKAGE_MESSAGE_DETAILS_Threads",
@@ -1269,6 +1281,38 @@
"type" : "STATIC",
"value" : "/home/jaro/serialport-downloader"
},
{
"name" : "boost_atomic_DIR",
"properties" :
[
{
"name" : "ADVANCED",
"value" : "1"
},
{
"name" : "HELPSTRING",
"value" : "The directory containing a CMake configuration file for boost_atomic."
}
],
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_atomic-1.71.0"
},
{
"name" : "boost_chrono_DIR",
"properties" :
[
{
"name" : "ADVANCED",
"value" : "1"
},
{
"name" : "HELPSTRING",
"value" : "The directory containing a CMake configuration file for boost_chrono."
}
],
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_chrono-1.71.0"
},
{
"name" : "boost_date_time_DIR",
"properties" :
@@ -1285,6 +1329,22 @@
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_date_time-1.71.0"
},
{
"name" : "boost_filesystem_DIR",
"properties" :
[
{
"name" : "ADVANCED",
"value" : "1"
},
{
"name" : "HELPSTRING",
"value" : "The directory containing a CMake configuration file for boost_filesystem."
}
],
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_filesystem-1.71.0"
},
{
"name" : "boost_headers_DIR",
"properties" :
@@ -1301,6 +1361,50 @@
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_headers-1.71.0"
},
{
"name" : "boost_log_DIR",
"properties" :
[
{
"name" : "ADVANCED",
"value" : "1"
},
{
"name" : "HELPSTRING",
"value" : "The directory containing a CMake configuration file for boost_log."
}
],
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_log-1.71.0"
},
{
"name" : "boost_program_options_DIR",
"properties" :
[
{
"name" : "HELPSTRING",
"value" : "The directory containing a CMake configuration file for boost_program_options."
}
],
"type" : "PATH",
"value" : "boost_program_options_DIR-NOTFOUND"
},
{
"name" : "boost_regex_DIR",
"properties" :
[
{
"name" : "ADVANCED",
"value" : "1"
},
{
"name" : "HELPSTRING",
"value" : "The directory containing a CMake configuration file for boost_regex."
}
],
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_regex-1.71.0"
},
{
"name" : "boost_system_DIR",
"properties" :
@@ -1316,6 +1420,22 @@
],
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_system-1.71.0"
},
{
"name" : "boost_thread_DIR",
"properties" :
[
{
"name" : "ADVANCED",
"value" : "1"
},
{
"name" : "HELPSTRING",
"value" : "The directory containing a CMake configuration file for boost_thread."
}
],
"type" : "PATH",
"value" : "/usr/lib/x86_64-linux-gnu/cmake/boost_thread-1.71.0"
}
],
"kind" : "cache",

View File

@@ -38,7 +38,7 @@
{
"directoryIndex" : 0,
"id" : "timeout::@6890427a1f51a3e7e1df",
"jsonFile" : "target-timeout-Debug-6fb942df9d75cf9b39de.json",
"jsonFile" : "target-timeout-Debug-85a0f71a1db986bd99c5.json",
"name" : "timeout",
"projectIndex" : 0
}

View File

@@ -25,7 +25,7 @@
"objects" :
[
{
"jsonFile" : "codemodel-v2-a67eb6c77230df2253b5.json",
"jsonFile" : "codemodel-v2-4167e22d1c847713af64.json",
"kind" : "codemodel",
"version" :
{
@@ -34,7 +34,7 @@
}
},
{
"jsonFile" : "cache-v2-053ee6e25f83c56b3f05.json",
"jsonFile" : "cache-v2-9de8371d03fa8cbf9def.json",
"kind" : "cache",
"version" :
{
@@ -67,7 +67,7 @@
"responses" :
[
{
"jsonFile" : "cache-v2-053ee6e25f83c56b3f05.json",
"jsonFile" : "cache-v2-9de8371d03fa8cbf9def.json",
"kind" : "cache",
"version" :
{
@@ -76,7 +76,7 @@
}
},
{
"jsonFile" : "codemodel-v2-a67eb6c77230df2253b5.json",
"jsonFile" : "codemodel-v2-4167e22d1c847713af64.json",
"kind" : "codemodel",
"version" :
{

View File

@@ -11,7 +11,8 @@
"commands" :
[
"add_executable",
"target_link_libraries"
"target_link_libraries",
"add_definitions"
],
"files" :
[
@@ -25,13 +26,19 @@
{
"command" : 0,
"file" : 0,
"line" : 8,
"line" : 9,
"parent" : 0
},
{
"command" : 1,
"file" : 0,
"line" : 14,
"line" : 15,
"parent" : 0
},
{
"command" : 2,
"file" : 0,
"line" : 8,
"parent" : 0
}
]
@@ -54,13 +61,37 @@
"backtrace" : 2,
"define" : "BOOST_ALL_NO_LIB"
},
{
"backtrace" : 2,
"define" : "BOOST_ATOMIC_DYN_LINK"
},
{
"backtrace" : 2,
"define" : "BOOST_CHRONO_DYN_LINK"
},
{
"backtrace" : 2,
"define" : "BOOST_DATE_TIME_DYN_LINK"
},
{
"backtrace" : 2,
"define" : "BOOST_FILESYSTEM_DYN_LINK"
},
{
"backtrace" : 3,
"define" : "BOOST_LOG_DYN_LINK"
},
{
"backtrace" : 2,
"define" : "BOOST_REGEX_DYN_LINK"
},
{
"backtrace" : 2,
"define" : "BOOST_SYSTEM_DYN_LINK"
},
{
"backtrace" : 2,
"define" : "BOOST_THREAD_DYN_LINK"
}
],
"language" : "CXX",
@@ -95,6 +126,40 @@
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_system.so.1.71.0",
"role" : "libraries"
},
{
"backtrace" : 2,
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_log.so.1.71.0",
"role" : "libraries"
},
{
"fragment" : "-lpthread",
"role" : "libraries"
},
{
"backtrace" : 2,
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_date_time.so.1.71.0",
"role" : "libraries"
},
{
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_chrono.so.1.71.0",
"role" : "libraries"
},
{
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_filesystem.so.1.71.0",
"role" : "libraries"
},
{
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_regex.so.1.71.0",
"role" : "libraries"
},
{
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_thread.so.1.71.0",
"role" : "libraries"
},
{
"fragment" : "/usr/lib/x86_64-linux-gnu/libboost_atomic.so.1.71.0",
"role" : "libraries"
},
{
"fragment" : "-lpthread",
"role" : "libraries"
@@ -138,7 +203,7 @@
{
"backtrace" : 1,
"compileGroupIndex" : 0,
"path" : "XModem.cpp",
"path" : "xymodem.cpp",
"sourceGroupIndex" : 0
}
],

View File

@@ -150,33 +150,6 @@ TimeoutSerial.cpp.s:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/TimeoutSerial.cpp.s
.PHONY : TimeoutSerial.cpp.s
XModem.o: XModem.cpp.o
.PHONY : XModem.o
# target to build an object file
XModem.cpp.o:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/XModem.cpp.o
.PHONY : XModem.cpp.o
XModem.i: XModem.cpp.i
.PHONY : XModem.i
# target to preprocess a source file
XModem.cpp.i:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/XModem.cpp.i
.PHONY : XModem.cpp.i
XModem.s: XModem.cpp.s
.PHONY : XModem.s
# target to generate assembly for a file
XModem.cpp.s:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/XModem.cpp.s
.PHONY : XModem.cpp.s
main.o: main.cpp.o
.PHONY : main.o
@@ -204,6 +177,33 @@ main.cpp.s:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/main.cpp.s
.PHONY : main.cpp.s
xymodem.o: xymodem.cpp.o
.PHONY : xymodem.o
# target to build an object file
xymodem.cpp.o:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/xymodem.cpp.o
.PHONY : xymodem.cpp.o
xymodem.i: xymodem.cpp.i
.PHONY : xymodem.i
# target to preprocess a source file
xymodem.cpp.i:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/xymodem.cpp.i
.PHONY : xymodem.cpp.i
xymodem.s: xymodem.cpp.s
.PHONY : xymodem.s
# target to generate assembly for a file
xymodem.cpp.s:
$(MAKE) -f CMakeFiles/timeout.dir/build.make CMakeFiles/timeout.dir/xymodem.cpp.s
.PHONY : xymodem.cpp.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@@ -216,12 +216,12 @@ help:
@echo "... TimeoutSerial.o"
@echo "... TimeoutSerial.i"
@echo "... TimeoutSerial.s"
@echo "... XModem.o"
@echo "... XModem.i"
@echo "... XModem.s"
@echo "... main.o"
@echo "... main.i"
@echo "... main.s"
@echo "... xymodem.o"
@echo "... xymodem.i"
@echo "... xymodem.s"
.PHONY : help

View File

@@ -1,17 +1,17 @@
[
{
"directory": "/home/jaro/serialport-downloader/build",
"command": "/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_DATE_TIME_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -g -std=gnu++11 -o CMakeFiles/timeout.dir/main.cpp.o -c /home/jaro/serialport-downloader/main.cpp",
"command": "/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_ATOMIC_DYN_LINK -DBOOST_CHRONO_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_LOG_DYN_LINK -DBOOST_REGEX_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_THREAD_DYN_LINK -g -std=gnu++11 -o CMakeFiles/timeout.dir/main.cpp.o -c /home/jaro/serialport-downloader/main.cpp",
"file": "/home/jaro/serialport-downloader/main.cpp"
},
{
"directory": "/home/jaro/serialport-downloader/build",
"command": "/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_DATE_TIME_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -g -std=gnu++11 -o CMakeFiles/timeout.dir/TimeoutSerial.cpp.o -c /home/jaro/serialport-downloader/TimeoutSerial.cpp",
"command": "/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_ATOMIC_DYN_LINK -DBOOST_CHRONO_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_LOG_DYN_LINK -DBOOST_REGEX_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_THREAD_DYN_LINK -g -std=gnu++11 -o CMakeFiles/timeout.dir/TimeoutSerial.cpp.o -c /home/jaro/serialport-downloader/TimeoutSerial.cpp",
"file": "/home/jaro/serialport-downloader/TimeoutSerial.cpp"
},
{
"directory": "/home/jaro/serialport-downloader/build",
"command": "/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_DATE_TIME_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -g -std=gnu++11 -o CMakeFiles/timeout.dir/XModem.cpp.o -c /home/jaro/serialport-downloader/XModem.cpp",
"file": "/home/jaro/serialport-downloader/XModem.cpp"
"command": "/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_ATOMIC_DYN_LINK -DBOOST_CHRONO_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_LOG_DYN_LINK -DBOOST_REGEX_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_THREAD_DYN_LINK -g -std=gnu++11 -o CMakeFiles/timeout.dir/xymodem.cpp.o -c /home/jaro/serialport-downloader/xymodem.cpp",
"file": "/home/jaro/serialport-downloader/xymodem.cpp"
}
]

View File

@@ -6,10 +6,28 @@
*/
#include <iostream>
#include <boost/log/sinks/debug_output_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sinks/event_log_backend.hpp>
#include <boost/thread/future.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/core.hpp>
#include <boost/log/common.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/log/detail/config.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
#include "TimeoutSerial.h"
#include "XModem.h"
using namespace std;
using namespace boost;
@@ -27,6 +45,52 @@ bool dataHandler(unsigned long blockNo, char* buffer, int size)
return true;
}
static void init_log(void)
{
/* init boost log
* 1. Add common attributes
* 2. set log filter to trace
*/
boost::log::add_common_attributes();
boost::log::core::get()->add_global_attribute("Scope",
boost::log::attributes::named_scope());
boost::log::core::get()->set_filter(
boost::log::trivial::severity >= boost::log::trivial::trace
);
/* log formatter:
* [TimeStamp] [ThreadId] [Severity Level] [Scope] Log message
*/
auto fmtTimeStamp = boost::log::expressions::
format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S.%f");
auto fmtThreadId = boost::log::expressions::
attr<boost::log::attributes::current_thread_id::value_type>("ThreadID");
auto fmtSeverity = boost::log::expressions::
attr<boost::log::trivial::severity_level>("Severity");
auto fmtScope = boost::log::expressions::format_named_scope("Scope",
boost::log::keywords::format = "%n(%f:%l)",
boost::log::keywords::iteration = boost::log::expressions::reverse,
boost::log::keywords::depth = 2);
boost::log::formatter logFmt =
boost::log::expressions::format("[%1%] (%2%) [%3%] [%4%] %5%")
% fmtTimeStamp % fmtThreadId % fmtSeverity % fmtScope
% boost::log::expressions::smessage;
/* console sink */
auto consoleSink = boost::log::add_console_log(std::clog);
consoleSink->set_formatter(logFmt);
/* fs sink */
auto fsSink = boost::log::add_file_log(
boost::log::keywords::file_name = "serial-dw_%Y-%m-%d_%H-%M-%S.%N.log",
boost::log::keywords::rotation_size = 10 * 1024 * 1024,
boost::log::keywords::min_free_space = 30 * 1024 * 1024,
boost::log::keywords::open_mode = std::ios_base::app);
fsSink->set_formatter(logFmt);
fsSink->locked_backend()->auto_flush(true);
}
int main(int argc, char* argv[])
{
string line;
@@ -34,17 +98,16 @@ int main(int argc, char* argv[])
float humidity= NAN;
float pressure = NAN;
init_log();
try {
BOOST_LOG_TRIVIAL(debug) << "TEST";
TimeoutSerial serial("/dev/ttyUSB0",115200);
serial.setTimeout(posix_time::seconds(10));
serial.setDTR(false);
serial.setRTS(false);
sleep(5);
//Text test
serial.writeString("?\r\n");
boost::this_thread::sleep(posix_time::milliseconds(5000));
line = serial.readStringUntil("\n");
trim(line);
@@ -77,10 +140,10 @@ int main(int argc, char* argv[])
cout << "vlhkost = " << humidity << endl;
cout << "tlak = " << pressure << endl;
XModem modem(&serial,dataHandler);
//XModem modem(&serial,dataHandler);
serial.writeString("capture\r\n");
sleep(1);
boost::this_thread::sleep(posix_time::milliseconds(100));
line = serial.readStringUntil("\r\n");
cout << line << endl;
line = serial.readStringUntil("\r\n");
@@ -93,11 +156,11 @@ int main(int argc, char* argv[])
// cout << line << endl;
serial.writeString("rb\r\n");
sleep(1);
boost::this_thread::sleep(posix_time::milliseconds(100));
serial.readChar(1);
serial.readChar(1);
modem.receive();
sleep(1);
//modem.receive();
boost::this_thread::sleep(posix_time::milliseconds(100));
serial.writeString("free\r\n");
serial.close();

597
xymodem.c
View File

@@ -1,597 +0,0 @@
/*
* Handles the X-Modem, Y-Modem and Y-Modem/G protocols
*
* Copyright (C) 2008 Robert Jarzmik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This file provides functions to receive X-Modem or Y-Modem(/G) protocols.
*
* References:
* *-Modem: http://www.techfest.com/hardware/modem/xymodem.htm
* XMODEM/YMODEM PROTOCOL REFERENCE, Chuck Forsberg
*/
#include <common.h>
#include <xfuncs.h>
#include <errno.h>
#include <crc.h>
#include <clock.h>
#include <console.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <fs.h>
#include <kfifo.h>
#include <linux/byteorder/generic.h>
#include <xymodem.h>
#define xy_dbg(fmt, args...)
/* Values magic to the protocol */
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define BSP 0x08
#define NAK 0x15
#define CAN 0x18
#define PROTO_XMODEM 0
#define PROTO_YMODEM 1
#define PROTO_YMODEM_G 2
#define MAX_PROTOS 3
#define CRC_NONE 0 /* No CRC checking */
#define CRC_ADD8 1 /* Add of all data bytes */
#define CRC_CRC16 2 /* CCCIT CRC16 */
#define MAX_CRCS 3
#define MAX_RETRIES 10
#define MAX_RETRIES_WITH_CRC 5
#define TIMEOUT_READ (1 * SECOND)
#define TIMEOUT_FLUSH (1 * SECOND)
#define MAX_CAN_BEFORE_ABORT 5
#define INPUT_FIFO_SIZE (4 * 1024) /* Should always be > 1029 */
enum proto_state {
PROTO_STATE_GET_FILENAME = 0,
PROTO_STATE_NEGOCIATE_CRC,
PROTO_STATE_RECEIVE_BODY,
PROTO_STATE_FINISHED_FILE,
PROTO_STATE_FINISHED_XFER,
};
/**
* struct xyz_ctxt - context of a x/y modem (g) transfer
*
* @cdev: console device to support *MODEM transfer
* @fifo: fifo to buffer input from serial line
* This is necessary for low hardware FIFOs buffers as UARTs.
* @mode: protocol (XMODEM, YMODEM or YMODEM/G)
* @crc_mode: CRC_NONE, CRC_ADD8 or CRC_CRC16
* @state: protocol state (as in "state machine")
* @buf: buffer to store the last tranfered buffer chunk
* @filename : filename transmitted by sender (YMODEM* only)
* @fd : file descriptor of the current stored file
* @file_len: length declared by sender (YMODEM* only)
* @nb_received: number of data bytes received since session open
* (this doesn't count resends)
* @total_SOH: number of SOH frames received (128 bytes chunks)
* @total_STX: number of STX frames received (1024 bytes chunks)
* @total_CAN: nubmer of CAN frames received (cancel frames)
*/
struct xyz_ctxt {
struct console_device *cdev;
struct kfifo *fifo;
int mode;
int crc_mode;
enum proto_state state;
char filename[1024];
int fd;
int file_len;
int nb_received;
int next_blk;
int total_SOH, total_STX, total_CAN, total_retries;
};
/**
* struct xy_block - one unitary block of x/y modem (g) transfer
*
* @buf: data buffer
* @len: length of data buffer (can only be 128 or 1024)
* @seq: block sequence number (as in X/Y/YG MODEM protocol)
*/
struct xy_block {
unsigned char buf[1024];
int len;
int seq;
};
/*
* For XMODEM/YMODEM, always try to use the CRC16 versions, called also
* XMODEM/CRC and YMODEM.
* Only fallback to additive CRC (8 bits) if sender doesn't cope with CRC16.
*/
static const char invite_filename_hdr[MAX_PROTOS][MAX_CRCS] = {
{ 0, NAK, 'C' }, /* XMODEM */
{ 0, NAK, 'C' }, /* YMODEM */
{ 0, 'G', 'G' }, /* YMODEM-G */
};
static const char invite_file_body[MAX_PROTOS][MAX_CRCS] = {
{ 0, NAK, 'C' }, /* XMODEM */
{ 0, NAK, 'C' }, /* YMODEM */
{ 0, 'G', 'G' }, /* YMODEM-G */
};
static const char block_ack[MAX_PROTOS][MAX_CRCS] = {
{ 0, ACK, ACK }, /* XMODEM */
{ 0, ACK, ACK }, /* YMODEM */
{ 0, 0, 0 }, /* YMODEM-G */
};
static const char block_nack[MAX_PROTOS][MAX_CRCS] = {
{ 0, NAK, NAK }, /* XMODEM */
{ 0, NAK, NAK }, /* YMODEM */
{ 0, 0, 0 }, /* YMODEM-G */
};
static int input_fifo_fill(struct console_device *cdev, struct kfifo *fifo)
{
while (cdev->tstc(cdev) && kfifo_len(fifo) < INPUT_FIFO_SIZE)
kfifo_putc(fifo, (unsigned char)(cdev->getc(cdev)));
return kfifo_len(fifo);
}
/*
* This function is optimized to :
* - maximize throughput (ie. read as much as is available in lower layer fifo)
* - minimize latencies (no delay or wait timeout if data available)
* - have a timeout
* This is why standard getc() is not used, and input_fifo_fill() exists.
*/
static int xy_gets(struct console_device *cdev, struct kfifo *fifo,
unsigned char *buf, int len, uint64_t timeout)
{
int i, rc;
uint64_t start = get_time_ns();
for (i = 0, rc = 0; rc >= 0 && i < len; ) {
if (is_timeout(start, timeout)) {
rc = -ETIMEDOUT;
continue;
}
if (input_fifo_fill(cdev, fifo))
kfifo_getc(fifo, &buf[i++]);
}
return rc < 0 ? rc : i;
}
static void xy_putc(struct console_device *cdev, unsigned char c)
{
cdev->putc(cdev, c);
}
static void xy_flush(struct console_device *cdev, struct kfifo *fifo)
{
uint64_t start;
start = get_time_ns();
while (cdev->tstc(cdev) &&
!is_timeout(start, TIMEOUT_FLUSH))
cdev->getc(cdev);
mdelay(250);
while (cdev->tstc(cdev) &&
!is_timeout(start, TIMEOUT_FLUSH))
cdev->getc(cdev);
kfifo_reset(fifo);
}
static int is_xmodem(struct xyz_ctxt *proto)
{
return proto->mode == PROTO_XMODEM;
}
static void xy_block_ack(struct xyz_ctxt *proto)
{
unsigned char c = block_ack[proto->mode][proto->crc_mode];
if (c)
xy_putc(proto->cdev, c);
}
static void xy_block_nack(struct xyz_ctxt *proto)
{
unsigned char c = block_nack[proto->mode][proto->crc_mode];
if (c)
xy_putc(proto->cdev, c);
proto->total_retries++;
}
static int check_crc(unsigned char *buf, int len, int crc, int crc_mode)
{
unsigned char crc8 = 0;
uint16_t crc16;
int i;
switch (crc_mode) {
case CRC_ADD8:
for (i = 0; i < len; i++)
crc8 += buf[i];
return crc8 == crc ? 0 : -EBADMSG;
case CRC_CRC16:
crc16 = cyg_crc16(buf, len);
xy_dbg("crc16: received = %x, calculated=%x\n", crc, crc16);
return crc16 == crc ? 0 : -EBADMSG;
case CRC_NONE:
return 0;
default:
return -EBADMSG;
}
}
/**
* xy_read_block - read a X-Modem or Y-Modem(G) block
* @proto: protocol control structure
* @blk: block read
* @timeout: maximal time to get data
*
* This is the pivotal function for block receptions. It attempts to receive one
* block, ie. one 128 bytes or one 1024 bytes block. The received data can also
* be an end of transmission, or a cancel.
*
* Returns :
* >0 : size of the received block
* 0 : last block, ie. end of transmission, ie. EOT
* -EBADMSG : malformed message (ie. sequence bi-bytes are not
* complementary), or CRC check error
* -EILSEQ : block sequence number error wrt previously received block
* -ETIMEDOUT : block not received before timeout passed
* -ECONNABORTED : transfer aborted by sender, ie. CAN
*/
static ssize_t xy_read_block(struct xyz_ctxt *proto, struct xy_block *blk,
uint64_t timeout)
{
ssize_t rc, data_len = 0;
unsigned char hdr, seqs[2], crcs[2];
int crc = 0;
bool hdr_found = 0;
uint64_t start = get_time_ns();
while (!hdr_found) {
rc = xy_gets(proto->cdev, proto->fifo, &hdr, 1, timeout);
xy_dbg("read 0x%x(%c) -> %d\n", hdr, hdr, rc);
if (rc < 0)
goto out;
if (is_timeout(start, timeout))
goto timeout;
switch (hdr) {
case SOH:
data_len = 128;
hdr_found = 1;
proto->total_SOH++;
break;
case STX:
data_len = 1024;
hdr_found = 1;
proto->total_STX++;
break;
case CAN:
rc = -ECONNABORTED;
if (proto->total_CAN++ > MAX_CAN_BEFORE_ABORT)
goto out;
break;
case EOT:
rc = 0;
blk->len = 0;
goto out;
default:
break;
}
}
blk->seq = 0;
rc = xy_gets(proto->cdev, proto->fifo, seqs, 2, timeout);
if (rc < 0)
goto out;
blk->seq = seqs[0];
if (255 - seqs[0] != seqs[1])
return -EBADMSG;
rc = xy_gets(proto->cdev, proto->fifo, blk->buf, data_len, timeout);
if (rc < 0)
goto out;
blk->len = rc;
switch (proto->crc_mode) {
case CRC_ADD8:
rc = xy_gets(proto->cdev, proto->fifo, crcs, 1, timeout);
crc = crcs[0];
break;
case CRC_CRC16:
rc = xy_gets(proto->cdev, proto->fifo, crcs, 2, timeout);
crc = (crcs[0] << 8) + crcs[1];
break;
case CRC_NONE:
rc = 0;
break;
}
if (rc < 0)
goto out;
rc = check_crc(blk->buf, data_len, crc, proto->crc_mode);
if (rc < 0)
goto out;
return data_len;
timeout:
return -ETIMEDOUT;
out:
return rc;
}
static int check_blk_seq(struct xyz_ctxt *proto, struct xy_block *blk,
int read_rc)
{
if (blk->seq == ((proto->next_blk - 1) % 256))
return -EALREADY;
if (blk->seq != proto->next_blk)
return -EILSEQ;
return read_rc;
}
static int parse_first_block(struct xyz_ctxt *proto, struct xy_block *blk)
{
int filename_len;
char *str_num;
filename_len = strlen(blk->buf);
if (filename_len > blk->len)
return -EINVAL;
strlcpy(proto->filename, blk->buf, sizeof(proto->filename));
str_num = blk->buf + filename_len + 1;
strsep(&str_num, " ");
proto->file_len = simple_strtoul(blk->buf + filename_len + 1, NULL, 10);
return 1;
}
static int xy_get_file_header(struct xyz_ctxt *proto)
{
struct xy_block blk;
int tries, rc = 0;
memset(&blk, 0, sizeof(blk));
proto->state = PROTO_STATE_GET_FILENAME;
proto->crc_mode = CRC_CRC16;
for (tries = 0; tries < MAX_RETRIES; tries++) {
xy_putc(proto->cdev,
invite_filename_hdr[proto->mode][proto->crc_mode]);
rc = xy_read_block(proto, &blk, 3 * SECOND);
xy_dbg("read block returned %d\n", rc);
switch (rc) {
case -ECONNABORTED:
goto fail;
case -ETIMEDOUT:
case -EBADMSG:
if (proto->mode != PROTO_YMODEM_G)
xy_flush(proto->cdev, proto->fifo);
break;
case -EALREADY:
default:
proto->next_blk = 1;
xy_block_ack(proto);
proto->state = PROTO_STATE_NEGOCIATE_CRC;
rc = parse_first_block(proto, &blk);
return rc;
}
if (rc < 0 && tries++ >= MAX_RETRIES_WITH_CRC)
proto->crc_mode = CRC_ADD8;
}
rc = -ETIMEDOUT;
fail:
proto->total_retries += tries;
return rc;
}
static int xy_await_header(struct xyz_ctxt *proto)
{
int rc;
rc = xy_get_file_header(proto);
if (rc < 0)
return rc;
proto->state = PROTO_STATE_NEGOCIATE_CRC;
xy_dbg("header received, filename=%s, file length=%d\n",
proto->filename, proto->file_len);
if (proto->filename[0])
proto->fd = open(proto->filename, O_WRONLY | O_CREAT);
else
proto->state = PROTO_STATE_FINISHED_XFER;
proto->nb_received = 0;
return rc;
}
static void xy_finish_file(struct xyz_ctxt *proto)
{
close(proto->fd);
proto->fd = 0;
proto->state = PROTO_STATE_FINISHED_FILE;
}
static struct xyz_ctxt *xymodem_open(struct console_device *cdev,
int proto_mode, int xmodem_fd)
{
struct xyz_ctxt *proto;
proto = xzalloc(sizeof(struct xyz_ctxt));
proto->fifo = kfifo_alloc(INPUT_FIFO_SIZE);
proto->mode = proto_mode;
proto->cdev = cdev;
proto->crc_mode = CRC_CRC16;
if (is_xmodem(proto)) {
proto->fd = xmodem_fd;
proto->state = PROTO_STATE_NEGOCIATE_CRC;
} else {
proto->state = PROTO_STATE_GET_FILENAME;
}
xy_flush(proto->cdev, proto->fifo);
return proto;
}
static int xymodem_handle(struct xyz_ctxt *proto)
{
int rc = 0, xfer_max, len = 0, again = 1, remain;
int crc_tries = 0, same_blk_retries = 0;
unsigned char invite;
struct xy_block blk;
while (again) {
switch (proto->state) {
case PROTO_STATE_GET_FILENAME:
crc_tries = 0;
rc = xy_await_header(proto);
if (rc < 0)
goto fail;
continue;
case PROTO_STATE_FINISHED_FILE:
if (is_xmodem(proto))
proto->state = PROTO_STATE_FINISHED_XFER;
else
proto->state = PROTO_STATE_GET_FILENAME;
xy_putc(proto->cdev, ACK);
continue;
case PROTO_STATE_FINISHED_XFER:
again = 0;
rc = 0;
goto out;
case PROTO_STATE_NEGOCIATE_CRC:
invite = invite_file_body[proto->mode][proto->crc_mode];
proto->next_blk = 1;
if (crc_tries++ > MAX_RETRIES_WITH_CRC)
proto->crc_mode = CRC_ADD8;
xy_putc(proto->cdev, invite);
/* Fall through */
case PROTO_STATE_RECEIVE_BODY:
rc = xy_read_block(proto, &blk, 3 * SECOND);
if (rc > 0) {
rc = check_blk_seq(proto, &blk, rc);
proto->state = PROTO_STATE_RECEIVE_BODY;
}
break;
}
if (proto->state != PROTO_STATE_RECEIVE_BODY)
continue;
switch (rc) {
case -ECONNABORTED:
goto fail;
case -ETIMEDOUT:
if (proto->mode == PROTO_YMODEM_G)
goto fail;
xy_flush(proto->cdev, proto->fifo);
xy_block_nack(proto);
break;
case -EBADMSG:
case -EILSEQ:
if (proto->mode == PROTO_YMODEM_G)
goto fail;
xy_flush(proto->cdev, proto->fifo);
xy_block_nack(proto);
break;
case -EALREADY:
xy_block_ack(proto);
break;
case 0:
xy_finish_file(proto);
break;
default:
remain = proto->file_len - proto->nb_received;
if (is_xmodem(proto))
xfer_max = blk.len;
else
xfer_max = min(blk.len, remain);
rc = write(proto->fd, blk.buf, xfer_max);
proto->next_blk = ((blk.seq + 1) % 256);
proto->nb_received += rc;
len += rc;
xy_block_ack(proto);
break;
}
if (rc < 0)
same_blk_retries++;
else
same_blk_retries = 0;
if (same_blk_retries > MAX_RETRIES)
goto fail;
}
out:
return rc;
fail:
if (proto->fd)
close(proto->fd);
return rc;
}
static void xymodem_close(struct xyz_ctxt *proto)
{
xy_flush(proto->cdev, proto->fifo);
printf("\nxyModem - %d(SOH)/%d(STX)/%d(CAN) packets,"
" %d retries\n",
proto->total_SOH, proto->total_STX,
proto->total_CAN, proto->total_retries);
kfifo_free(proto->fifo);
}
int do_load_serial_xmodem(struct console_device *cdev, int fd)
{
struct xyz_ctxt *proto;
int rc;
proto = xymodem_open(cdev, PROTO_XMODEM, fd);
do {
rc = xymodem_handle(proto);
} while (rc > 0);
xymodem_close(proto);
return rc < 0 ? rc : 0;
}
EXPORT_SYMBOL(do_load_serial_xmodem);
int do_load_serial_ymodem(struct console_device *cdev)
{
struct xyz_ctxt *proto;
int rc;
proto = xymodem_open(cdev, PROTO_YMODEM, 0);
do {
rc = xymodem_handle(proto);
} while (rc > 0);
xymodem_close(proto);
return rc < 0 ? rc : 0;
}
EXPORT_SYMBOL(do_load_serial_ymodem);
int do_load_serial_ymodemg(struct console_device *cdev)
{
struct xyz_ctxt *proto;
int rc;
proto = xymodem_open(cdev, PROTO_YMODEM_G, 0);
do {
rc = xymodem_handle(proto);
} while (rc > 0);
xymodem_close(proto);
return rc < 0 ? rc : 0;
}
EXPORT_SYMBOL(do_load_serial_ymodemg);

585
xymodem.cpp Normal file
View File

@@ -0,0 +1,585 @@
/*
* xymodem file transferts
*
* (C) Copyright 2011 Angelo Dureghello <angelo70@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <cstring>
#include <sstream>
#include <stdexcept>
#include <boost/utility.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/log/trivial.hpp>
#include <boost/thread/future.hpp>
#include "xymodem.h"
#include "Thread.h"
using namespace std;
using namespace boost;
static const unsigned char ZPAD = 0x2a;
static const unsigned char ZDLE = 0x18;
static const unsigned char ZBIN = 0x41;
static const unsigned char ZHEX = 0x42;
static const unsigned char XON = 0x11;
static const unsigned char ZMABORT[] = {ZDLE,ZDLE,ZDLE,ZDLE,ZDLE};
static const unsigned char ESC_HEXHDR[] = {ZPAD,ZPAD,ZDLE,ZHEX};
static const unsigned char ZRQINIT[] = "0000000000195E";
static const unsigned char ZTERM[]= {0x0d,0x0a,XON};
class __tools {
public:
__tools () {}
public:
unsigned short crc16_ccitt( char *buf, int len )
{
unsigned short crc = 0;
while( len-- ) {
int i;
crc ^= *(char *)buf++ << 8;
for( i = 0; i < 8; ++i ) {
if( crc & 0x8000 )
crc = (crc << 1) ^ 0x1021;
else
crc = crc << 1;
}
}
return crc;
}
unsigned char calc_xmodem_checksum (unsigned char *buf, int sz)
{
int i;
int cks = 0;
for (i = 0; i < sz; ++i) {
cks += (int)buf[i];
}
return cks%256;
}
};
/* XMODEM */
static const unsigned char SOH = 0x01;
static const unsigned char STX = 0x02;
static const unsigned char EOT = 0x04;
static const unsigned char ACK = 0x06;
static const unsigned char NAK = 0x15;
static const unsigned char ETB = 0x17;
static const unsigned char CAN = 0x18;
static const unsigned char SYN = 0x43;
static const unsigned char CPMEOF = 0x1a;
Operator::Operator (TimeoutSerial &c) : com(&c)
{
obuff.resize (512 , 0);
obuff.resize (2048, 0);
}
void Operator::SessionStartup (const string &file)
{
fname = file;
f.open(file.c_str(), fstream::binary | fstream::in);
/* adding a cr, C was probably on the screen */
if (!f.is_open())
{
BOOST_LOG_TRIVIAL(debug) << "Cannot open file " << file << " please check the path";
}
BOOST_LOG_TRIVIAL(debug) << "File: " << file;
/* getting file size */
f.seekg (0, ios::end);
fsize = f.tellg();
f.seekg (0, ios::beg);
stringstream ss;
string size;
ss << fsize;
ss >> size;
BOOST_LOG_TRIVIAL(debug) << "Size: " << size << " bytes";
}
void Operator::StartTransfert ()
{
BOOST_LOG_TRIVIAL(debug) << "Starting file transfer ...";
Entry();
}
void Operator::SendBlock(char *block, int size)
{
int hsz, csz;
/* prepare block header */
hsz = SetupBlockHdr();
/* prepare block binary data */
memcpy (&obuff[hsz], block, size);
csz = SetupCheckSum (&obuff[hsz], &obuff[hsz+size]);
q->write (&obuff[0], hsz+size+csz);
}
/*
* A thread process the entire transfert and allows UI to be free of operating
*/
enum states {
SESSION_START,
SESSION_BLOCKS,
SESSION_CLOSE,
SESSION_CLOSE_WAIT_ACK,
SESSION_END,
};
bool Operator::GetChar()
{
try {
q->read(&ibuff[0], 1);
return true;
} catch(boost::system::system_error& e)
{
return false;
}
}
bool Operator::IsChar ()
{ return (ibuff[0]!=0); }
bool Operator::IsAck ()
{
return (ibuff[0] == GetAck());
}
bool Operator::IsSync ()
{
return (ibuff[0] == GetSync());
}
bool Operator::IsNack ()
{
return (ibuff[0] == GetNack());
}
bool Operator::IsCan ()
{
return (ibuff[0] == GetCan());
}
void Operator::SendEot ()
{
char c[2];
c[0] = GetEot();
q->write (c, 1);
}
void Operator::SetupInfoBlock (string &block)
{
stringstream ss;
unsigned int i = fname.rfind('/');
if (i==string::npos)
i = fname.rfind('\\');
if (i!=string::npos)
{
fname = fname.substr(i+1);
}
i=fname.size();
ss << fsize;
pkt = 0;
/* prepare first block */
memcpy(&block[0],&fname[0], i);
block[i++]=0;
memcpy(&block[i],ss.str().c_str(),ss.str().size());
/*
* space after length should be needed only if mod date is sent
* but some receiver (u-boot) need it.
*/
block[i+ss.str().size()] = ' ';
}
void Operator::UpdateProgress (int size)
{
stringstream ss;
if (!size) {
psize = GetBlockSize() * count++;
} else
psize += size;
ss << psize << "/" << fsize;
// EvtDel ();
// EvtMsg (ss.str());
}
int Operator::Entry()
{
string block(1024,0);
struct y_modem_data ymd;
int state = SESSION_START;
int bsize = GetBlockSize();
int blnum = fsize / bsize;
int reminder=0;
count=1;
/* progress size */
psize=0;
stringstream ss;
string ascii_bsize;
ss << bsize;
ss >> ascii_bsize;
/* clone */
q = com;
pkt = 1;
if (GetProto()=='Y')
{
/*
* always sending file size allows a faster close of
* the session from the receiver.
*
* = SPEC =
* The pathname (conventionally, the file name) is sent as a null
* terminated ASCII string.
* No spaces are included in the pathname. Normally only the file name
* stem (no directory prefix) is transmitted unless the sender has
* selected YAM's f option to send the full pathname. The source drive
* (A:, B:, etc.) is not sent.
*
*/
SetupInfoBlock(block);
blnum++;
/* signal to skip update progress for first info block */
ymd.first_pkt = true;
}
else
/* read first block from file*/
f.read(&block[0], bsize);
for (;;) {
/* always, wait for tranfert occour and proper replies */
boost::this_thread::sleep(posix_time::milliseconds(10));
if (GetChar()==false) continue;
switch (state) {
case SESSION_START:
if (IsSync()) {
if (ymd.first_ack) {
BOOST_LOG_TRIVIAL(debug) << "Entry() : YMODEM FIRST C after ACK";
/*
* YMODEM, as per spec we expect receiver
* sends this furhter 'C'
*/
state++;
/*
* clear rx data for walkthrough and
* send 1st block
*/
ibuff[0]=0;
}
else {
if (GetProto()=='X')
{
/*
* C here mean receiver wants
* 16bit CRC mode and
* assumes we know it if we send
* a block
*/
SetMode(XMODEMCRC);
/* send and promote */
state++;
}
/* received SYN */
BOOST_LOG_TRIVIAL(debug) << "Sync received, transfert start";
/*
* resend same initial packet
* note: non sense for Y-MODEM use 1024
*/
SetSize(128);
SendBlock (&block[0], 128);
if (GetProto()=='Y') SetSize(1024);
boost::this_thread::sleep(posix_time::milliseconds(10));;
continue;
}
}
else
if (IsAck()) {
if (GetProto()=='Y') {
BOOST_LOG_TRIVIAL(debug) << "Entry() : YMODEM FIRST ACK";
ymd.first_ack=true;
/* back up to get C after ack */
continue;
}
else
state++;
}
else
if (IsNack()) {
/*
* note ! programs as rx or lrz need filename as
* additional parameter, otherwise they sends
* C.. C and a CAN just after.
*/
if (GetProto()=='X') {
BOOST_LOG_TRIVIAL(debug) << "Nack received, transfert start\r\n";
/* resend same initial packet*/
SendBlock (&block[0], bsize);
boost::this_thread::sleep(posix_time::milliseconds(10));
/* promoted */
state++;
}
else
{
BOOST_LOG_TRIVIAL(debug) << "Entry() : NACK FIRST";
}
}
// NO BREAK,
// FALL THROUGH IS INTENTIONAL
case SESSION_BLOCKS:
if (IsChar() && !IsAck()) {
if (IsSync()) {
if (GetProto()=='Y')
{
/*
* first synch after first
* block sent, do nothing here
*/
}
}
else
if (IsNack()) {
//EvtMsg("Nack/Sync\r\n");
/* resend */
if (GetProto()=='Y' ||
(GetProto()=='X' && pkt!=1))
{
BOOST_LOG_TRIVIAL(debug) << "entry() : NACK, RESEND";
SendBlock (&block[0], bsize);
boost::this_thread::sleep(posix_time::milliseconds(10));
}
}
else
if (IsCan())
{
BOOST_LOG_TRIVIAL(debug) << "Transfert canceled from receiver";
boost::this_thread::sleep(posix_time::milliseconds(100));
/* destroy object and the thread */
return (void*)1;
}
else
{
/* to do, if some other chances */
}
continue;
}
// ACK received, so progress
if (blnum)
{
if (GetProto()=='Y')
{
if (ymd.first_pkt) ymd.first_pkt=false;
else
UpdateProgress();
}
else
// XMODEM, always
UpdateProgress();
/* if multiple of 256/1024 close */
if (blnum==1 && psize==fsize) {
blnum--;
goto close_session;
}
}
else
{
close_session:
/* completed */
UpdateProgress (reminder);
BOOST_LOG_TRIVIAL(debug) << "Closing session ...";
SendEot();
if (GetProto()=='X') state=SESSION_END; else state++;
continue;
}
blnum--;
pkt++;
/* end of blocks ? */
if (blnum>0) {
f.read(&block[0], bsize);
SendBlock (&block[0], bsize);
}
else {
reminder=fsize%bsize;
if (reminder)
{
f.read(&block[0], reminder);
memset (&block[reminder],CPMEOF,bsize-reminder);
SendBlock (&block[0], bsize);
}
}
break;
case SESSION_CLOSE:
/* YMODEM ONLY */
if (IsAck()) state++;
else
if (IsNack())
{
/*
* in YMODEM termination is EOT-> NACK<-,
* EOT-> ACK<-
*/
SendEot();
}
break;
case SESSION_CLOSE_WAIT_ACK:
/* YMODEM ONLY */
if (IsSync())
{
/* Synch, terminating.. */
memset (&block[0], 0, 128);
SetSize(128);
pkt=0;
SendBlock (&block[0], 128);
state++;
}
break;
case SESSION_END:
if (!IsAck()) continue;
BOOST_LOG_TRIVIAL(debug) << "Transfert complete";
boost::this_thread::sleep(posix_time::milliseconds(100));
/* destroy object and the thread */
//delete q;
//EvtEnd ();
return (void*)1;
break;
}
}
return 0;
}
XModem::XModem (TimeoutSerial &c) : Operator (c), t(*new __tools)
{
xmodem_mode = 0;
psize = LEN_B_XMODEM;
}
int XModem::SetupBlockHdr()
{
obuff[0]=(xmodem_mode&XMODEM1K)?STX:SOH;
obuff[1]=pkt;
obuff[2]=0xff-pkt;
return 3;
}
int XModem::SetupCheckSum (char *data, char *dest)
{
if (xmodem_mode&XMODEMCRC)
{
unsigned short crc = t.crc16_ccitt(data, psize);
*(unsigned short*)dest = (unsigned short)
((crc<<8)&0xFF00) | ((crc>>8)&0xff);
return 2;
}
else
{
unsigned char crc =
t.calc_xmodem_checksum((unsigned char*)data, psize);
*dest = crc;
return 1;
}
}
inline char XModem::GetSync () { return SYN; }
inline char XModem::GetAck () { return ACK; }
inline char XModem::GetNack () { return NAK; }
inline char XModem::GetCan () { return CAN; }
inline char XModem::GetEot () { return EOT; }
inline char XModem::GetProto() { return 'X'; }
inline void XModem::SetSize (int size) { psize = size; }
YModem::YModem (TimeoutSerial &c) : Operator (c), t(*new __tools)
{
psize = LEN_B_YMODEM;
}
/* YModem allows 128 (std) blocks or 1024
*/
int YModem::SetupBlockHdr()
{
obuff[0]=(psize==LEN_B_YMODEM)?STX:SOH;
obuff[1]=pkt;
obuff[2]=0xff-pkt;
return 3;
}
int YModem::SetupCheckSum (char *data, char *dest)
{
unsigned short crc = t.crc16_ccitt(data, psize);
*(unsigned short*)dest = (unsigned short)((crc<<8)&0xFF00) | ((crc>>8)&0xff);
return 2;
}
inline char YModem::GetSync () { return SYN; }
inline char YModem::GetAck () { return ACK; }
inline char YModem::GetNack () { return NAK; }
inline char YModem::GetCan () { return CAN; }
inline char YModem::GetEot () { return EOT; }
inline char YModem::GetProto() { return 'Y'; }
inline void YModem::SetSize (int size) { psize = size; }

141
xymodem.h Normal file
View File

@@ -0,0 +1,141 @@
#ifndef modem_HH
#define modem_HH
#include <string>
#include <fstream>
#include "Thread.h"
#include "TimeoutSerial.h"
using std::fstream;
using std::string;
class __tools;
struct y_modem_data
{
y_modem_data() : first_ack(false) {}
bool first_ack;
bool first_pkt;
};
class Operator: public Thread< Operator >
{
public:
Operator (TimeoutSerial &c);
private:
int Entry(); //Strting point
void SetupInfoBlock (string &block);
void UpdateProgress (int = 0);
bool GetChar();
bool IsChar ();
bool IsAck ();
bool IsSync ();
bool IsNack ();
bool IsCan ();
protected:
void SendBlock(char *p, int size);
void SendEot ();
virtual int GetBlockSize () = 0;
virtual int SetupBlockHdr () = 0;
virtual int SetupCheckSum (char *data, char *dest) = 0;
virtual char GetSync () = 0;
virtual char GetAck () = 0;
virtual char GetCan () = 0;
virtual char GetNack () = 0;
virtual char GetEot () = 0;
virtual char GetProto() = 0;
virtual void SetSize (int) {}
virtual void SetMode (int) {}
virtual int GetMode () { return 0; }
public:
void SessionStartup (const string &);
void StartTransfert ();
protected:
TimeoutSerial *com, *q;
fstream f;
int pkt;
int fsize,psize;
int count;
string fname;
string ibuff;
string obuff;
};
static const int LEN_B_XMODEM = 128;
// bitmask
enum {
XMODEM1K=0x01,
XMODEMCRC=0x02,
};
class XModem : public Operator {
public:
XModem (TimeoutSerial &c);
private:
int GetBlockSize () { return psize; }
int SetupBlockHdr ();
int SetupCheckSum (char *data, char *dest);
char GetSync ();
char GetAck ();
char GetNack ();
char GetCan ();
char GetProto();
char GetEot ();
int GetMode () {return xmodem_mode;}
void SetSize (int size);
void SetMode (int mode) {
xmodem_mode|=mode;
if (mode&XMODEM1K) psize=1024;
}
public:
int xmodem_mode;
private:
__tools &t;
int psize;
};
/* we don't use the standard 128 block but 1024 only
*/
static const int LEN_B_YMODEM = 1024;
class YModem : public Operator {
public:
YModem (TimeoutSerial &c);
private:
int GetBlockSize () { return psize; }
int SetupBlockHdr ();
int SetupCheckSum (char *data, char *dest);
char GetSync ();
char GetAck ();
char GetNack ();
char GetCan ();
char GetProto();
char GetEot ();
void SetSize (int size);
private:
__tools &t;
int psize;
};
#endif // modem_HH

View File

@@ -1,671 +0,0 @@
/**
******************************************************************************
* @file IAP/src/ymodem.c
* @author MCD Application Team
* @version V3.3.0
* @date 10/15/2010
* @brief This file provides all the software functions related to the ymodem
* protocol.
******************************************************************************
* @copy
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>&copy; COPYRIGHT 2010 STMicroelectronics</center></h2>
*/
#include <string>
#include <algorithm>
#include <iostream>
#include <boost/bind.hpp>
#include <sys/ioctl.h>
#include "ymodem.h"
uint8_t file_name[FILE_NAME_LENGTH];
uint32_t FlashDestination = ApplicationAddress; /* Flash user program offset */
uint16_t PageSize = PAGE_SIZE;
uint32_t EraseCounter = 0x0;
uint32_t NbrOfPage = 0;
FLASH_Status FLASHStatus = FLASH_COMPLETE;
uint32_t RamSource;
extern uint8_t tab_1024[1024];
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief Receive byte from sender
* @param c: Character
* @param timeout: Timeout
* @retval 0: Byte received
* -1: Timeout
*/
static int32_t Receive_Byte (uint8_t *c, uint32_t timeout)
{
while (timeout-- > 0)
{
if (SerialKeyPressed(c) == 1)
{
return 0;
}
}
return -1;
}
/**
* @brief Send a byte
* @param c: Character
* @retval 0: Byte sent
*/
static uint32_t Send_Byte (uint8_t c)
{
SerialPutChar(c);
return 0;
}
/**
* @brief Receive a packet from sender
* @param data
* @param length
* @param timeout
* 0: end of transmission
* -1: abort by sender
* >0: packet length
* @retval 0: normally return
* -1: timeout or packet error
* 1: abort by user
*/
static int32_t Receive_Packet (uint8_t *data, int32_t *length, uint32_t timeout)
{
uint16_t i, packet_size;
uint8_t c;
*length = 0;
if (Receive_Byte(&c, timeout) != 0)
{
return -1;
}
switch (c)
{
case SOH:
packet_size = PACKET_SIZE;
break;
case STX:
packet_size = PACKET_1K_SIZE;
break;
case EOT:
return 0;
case CA:
if ((Receive_Byte(&c, timeout) == 0) && (c == CA))
{
*length = -1;
return 0;
}
else
{
return -1;
}
case ABORT1:
case ABORT2:
return 1;
default:
return -1;
}
*data = c;
for (i = 1; i < (packet_size + PACKET_OVERHEAD); i ++)
{
if (Receive_Byte(data + i, timeout) != 0)
{
return -1;
}
}
if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff))
{
return -1;
}
*length = packet_size;
return 0;
}
/**
* @brief Receive a file using the ymodem protocol
* @param buf: Address of the first byte
* @retval The size of the file
*/
int32_t Ymodem_Receive (uint8_t *buf)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
int32_t i, j, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
/* Initialize FlashDestination variable */
FlashDestination = ApplicationAddress;
for (session_done = 0, errors = 0, session_begin = 0; ;)
{
for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
{
switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
{
case 0:
errors = 0;
switch (packet_length)
{
/* Abort by sender */
case - 1:
Send_Byte(ACK);
return 0;
/* End of transmission */
case 0:
Send_Byte(ACK);
file_done = 1;
break;
/* Normal packet */
default:
if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
{
Send_Byte(NAK);
}
else
{
if (packets_received == 0)
{
/* Filename packet */
if (packet_data[PACKET_HEADER] != 0)
{
/* Filename packet has valid data */
for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
{
file_name[i++] = *file_ptr++;
}
file_name[i++] = '\0';
for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
{
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
Str2Int(file_size, &size);
/* Test the size of the image to be sent */
/* Image size is greater than Flash size */
if (size > (FLASH_SIZE - 1))
{
/* End session */
Send_Byte(CA);
Send_Byte(CA);
return -1;
}
/* Erase the needed pages where the user application will be loaded */
/* Define the number of page to be erased */
NbrOfPage = FLASH_PagesMask(size);
/* Erase the FLASH pages */
for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
{
FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
}
Send_Byte(ACK);
Send_Byte(CRC16);
}
/* Filename packet is empty, end session */
else
{
Send_Byte(ACK);
file_done = 1;
session_done = 1;
break;
}
}
/* Data packet */
else
{
memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
RamSource = (uint32_t)buf;
for (j = 0;(j < packet_length) && (FlashDestination < ApplicationAddress + size);j += 4)
{
/* Program the data received into STM32F10x Flash */
FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);
if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
{
/* End session */
Send_Byte(CA);
Send_Byte(CA);
return -2;
}
FlashDestination += 4;
RamSource += 4;
}
Send_Byte(ACK);
}
packets_received ++;
session_begin = 1;
}
}
break;
case 1:
Send_Byte(CA);
Send_Byte(CA);
return -3;
default:
if (session_begin > 0)
{
errors ++;
}
if (errors > MAX_ERRORS)
{
Send_Byte(CA);
Send_Byte(CA);
return 0;
}
Send_Byte(CRC16);
break;
}
if (file_done != 0)
{
break;
}
}
if (session_done != 0)
{
break;
}
}
return (int32_t)size;
}
/**
* @brief check response using the ymodem protocol
* @param buf: Address of the first byte
* @retval The size of the file
*/
int32_t Ymodem_CheckResponse(uint8_t c)
{
return 0;
}
/**
* @brief Prepare the first block
* @param timeout
* 0: end of transmission
*/
void Ymodem_PrepareIntialPacket(uint8_t *data, const uint8_t* fileName, uint32_t *length)
{
uint16_t i, j;
uint8_t file_ptr[10];
/* Make first three packet */
data[0] = SOH;
data[1] = 0x00;
data[2] = 0xff;
/* Filename packet has valid data */
for (i = 0; (fileName[i] != '\0') && (i < FILE_NAME_LENGTH);i++)
{
data[i + PACKET_HEADER] = fileName[i];
}
data[i + PACKET_HEADER] = 0x00;
Int2Str (file_ptr, *length);
for (j =0, i = i + PACKET_HEADER + 1; file_ptr[j] != '\0' ; )
{
data[i++] = file_ptr[j++];
}
for (j = i; j < PACKET_SIZE + PACKET_HEADER; j++)
{
data[j] = 0;
}
}
/**
* @brief Prepare the data packet
* @param timeout
* 0: end of transmission
*/
void Ymodem_PreparePacket(uint8_t *SourceBuf, uint8_t *data, uint8_t pktNo, uint32_t sizeBlk)
{
uint16_t i, size, packetSize;
uint8_t* file_ptr;
/* Make first three packet */
packetSize = sizeBlk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE;
size = sizeBlk < packetSize ? sizeBlk :packetSize;
if (packetSize == PACKET_1K_SIZE)
{
data[0] = STX;
}
else
{
data[0] = SOH;
}
data[1] = pktNo;
data[2] = (~pktNo);
file_ptr = SourceBuf;
/* Filename packet has valid data */
for (i = PACKET_HEADER; i < size + PACKET_HEADER;i++)
{
data[i] = *file_ptr++;
}
if ( size <= packetSize)
{
for (i = size + PACKET_HEADER; i < packetSize + PACKET_HEADER; i++)
{
data[i] = 0x1A; /* EOF (0x1A) or 0x00 */
}
}
}
/**
* @brief Update CRC16 for input byte
* @param CRC input value
* @param input byte
* @retval None
*/
uint16_t UpdateCRC16(uint16_t crcIn, uint8_t byte)
{
uint32_t crc = crcIn;
uint32_t in = byte|0x100;
do
{
crc <<= 1;
in <<= 1;
if(in&0x100)
++crc;
if(crc&0x10000)
crc ^= 0x1021;
}
while(!(in&0x10000));
return crc&0xffffu;
}
/**
* @brief Cal CRC16 for YModem Packet
* @param data
* @param length
* @retval None
*/
uint16_t Cal_CRC16(const uint8_t* data, uint32_t size)
{
uint32_t crc = 0;
const uint8_t* dataEnd = data+size;
while(data<dataEnd)
crc = UpdateCRC16(crc,*data++);
crc = UpdateCRC16(crc,0);
crc = UpdateCRC16(crc,0);
return crc&0xffffu;
}
/**
* @brief Cal Check sum for YModem Packet
* @param data
* @param length
* @retval None
*/
uint8_t CalChecksum(const uint8_t* data, uint32_t size)
{
uint32_t sum = 0;
const uint8_t* dataEnd = data+size;
while(data < dataEnd )
sum += *data++;
return sum&0xffu;
}
/**
* @brief Transmit a data packet using the ymodem protocol
* @param data
* @param length
* @retval None
*/
void Ymodem_SendPacket(uint8_t *data, uint16_t length)
{
uint16_t i;
i = 0;
while (i < length)
{
Send_Byte(data[i]);
i++;
}
}
/**
* @brief Transmit a file using the ymodem protocol
* @param buf: Address of the first byte
* @retval The size of the file
*/
uint8_t Ymodem_Transmit (uint8_t *buf, const uint8_t* sendFileName, uint32_t sizeFile)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
uint8_t FileName[FILE_NAME_LENGTH];
uint8_t *buf_ptr, tempCheckSum ;
uint16_t tempCRC, blkNumber;
uint8_t receivedC[2], CRC16_F = 0, i;
uint32_t errors, ackReceived, size = 0, pktSize;
errors = 0;
ackReceived = 0;
for (i = 0; i < (FILE_NAME_LENGTH - 1); i++)
{
FileName[i] = sendFileName[i];
}
CRC16_F = 1;
/* Prepare first block */
Ymodem_PrepareIntialPacket(&packet_data[0], FileName, &sizeFile);
do
{
/* Send Packet */
Ymodem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
if (CRC16_F)
{
tempCRC = Cal_CRC16(&packet_data[3], PACKET_SIZE);
Send_Byte(tempCRC >> 8);
Send_Byte(tempCRC & 0xFF);
}
else
{
tempCheckSum = CalChecksum (&packet_data[3], PACKET_SIZE);
Send_Byte(tempCheckSum);
}
/* Wait for Ack and 'C' */
if (Receive_Byte(&receivedC[0], 10000) == 0)
{
if (receivedC[0] == ACK)
{
/* Packet transfered correctly */
ackReceived = 1;
}
}
else
{
errors++;
}
}while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
buf_ptr = buf;
size = sizeFile;
blkNumber = 0x01;
/* Here 1024 bytes package is used to send the packets */
/* Resend packet if NAK for a count of 10 else end of commuincation */
while (size)
{
/* Prepare next packet */
Ymodem_PreparePacket(buf_ptr, &packet_data[0], blkNumber, size);
ackReceived = 0;
receivedC[0]= 0;
errors = 0;
do
{
/* Send next packet */
if (size >= PACKET_1K_SIZE)
{
pktSize = PACKET_1K_SIZE;
}
else
{
pktSize = PACKET_SIZE;
}
Ymodem_SendPacket(packet_data, pktSize + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
/* Send CRC or Check Sum based on CRC16_F */
if (CRC16_F)
{
tempCRC = Cal_CRC16(&packet_data[3], pktSize);
Send_Byte(tempCRC >> 8);
Send_Byte(tempCRC & 0xFF);
}
else
{
tempCheckSum = CalChecksum (&packet_data[3], pktSize);
Send_Byte(tempCheckSum);
}
/* Wait for Ack */
if ((Receive_Byte(&receivedC[0], 100000) == 0) && (receivedC[0] == ACK))
{
ackReceived = 1;
if (size > pktSize)
{
buf_ptr += pktSize;
size -= pktSize;
if (blkNumber == (FLASH_IMAGE_SIZE/1024))
{
return 0xFF; /* error */
}
else
{
blkNumber++;
}
}
else
{
buf_ptr += pktSize;
size = 0;
}
}
else
{
errors++;
}
}while(!ackReceived && (errors < 0x0A));
/* Resend packet if NAK for a count of 10 else end of commuincation */
if (errors >= 0x0A)
{
return errors;
}
}
ackReceived = 0;
receivedC[0] = 0x00;
errors = 0;
do
{
Send_Byte(EOT);
/* Send (EOT); */
/* Wait for Ack */
if ((Receive_Byte(&receivedC[0], 10000) == 0) && receivedC[0] == ACK)
{
ackReceived = 1;
}
else
{
errors++;
}
}while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
/* Last packet preparation */
ackReceived = 0;
receivedC[0] = 0x00;
errors = 0;
packet_data[0] = SOH;
packet_data[1] = 0;
packet_data [2] = 0xFF;
for (i = PACKET_HEADER; i < (PACKET_SIZE + PACKET_HEADER); i++)
{
packet_data [i] = 0x00;
}
do
{
/* Send Packet */
Ymodem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
tempCRC = Cal_CRC16(&packet_data[3], PACKET_SIZE);
Send_Byte(tempCRC >> 8);
Send_Byte(tempCRC & 0xFF);
/* Wait for Ack and 'C' */
if (Receive_Byte(&receivedC[0], 10000) == 0)
{
if (receivedC[0] == ACK)
{
/* Packet transfered correctly */
ackReceived = 1;
}
}
else
{
errors++;
}
}while (!ackReceived && (errors < 0x0A));
/* Resend packet if NAK for a count of 10 else end of commuincation */
if (errors >= 0x0A)
{
return errors;
}
do
{
Send_Byte(EOT);
/* Send (EOT); */
/* Wait for Ack */
if ((Receive_Byte(&receivedC[0], 10000) == 0) && receivedC[0] == ACK)
{
ackReceived = 1;
}
else
{
errors++;
}
}while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
return 0; /* file trasmitted successfully */
}
/**
* @}
*/
/*******************(C)COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/