1 /*
2 * Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
3 * Distributed under terms of the MIT license.
4 */
5
6
7 #include "XModem.h"
8
9 #include "SerialApp.h"
10
11 #include <Catalog.h>
12 #include <String.h>
13
14 #include <stdio.h>
15 #include <string.h>
16
17
18 #define B_TRANSLATION_CONTEXT "XModemStatus"
19
20
21 // ASCII control characters used in XMODEM protocol
22 static const char kSOH = 1;
23 static const char kEOT = 4;
24 static const char kACK = 6;
25 static const char kNAK = 21;
26 static const char kCAN = 24;
27 static const char kSUB = 26;
28
29 static const int kBlockSize = 128;
30
31
XModemSender(BDataIO * source,BSerialPort * sink,BHandler * listener)32 XModemSender::XModemSender(BDataIO* source, BSerialPort* sink, BHandler* listener)
33 : fSource(source),
34 fSink(sink),
35 fListener(listener),
36 fBlockNumber(0),
37 fEotSent(false),
38 fUseCRC(false)
39 {
40 fStatus = B_TRANSLATE("Waiting for receiver" B_UTF8_ELLIPSIS);
41
42 BPositionIO* pos = dynamic_cast<BPositionIO*>(source);
43 if (pos)
44 pos->GetSize(&fSourceSize);
45 else
46 fSourceSize = 0;
47
48 NextBlock();
49 }
50
51
~XModemSender()52 XModemSender::~XModemSender()
53 {
54 delete fSource;
55 }
56
57
58 bool
BytesReceived(const uint8_t * data,size_t length)59 XModemSender::BytesReceived(const uint8_t* data, size_t length)
60 {
61 size_t i;
62
63 for (i = 0; i < length; i++)
64 {
65 switch (data[i])
66 {
67 case 'C':
68 // A 'C' to request the first block is a request to use a CRC
69 // in place of an 8-bit checksum.
70 // In any other place, it is ignored.
71 if (fBlockNumber <= 1) {
72 fStatus = B_TRANSLATE("CRC requested");
73 fUseCRC = true;
74 SendBlock();
75 } else
76 break;
77 case kNAK:
78 if (fEotSent) {
79 fSink->Write(&kEOT, 1);
80 } else {
81 fStatus = B_TRANSLATE("Checksum error, re-send block");
82 SendBlock();
83 }
84 break;
85
86 case kACK:
87 if (fEotSent) {
88 return true;
89 }
90
91 if (NextBlock() == B_OK) {
92 fStatus = B_TRANSLATE("Sending" B_UTF8_ELLIPSIS);
93 SendBlock();
94 } else {
95 fStatus = B_TRANSLATE("Everything sent, "
96 "waiting for acknowledge");
97 fSink->Write(&kEOT, 1);
98 fEotSent = true;
99 }
100 break;
101
102 case kCAN:
103 {
104 BMessage msg(kMsgProgress);
105 msg.AddInt32("pos", 0);
106 msg.AddInt32("size", 0);
107 msg.AddString("info",
108 B_TRANSLATE("Remote cancelled transfer"));
109 fListener.SendMessage(&msg);
110 return true;
111 }
112
113 default:
114 break;
115 }
116 }
117
118 return false;
119 }
120
121
122 void
SendBlock()123 XModemSender::SendBlock()
124 {
125 uint8_t header[3];
126 uint8_t checksum = 0;
127 int i;
128
129 header[0] = kSOH;
130 header[1] = fBlockNumber;
131 header[2] = 255 - fBlockNumber;
132
133 fSink->Write(header, 3);
134 fSink->Write(fBuffer, kBlockSize);
135
136 if (fUseCRC) {
137 uint16_t crc = CRC(fBuffer, kBlockSize);
138 uint8_t crcBuf[2];
139 crcBuf[0] = crc >> 8;
140 crcBuf[1] = crc & 0xFF;
141 fSink->Write(crcBuf, 2);
142 } else {
143 // Use a traditional (and fragile) checksum
144 for (i = 0; i < kBlockSize; i++)
145 checksum += fBuffer[i];
146
147 fSink->Write(&checksum, 1);
148 }
149 }
150
151
152 status_t
NextBlock()153 XModemSender::NextBlock()
154 {
155 memset(fBuffer, kSUB, kBlockSize);
156
157 if (fSource->Read(fBuffer, kBlockSize) > 0) {
158 // Notify for progress bar update
159 BMessage msg(kMsgProgress);
160 msg.AddInt32("pos", fBlockNumber);
161 msg.AddInt32("size", fSourceSize / kBlockSize);
162 msg.AddString("info", fStatus);
163 fListener.SendMessage(&msg);
164
165 // Remember that we moved to next block
166 fBlockNumber++;
167 return B_OK;
168 }
169 return B_ERROR;
170 }
171
CRC(const uint8_t * buf,size_t len)172 uint16_t XModemSender::CRC(const uint8_t *buf, size_t len)
173 {
174 uint16_t crc = 0;
175 while( len-- ) {
176 int i;
177 crc ^= ((uint16_t)(*buf++)) << 8;
178 for( i = 0; i < 8; ++i ) {
179 if( crc & 0x8000 )
180 crc = (crc << 1) ^ 0x1021;
181 else
182 crc = crc << 1;
183 }
184 }
185 return crc;
186 }
187