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

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; }