Add Ymodem transmit translated to boosts
This commit is contained in:
@@ -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)
|
||||
|
||||
30
Makefile
30
Makefile
@@ -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
44
Thread.h
Normal 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
|
||||
369
XModem.cpp
369
XModem.cpp
@@ -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;
|
||||
}
|
||||
81
XModem.h
81
XModem.h
@@ -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();
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -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",
|
||||
@@ -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
|
||||
}
|
||||
@@ -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" :
|
||||
{
|
||||
@@ -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
|
||||
}
|
||||
],
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
85
main.cpp
85
main.cpp
@@ -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
597
xymodem.c
@@ -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
585
xymodem.cpp
Normal 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
141
xymodem.h
Normal 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
|
||||
671
ymodem.cpp
671
ymodem.cpp
@@ -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>© 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****/
|
||||
Reference in New Issue
Block a user