repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / serialconnect / XModem.cpp
blob6628b893c930aa61c0f1000c5bc054b8eeddc146
1 /*
2 * Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
3 * Distributed under terms of the MIT license.
4 */
7 #include "XModem.h"
9 #include "SerialApp.h"
11 #include <String.h>
13 #include <stdio.h>
14 #include <string.h>
17 // ASCII control characters used in XMODEM protocol
18 static const char kSOH = 1;
19 static const char kEOT = 4;
20 static const char kACK = 6;
21 static const char kNAK = 21;
22 static const char kCAN = 24;
23 static const char kSUB = 26;
25 static const int kBlockSize = 128;
28 XModemSender::XModemSender(BDataIO* source, BSerialPort* sink, BHandler* listener)
29 : fSource(source),
30 fSink(sink),
31 fListener(listener),
32 fBlockNumber(0),
33 fEotSent(false),
34 fUseCRC(false)
36 fStatus = "Waiting for receiver" B_UTF8_ELLIPSIS;
38 BPositionIO* pos = dynamic_cast<BPositionIO*>(source);
39 if (pos)
40 pos->GetSize(&fSourceSize);
41 else
42 fSourceSize = 0;
44 NextBlock();
48 XModemSender::~XModemSender()
50 delete fSource;
54 bool
55 XModemSender::BytesReceived(const uint8_t* data, size_t length)
57 size_t i;
59 for (i = 0; i < length; i++)
61 switch (data[i])
63 case 'C':
64 // A 'C' to request the first block is a request to use a CRC
65 // in place of an 8-bit checksum.
66 // In any other place, it is ignored.
67 if (fBlockNumber <= 1) {
68 fStatus = "CRC requested";
69 fUseCRC = true;
70 SendBlock();
71 } else
72 break;
73 case kNAK:
74 if (fEotSent) {
75 fSink->Write(&kEOT, 1);
76 } else {
77 fStatus = "Checksum error, re-send block";
78 SendBlock();
80 break;
82 case kACK:
83 if (fEotSent) {
84 return true;
87 if (NextBlock() == B_OK) {
88 fStatus = "Sending" B_UTF8_ELLIPSIS;
89 SendBlock();
90 } else {
91 fStatus = "Everything sent, waiting for acknowledge";
92 fSink->Write(&kEOT, 1);
93 fEotSent = true;
95 break;
97 case kCAN:
99 BMessage msg(kMsgProgress);
100 msg.AddInt32("pos", 0);
101 msg.AddInt32("size", 0);
102 msg.AddString("info", "Remote cancelled transfer");
103 fListener.SendMessage(&msg);
104 return true;
107 default:
108 break;
112 return false;
116 void
117 XModemSender::SendBlock()
119 uint8_t header[3];
120 uint8_t checksum = 0;
121 int i;
123 header[0] = kSOH;
124 header[1] = fBlockNumber;
125 header[2] = 255 - fBlockNumber;
127 fSink->Write(header, 3);
128 fSink->Write(fBuffer, kBlockSize);
130 if (fUseCRC) {
131 uint16_t crc = CRC(fBuffer, kBlockSize);
132 uint8_t crcBuf[2];
133 crcBuf[0] = crc >> 8;
134 crcBuf[1] = crc & 0xFF;
135 fSink->Write(crcBuf, 2);
136 } else {
137 // Use a traditional (and fragile) checksum
138 for (i = 0; i < kBlockSize; i++)
139 checksum += fBuffer[i];
141 fSink->Write(&checksum, 1);
146 status_t
147 XModemSender::NextBlock()
149 memset(fBuffer, kSUB, kBlockSize);
151 if (fSource->Read(fBuffer, kBlockSize) > 0) {
152 // Notify for progress bar update
153 BMessage msg(kMsgProgress);
154 msg.AddInt32("pos", fBlockNumber);
155 msg.AddInt32("size", fSourceSize / kBlockSize);
156 msg.AddString("info", fStatus);
157 fListener.SendMessage(&msg);
159 // Remember that we moved to next block
160 fBlockNumber++;
161 return B_OK;
163 return B_ERROR;
166 uint16_t XModemSender::CRC(const uint8_t *buf, size_t len)
168 uint16_t crc = 0;
169 while( len-- ) {
170 int i;
171 crc ^= ((uint16_t)(*buf++)) << 8;
172 for( i = 0; i < 8; ++i ) {
173 if( crc & 0x8000 )
174 crc = (crc << 1) ^ 0x1021;
175 else
176 crc = crc << 1;
179 return crc;