|
1 // xmodem.cpp |
|
2 // |
|
3 // Copyright (c) 2008 - 2010 Accenture. All rights reserved. |
|
4 // This component and the accompanying materials are made available |
|
5 // under the terms of the "Eclipse Public License v1.0" |
|
6 // which accompanies this distribution, and is available |
|
7 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 // |
|
9 // Initial Contributors: |
|
10 // Accenture - Initial contribution |
|
11 // |
|
12 |
|
13 #include <fshell/ioutils.h> |
|
14 #include "xmodem.h" |
|
15 |
|
16 using namespace IoUtils; |
|
17 |
|
18 const TInt KMaxRetries = 5; |
|
19 const TInt KSmallBlockSize = 128; |
|
20 const TInt KLargeBlockSize = 1024; |
|
21 const TInt KLongTimeout = 60; |
|
22 const TInt KMediumTimeout = 10; |
|
23 const TInt KShortTimeout = 1; |
|
24 const TUint8 KByteSoh = 0x01; |
|
25 const TUint8 KByteStx = 0x02; |
|
26 const TUint8 KByteEot = 0x04; |
|
27 const TUint8 KByteAck = 0x06; |
|
28 const TUint8 KByteNak = 0x15; |
|
29 const TUint8 KByteCan = 0x18; |
|
30 const TUint8 KByteSub = 0x1a; |
|
31 const TUint8 KByteTelnetIac = 0xff; |
|
32 const TUint8 KByteTelnetDo = 0xfd; |
|
33 const TUint8 KByteTelnetWill = 0xfb; |
|
34 const TUint8 KByteTelnetBinaryMode = 0x00; |
|
35 _LIT(KLitEot, "\x04"); |
|
36 _LIT(KLitAck, "\x06"); |
|
37 _LIT(KLitNak, "\x15"); |
|
38 _LIT(KLitCancel, "\x18\x18\x18"); |
|
39 _LIT(KLitC, "C"); |
|
40 _LIT(KLitTelnetDoBinaryMode, "\xff\xfd\x00"); |
|
41 _LIT(KLitTelnetWillBinaryMode, "\xff\xfb\x00"); |
|
42 |
|
43 #ifdef FSHELL_CORE_SUPPORT_XMODEM_CRCNOTAB |
|
44 |
|
45 TUint16 Crc16(const TDesC& aData) |
|
46 { |
|
47 TInt crc = 0; |
|
48 const TInt length = aData.Length(); |
|
49 for (TInt i = 0; i < length; ++i) |
|
50 { |
|
51 crc = crc ^ ((TInt)(aData[i]&0x00FF) << 8); |
|
52 for (TInt j = 0; j < 8; ++j) |
|
53 { |
|
54 if (crc & 0x8000) |
|
55 { |
|
56 crc = crc << 1 ^ 0x1021; |
|
57 } |
|
58 else |
|
59 { |
|
60 crc = crc << 1; |
|
61 } |
|
62 } |
|
63 } |
|
64 return (crc & 0xFFFF); |
|
65 } |
|
66 |
|
67 #else |
|
68 |
|
69 static TUint16 KCrc16Tab[256]= { |
|
70 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, |
|
71 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, |
|
72 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, |
|
73 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, |
|
74 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, |
|
75 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, |
|
76 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, |
|
77 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, |
|
78 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, |
|
79 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, |
|
80 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, |
|
81 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, |
|
82 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, |
|
83 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, |
|
84 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, |
|
85 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, |
|
86 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, |
|
87 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, |
|
88 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, |
|
89 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, |
|
90 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, |
|
91 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, |
|
92 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, |
|
93 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, |
|
94 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, |
|
95 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, |
|
96 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, |
|
97 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, |
|
98 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, |
|
99 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, |
|
100 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, |
|
101 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 |
|
102 }; |
|
103 |
|
104 TUint16 Crc16(const TDesC& aData) |
|
105 { |
|
106 TUint16 crc = 0; |
|
107 const TInt length = aData.Length(); |
|
108 for (TInt i = 0; i < length; ++i) |
|
109 { |
|
110 crc = (crc<<8) ^ KCrc16Tab[((crc>>8) ^ (TUint8)aData[i])&0x00FF]; |
|
111 } |
|
112 return crc; |
|
113 } |
|
114 |
|
115 #endif // FSHELL_CORE_SUPPORT_XMODEM_CRCNOTAB |
|
116 |
|
117 CCommandBase* CCmdXmodem::NewLC() |
|
118 { |
|
119 CCmdXmodem* self = new(ELeave) CCmdXmodem(); |
|
120 CleanupStack::PushL(self); |
|
121 self->BaseConstructL(); |
|
122 self->ConstructL(); |
|
123 return self; |
|
124 } |
|
125 |
|
126 CCmdXmodem::~CCmdXmodem() |
|
127 { |
|
128 delete iBuf; |
|
129 iTimer.Close(); |
|
130 } |
|
131 |
|
132 CCmdXmodem::CCmdXmodem() : iBlockSize(KSmallBlockSize), iPacketNumber(1) |
|
133 { |
|
134 } |
|
135 |
|
136 void CCmdXmodem::ConstructL() |
|
137 { |
|
138 User::LeaveIfError(iTimer.CreateLocal()); |
|
139 } |
|
140 |
|
141 TUint8 CCmdXmodem::ReceiveByteL(TInt aTimeout, TBool* aTimeoutOccurred) |
|
142 { |
|
143 return (TUint8)ReceiveShortL(aTimeout, aTimeoutOccurred); |
|
144 } |
|
145 |
|
146 TUint16 CCmdXmodem::ReceiveShortL(TInt aTimeout, TBool* aTimeoutOccurred) |
|
147 { |
|
148 TBuf<1> buf; |
|
149 ReceiveWithTimeoutL(buf, aTimeout, aTimeoutOccurred); |
|
150 if (buf.Length() == 1) |
|
151 { |
|
152 return buf[0]; |
|
153 } |
|
154 return 0; |
|
155 } |
|
156 |
|
157 void CCmdXmodem::ReceiveWithTimeoutL(TDes& aBuf, TInt aTimeout, TBool* aTimeoutOccurred) |
|
158 { |
|
159 aBuf.Zero(); |
|
160 TBool timeOutOccurred(EFalse); |
|
161 |
|
162 if (iTelnetMode & ERecvBinary) |
|
163 { |
|
164 TBool prevReceiveEndedWithIac(EFalse); |
|
165 while ((aBuf.Length() < aBuf.MaxLength()) && !timeOutOccurred) |
|
166 { |
|
167 TPtr ptr(const_cast<TUint16*>(aBuf.Ptr()) + aBuf.Length(), 0, aBuf.MaxLength() - aBuf.Length()); // Create a TPtr over the unused part of aBuf. |
|
168 DoReceiveWithTimeoutL(ptr, aTimeout, &timeOutOccurred); |
|
169 if (!timeOutOccurred) |
|
170 { |
|
171 if (prevReceiveEndedWithIac && (ptr.Length() >= 1) && (ptr[0] == KByteTelnetIac)) |
|
172 { |
|
173 ptr.Delete(0, 1); |
|
174 } |
|
175 prevReceiveEndedWithIac = EFalse; |
|
176 TInt numChars = ptr.Length(); |
|
177 for (TInt i = 0; i < numChars; ++i) |
|
178 { |
|
179 if (ptr[i] == KByteTelnetIac) |
|
180 { |
|
181 if ((i < (numChars - 1)) && (ptr[i + 1] == KByteTelnetIac)) |
|
182 { |
|
183 ptr.Delete(i, 1); |
|
184 --numChars; |
|
185 } |
|
186 else if (i == (numChars - 1)) |
|
187 { |
|
188 prevReceiveEndedWithIac = ETrue; |
|
189 } |
|
190 } |
|
191 } |
|
192 aBuf.SetLength(aBuf.Length() + ptr.Length()); |
|
193 } |
|
194 } |
|
195 } |
|
196 else |
|
197 { |
|
198 DoReceiveWithTimeoutL(aBuf, aTimeout, &timeOutOccurred); |
|
199 } |
|
200 |
|
201 if (!timeOutOccurred) |
|
202 { |
|
203 Progress(_L("Received:\r\n")); |
|
204 Dump(aBuf); |
|
205 } |
|
206 |
|
207 if (aTimeoutOccurred) |
|
208 { |
|
209 *aTimeoutOccurred = timeOutOccurred; |
|
210 } |
|
211 } |
|
212 |
|
213 void CCmdXmodem::DoReceiveWithTimeoutL(TDes& aBuf, TInt aTimeout, TBool* aTimeoutOccurred) |
|
214 { |
|
215 iStarted = ETrue; |
|
216 aBuf.Zero(); |
|
217 if (aBuf.MaxLength() <= iUngetBuf.Length()) |
|
218 { |
|
219 aBuf.Copy(iUngetBuf.Mid(0, aBuf.MaxLength())); |
|
220 iUngetBuf.Delete(0, aBuf.MaxLength()); |
|
221 Progress(_L("Received (from unget buf):\r\n")); |
|
222 Dump(aBuf); |
|
223 } |
|
224 else |
|
225 { |
|
226 aBuf.Copy(iUngetBuf); |
|
227 TPtr ptr(const_cast<TUint16*>(aBuf.Ptr()) + aBuf.Length(), 0, aBuf.MaxLength() - aBuf.Length()); // Create a TPtr over the unused part of aBuf. |
|
228 iUngetBuf.Zero(); |
|
229 TRequestStatus readStatus; |
|
230 TRequestStatus timeoutStatus; |
|
231 iTimer.After(timeoutStatus, aTimeout * 1000000); |
|
232 Stdin().Read(aBuf, readStatus); |
|
233 User::WaitForRequest(readStatus, timeoutStatus); |
|
234 if (readStatus == KRequestPending) |
|
235 { |
|
236 Progress(_L("Timeout expired, cancelling read...\r\n")); |
|
237 if (aTimeoutOccurred) |
|
238 { |
|
239 *aTimeoutOccurred = ETrue; |
|
240 } |
|
241 Stdin().ReadCancel(); |
|
242 User::WaitForRequest(readStatus); |
|
243 return; |
|
244 } |
|
245 else |
|
246 { |
|
247 if (aTimeoutOccurred) |
|
248 { |
|
249 *aTimeoutOccurred = EFalse; |
|
250 } |
|
251 iTimer.Cancel(); |
|
252 User::WaitForRequest(timeoutStatus); |
|
253 User::LeaveIfError(readStatus.Int()); |
|
254 aBuf.SetLength(aBuf.Length() + ptr.Length()); |
|
255 Progress(_L("Received (raw):\r\n")); |
|
256 Dump(aBuf); |
|
257 return; |
|
258 } |
|
259 } |
|
260 } |
|
261 |
|
262 void CCmdXmodem::PurgeInputL() |
|
263 { |
|
264 Progress(_L("Purging input...\r\n")); |
|
265 TBool timeoutOccurred; |
|
266 do |
|
267 { |
|
268 ReceiveByteL(KShortTimeout, &timeoutOccurred); |
|
269 if (timeoutOccurred) |
|
270 { |
|
271 Progress(_L("Timed out\r\n")); |
|
272 } |
|
273 else |
|
274 { |
|
275 Progress(_L("Received byte\r\n")); |
|
276 } |
|
277 } |
|
278 while (!timeoutOccurred); |
|
279 } |
|
280 |
|
281 void CCmdXmodem::SendL(const TDesC& aData) |
|
282 { |
|
283 Progress(_L("Sending:\r\n")); |
|
284 Dump(aData); |
|
285 iStarted = ETrue; |
|
286 |
|
287 if (iTelnetMode & ESendBinary) |
|
288 { |
|
289 const TInt numChars = aData.Length(); |
|
290 TInt numIacs = 0; |
|
291 for (TInt i = 0; i < numChars; ++i) |
|
292 { |
|
293 if (aData[i] == KByteTelnetIac) |
|
294 { |
|
295 ++numIacs; |
|
296 } |
|
297 } |
|
298 if (numIacs > 0) |
|
299 { |
|
300 Progress(_L("Escaping %d IACs\r\n"), numIacs); |
|
301 HBufC* newBuf = HBufC::NewLC(aData.Length() + numIacs); |
|
302 TPtr newBufPtr(newBuf->Des()); |
|
303 for (TInt i = 0; i < numChars; ++i) |
|
304 { |
|
305 if (aData[i] == KByteTelnetIac) |
|
306 { |
|
307 newBufPtr.Append(KByteTelnetIac); |
|
308 } |
|
309 newBufPtr.Append(aData[i]); |
|
310 } |
|
311 Dump(*newBuf); |
|
312 User::LeaveIfError(Stdout().Write(*newBuf)); |
|
313 CleanupStack::PopAndDestroy(newBuf); |
|
314 } |
|
315 else |
|
316 { |
|
317 User::LeaveIfError(Stdout().Write(aData)); |
|
318 } |
|
319 } |
|
320 else |
|
321 { |
|
322 User::LeaveIfError(Stdout().Write(aData)); |
|
323 } |
|
324 } |
|
325 |
|
326 TInt CCmdXmodem::CheckSize() const |
|
327 { |
|
328 if (iCrc) |
|
329 { |
|
330 return 2; |
|
331 } |
|
332 else |
|
333 { |
|
334 return 1; |
|
335 } |
|
336 } |
|
337 |
|
338 TInt CCmdXmodem::ProtocolOverhead() const |
|
339 { |
|
340 return 3 + CheckSize(); |
|
341 } |
|
342 |
|
343 TBool CCmdXmodem::CheckBlock() const |
|
344 { |
|
345 TBool match(EFalse); |
|
346 |
|
347 if (iCrc) |
|
348 { |
|
349 TUint16 crc = Crc16(iBuf->Mid(2, iBlockSize)); |
|
350 TUint16 receivedCrc = ((*iBuf)[iBlockSize + 2] << 8) + (*iBuf)[iBlockSize + 3]; |
|
351 match = (crc == receivedCrc); |
|
352 if (!match) |
|
353 { |
|
354 Progress(_L("CRC failed (calculated %u, received %u)\r\n"), crc, receivedCrc); |
|
355 } |
|
356 } |
|
357 else |
|
358 { |
|
359 TUint8 sum = 0; |
|
360 for (TInt i = 2; i < (iBlockSize + 2); ++i) // Plus 2 because iBuf contains the two packet number bytes. |
|
361 { |
|
362 sum += (*iBuf)[i]; |
|
363 } |
|
364 TUint8 received = (*iBuf)[iBlockSize + 2]; |
|
365 match = (sum == received); |
|
366 if (!match) |
|
367 { |
|
368 Progress(_L("CheckSum failed (calculated %u, received %u)\r\n"), sum, received); |
|
369 } |
|
370 } |
|
371 |
|
372 return match; |
|
373 } |
|
374 |
|
375 void CCmdXmodem::Progress(TRefByValue<const TDesC> aFmt, ...) const |
|
376 { |
|
377 if (iVerbose) |
|
378 { |
|
379 TOverflowTruncate overflow; |
|
380 VA_LIST list; |
|
381 VA_START(list, aFmt); |
|
382 TBuf<0x100> buf; |
|
383 buf.AppendFormatList(aFmt, list, &overflow); |
|
384 const_cast<CCmdXmodem*>(this)->Stderr().Write(buf); |
|
385 } |
|
386 } |
|
387 |
|
388 void CCmdXmodem::Dump(const TDesC& aData) |
|
389 { |
|
390 if (iVerbose) |
|
391 { |
|
392 TBuf<80> out; |
|
393 TBuf<16> ascii; |
|
394 TInt dataIndex = 0; |
|
395 TInt pos = 0; |
|
396 do |
|
397 { |
|
398 out.Zero(); |
|
399 ascii.Zero(); |
|
400 out.AppendNumFixedWidthUC(pos, EHex, 8); |
|
401 out.Append(_L(": ")); |
|
402 for (TInt i = 0; i < 16; ++i) |
|
403 { |
|
404 if (dataIndex < aData.Length()) |
|
405 { |
|
406 TUint8 byte = (TUint8)aData[dataIndex++]; |
|
407 out.AppendNumFixedWidthUC(byte, EHex, 2); |
|
408 out.Append(_L(" ")); |
|
409 if ((byte < 0x20) || (byte >= 0x7f) || byte == '%') |
|
410 { |
|
411 byte = '.'; |
|
412 } |
|
413 ascii.Append(TChar(byte)); |
|
414 ++pos; |
|
415 } |
|
416 else |
|
417 { |
|
418 out.Append(_L(" ")); |
|
419 } |
|
420 } |
|
421 out.Append(ascii); |
|
422 out.Append(_L("\r\n")); |
|
423 Stderr().Write(out); |
|
424 } |
|
425 while (dataIndex < aData.Length()); |
|
426 } |
|
427 } |
|
428 |
|
429 void CCmdXmodem::Abort() |
|
430 { |
|
431 TRAP_IGNORE( |
|
432 SendL(KLitCancel); |
|
433 PurgeInputL(); |
|
434 ); |
|
435 } |
|
436 |
|
437 void CCmdXmodem::WaitForSyncL() |
|
438 { |
|
439 Progress(_L("Waiting for sync...\r\n")); |
|
440 for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries) |
|
441 { |
|
442 switch (ReceiveByteL(KLongTimeout)) |
|
443 { |
|
444 case 'C': |
|
445 Progress(_L("Receiver supports CRC\r\n")); |
|
446 iCrc = ETrue; |
|
447 return; |
|
448 case KByteNak: |
|
449 Progress(_L("Receiver does not support CRC\r\n")); |
|
450 iCrc = EFalse; |
|
451 return; |
|
452 case KByteCan: |
|
453 Progress(_L("Receiver cancelled\r\n")); |
|
454 SendL(KLitAck); |
|
455 User::Leave(KErrCancel); |
|
456 break; |
|
457 case KByteTelnetIac: |
|
458 HandleTelnetCommandL(); |
|
459 break; |
|
460 default: |
|
461 break; |
|
462 } |
|
463 } |
|
464 |
|
465 Progress(_L("Failed to sync\r\n")); |
|
466 SendL(KLitCancel); |
|
467 User::Leave(KErrCancel); |
|
468 } |
|
469 |
|
470 CCmdXmodem::TSyncResult CCmdXmodem::SendSyncL() |
|
471 { |
|
472 Progress(_L("Sending sync...\r\n")); |
|
473 iCrc = ETrue; |
|
474 const TDesC* syncLit = &KLitC(); |
|
475 FOREVER |
|
476 { |
|
477 TBool skipNextSync(EFalse); |
|
478 for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries) |
|
479 { |
|
480 if (skipNextSync) |
|
481 { |
|
482 skipNextSync = EFalse; |
|
483 } |
|
484 else |
|
485 { |
|
486 SendL(*syncLit); |
|
487 } |
|
488 switch (ReceiveByteL(KMediumTimeout)) |
|
489 { |
|
490 case KByteSoh: |
|
491 Progress(_L("Using 128 byte block size\r\n")); |
|
492 iBlockSize = KSmallBlockSize; |
|
493 return ENormal; |
|
494 case KByteStx: |
|
495 Progress(_L("Using 1024 byte block size\r\n")); |
|
496 iBlockSize = KLargeBlockSize; |
|
497 return ENormal; |
|
498 case KByteCan: |
|
499 Progress(_L("Sender cancelled\r\n")); |
|
500 SendL(KLitAck); |
|
501 PurgeInputL(); |
|
502 User::Leave(KErrCancel); |
|
503 break; |
|
504 case KByteEot: |
|
505 Progress(_L("Received EOT\r\n")); |
|
506 SendL(KLitAck); |
|
507 return EEot; |
|
508 case KByteTelnetIac: |
|
509 HandleTelnetCommandL(); |
|
510 --numRetries; |
|
511 skipNextSync = ETrue; // This is to avoid HyperTerminal complaining about an "Unexpected response". |
|
512 break; |
|
513 default: |
|
514 break; |
|
515 } |
|
516 } |
|
517 if (syncLit == &KLitC) |
|
518 { |
|
519 Progress(_L("Sender doesn't support CRC\r\n")); |
|
520 syncLit = &KLitNak; |
|
521 iCrc = EFalse; |
|
522 } |
|
523 else |
|
524 { |
|
525 Progress(_L("Failed to sync\r\n")); |
|
526 SendL(KLitCancel); |
|
527 User::Leave(KErrCancel); |
|
528 } |
|
529 } |
|
530 } |
|
531 |
|
532 TBool IsValidTelnetCommand(TChar aChar) |
|
533 { |
|
534 return (aChar == KByteTelnetDo) || (aChar == KByteTelnetWill); |
|
535 } |
|
536 |
|
537 void CCmdXmodem::HandleTelnetCommandL() |
|
538 { |
|
539 Progress(_L("Received (what looks like) the start of a Telnet command\r\n")); |
|
540 TBuf<1> buf; |
|
541 TBool timedOut(EFalse); |
|
542 ReceiveWithTimeoutL(buf, KShortTimeout, &timedOut); |
|
543 if (!timedOut) |
|
544 { |
|
545 TChar command = buf[0]; |
|
546 if (IsValidTelnetCommand(command)) |
|
547 { |
|
548 Progress(_L("Valid telnet command received\r\n")); |
|
549 timedOut = EFalse; |
|
550 ReceiveWithTimeoutL(buf, KShortTimeout, &timedOut); |
|
551 if (timedOut) |
|
552 { |
|
553 Progress(_L("Timed-out, ungetting last character... (2)\r\n")); |
|
554 Unget(command); |
|
555 } |
|
556 else if (buf[0] == KByteTelnetBinaryMode) |
|
557 { |
|
558 if (command == KByteTelnetDo) |
|
559 { |
|
560 iTelnetMode &= (~ESendBinary); |
|
561 SendL(KLitTelnetWillBinaryMode); |
|
562 iTelnetMode |= ESendBinary; |
|
563 Progress(_L("Set ESendBinary\r\n")); |
|
564 } |
|
565 else if (command == KByteTelnetWill) |
|
566 { |
|
567 TBool enableSend = iTelnetMode & ESendBinary; |
|
568 iTelnetMode &= (~ESendBinary); |
|
569 SendL(KLitTelnetDoBinaryMode); |
|
570 iTelnetMode |= ERecvBinary; |
|
571 Progress(_L("Set ERecvBinary\r\n")); |
|
572 if (enableSend) |
|
573 { |
|
574 iTelnetMode |= ESendBinary; |
|
575 } |
|
576 } |
|
577 else |
|
578 { |
|
579 ASSERT(EFalse); |
|
580 } |
|
581 } |
|
582 else |
|
583 { |
|
584 Progress(_L("Unknown command option, ungetting last two characters...\r\n")); |
|
585 Unget(command); |
|
586 Unget(buf[0]); |
|
587 } |
|
588 } |
|
589 else |
|
590 { |
|
591 Progress(_L("Timed-out, ungetting last character... (1)\r\n")); |
|
592 Unget(command); |
|
593 } |
|
594 } |
|
595 Progress(_L("Telnet command handled - resuming...\r\n")); |
|
596 } |
|
597 |
|
598 void CCmdXmodem::Unget(TChar aChar) |
|
599 { |
|
600 ASSERT(iUngetBuf.Length() <= 1); |
|
601 iUngetBuf.Append(aChar); |
|
602 } |
|
603 |
|
604 void CCmdXmodem::PrepareConsoleToTransferL() |
|
605 { |
|
606 Write(_L("Please start the file transfer in your terminal...\r\n")); |
|
607 User::LeaveIfError(Stdin().CaptureAllKeys()); // To prevent other things (like fshell) interpreting binary data as special key presses (like cntrl-C). |
|
608 LeaveIfErr(Stdin().SetMode(RIoReadWriteHandle::EBinary), _L("Unable to set stdin to binary mode")); // To prevent vt100cons from scanning for ANSI escape sequences. |
|
609 LeaveIfErr(Stdout().SetMode(RIoReadWriteHandle::EBinary), _L("Unable to set stdout to binary mode")); // To tell iosrv to not mess about with line endings. |
|
610 } |
|
611 |
|
612 void CCmdXmodem::CleanupClonsoleAfterTransferL() |
|
613 { |
|
614 PurgeInputL(); |
|
615 LeaveIfErr(Stdin().SetMode(RIoReadWriteHandle::EText), _L("Unable to set stdin back to text mode")); |
|
616 LeaveIfErr(Stdout().SetMode(RIoReadWriteHandle::EText), _L("Unable to set stdout back to text mode")); |
|
617 if (Stdout().AttachedToConsole()) |
|
618 { |
|
619 RIoConsoleWriteHandle stdout = Stdout(); |
|
620 TPoint pos(stdout.GetCursorPosL()); |
|
621 stdout.SetCursorPosAbsL(TPoint(0, pos.iY)); |
|
622 stdout.ClearToEndOfLineL(); |
|
623 } |
|
624 } |
|
625 |
|
626 void CCmdXmodem::SendBlockL(const TDesC& aBlock) |
|
627 { |
|
628 Progress(_L("Sending block...\r\n")); |
|
629 ASSERT(aBlock.Length() <= iBlockSize); |
|
630 |
|
631 if (iBuf == NULL) |
|
632 { |
|
633 iBuf = HBufC::NewL(iBlockSize + ProtocolOverhead()); |
|
634 } |
|
635 |
|
636 TPtr buf(iBuf->Des()); |
|
637 |
|
638 FOREVER |
|
639 { |
|
640 buf.Zero(); |
|
641 buf.Append((TUint16)KByteSoh); |
|
642 buf.Append((TUint16)iPacketNumber); |
|
643 buf.Append((TUint16)((TUint8)(~iPacketNumber))); |
|
644 buf.Append(aBlock); |
|
645 if (aBlock.Length() < iBlockSize) |
|
646 { |
|
647 // There's not enough data to fill this block, so pad with SUB. |
|
648 buf.AppendFill((TUint16)KByteSub, iBlockSize - aBlock.Length()); |
|
649 } |
|
650 if (iCrc) |
|
651 { |
|
652 TUint16 crc = Crc16(buf.Mid(3, iBlockSize)); |
|
653 buf.Append((crc >> 8) & 0xFF); |
|
654 buf.Append(crc & 0xFF); |
|
655 } |
|
656 else |
|
657 { |
|
658 TUint8 sum = 0; |
|
659 for (TInt i = 3; i < (iBlockSize + 3); ++i) |
|
660 { |
|
661 sum += (*iBuf)[i]; |
|
662 } |
|
663 buf.Append((TUint16)sum); |
|
664 } |
|
665 |
|
666 Progress(_L("Sending block %d\r\n"), iPacketNumber); |
|
667 |
|
668 for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries) |
|
669 { |
|
670 SendL(*iBuf); |
|
671 TBool timedOut(EFalse); |
|
672 TUint8 byte = ReceiveByteL(KLongTimeout, &timedOut); |
|
673 if (timedOut) |
|
674 { |
|
675 User::Leave(KErrTimedOut); |
|
676 } |
|
677 switch (byte) |
|
678 { |
|
679 case KByteAck: |
|
680 Progress(_L("Block %d successfully sent\r\n"), iPacketNumber); |
|
681 ++iPacketNumber; |
|
682 return; |
|
683 case KByteCan: |
|
684 Progress(_L("Receiver cancelled\r\n")); |
|
685 SendL(KLitAck); |
|
686 PurgeInputL(); |
|
687 User::Leave(KErrCancel); |
|
688 break; |
|
689 case KByteNak: |
|
690 Progress(_L("Block NAK'd, sending again...\r\n")); |
|
691 break; |
|
692 default: |
|
693 Progress(_L("Unexpected response (0x%x), sending again...\r\n"), byte); |
|
694 break; |
|
695 } |
|
696 } |
|
697 } |
|
698 } |
|
699 |
|
700 TPtrC CCmdXmodem::ReceiveBlockL(TBool aIsFirstBlock, TBool& aIsFinalBlock) |
|
701 { |
|
702 _LIT(KFirst, "first "); |
|
703 Progress(_L("Receiving %Sblock...\r\n"), aIsFirstBlock ? &KFirst : &KNullDesC); |
|
704 TPtrC ret(NULL, 0); |
|
705 TInt repeats = 0; |
|
706 aIsFinalBlock = EFalse; |
|
707 |
|
708 if (iBuf == NULL) |
|
709 { |
|
710 iBuf = HBufC::NewL(iBlockSize + ProtocolOverhead()); |
|
711 } |
|
712 if (!aIsFirstBlock) // The first byte of the first block should have already been read by SendSyncL. |
|
713 { |
|
714 again: |
|
715 iBuf->Des().Zero(); |
|
716 TInt numRetries; |
|
717 for (numRetries = 0; numRetries < KMaxRetries; ++numRetries) |
|
718 { |
|
719 TBool startReceiving(EFalse); |
|
720 Progress(_L("Receiving first byte of block...\r\n")); |
|
721 TBool timedOut(EFalse); |
|
722 TUint8 c = ReceiveByteL(KMediumTimeout, &timedOut); |
|
723 if (timedOut) |
|
724 { |
|
725 Progress(_L("Timed out - sending NAK...\r\n")); |
|
726 SendL(KLitNak); |
|
727 } |
|
728 else |
|
729 { |
|
730 switch (c) |
|
731 { |
|
732 case KByteSoh: |
|
733 if (iBlockSize != KSmallBlockSize) |
|
734 { |
|
735 Progress(_L("Block size set to 128 bytes\r\n")); |
|
736 iBlockSize = KSmallBlockSize; |
|
737 } |
|
738 startReceiving = ETrue; |
|
739 break; |
|
740 case KByteStx: |
|
741 if (iBlockSize != KLargeBlockSize) |
|
742 { |
|
743 Progress(_L("Block size set to 1024 bytes\r\n")); |
|
744 iBlockSize = KLargeBlockSize; |
|
745 } |
|
746 startReceiving = ETrue; |
|
747 break; |
|
748 case KByteEot: |
|
749 Progress(_L("Last block received\r\n")); |
|
750 aIsFinalBlock = ETrue; |
|
751 SendL(KLitAck); |
|
752 return ret; |
|
753 case KByteCan: |
|
754 Progress(_L("Sender cancelled\r\n")); |
|
755 SendL(KLitAck); |
|
756 PurgeInputL(); |
|
757 User::Leave(KErrCancel); |
|
758 break; |
|
759 default: |
|
760 break; |
|
761 } |
|
762 |
|
763 if (startReceiving) |
|
764 { |
|
765 break; |
|
766 } |
|
767 } |
|
768 } |
|
769 |
|
770 if (numRetries == KMaxRetries) |
|
771 { |
|
772 Progress(_L("Failed to receive valid block\r\n")); |
|
773 User::Leave(KErrCommsLineFail); |
|
774 } |
|
775 } |
|
776 |
|
777 if (iBuf->Des().MaxLength() < (iBlockSize + ProtocolOverhead())) |
|
778 { |
|
779 Progress(_L("Reallocating buffer from %d to %d bytes\r\n"), iBuf->Des().MaxLength(), iBlockSize + ProtocolOverhead()); |
|
780 iBuf = iBuf->ReAllocL(iBlockSize + ProtocolOverhead()); |
|
781 } |
|
782 |
|
783 Progress(_L("Receiving remainder of block...\r\n")); |
|
784 TPtr ptr(const_cast<TUint16*>(iBuf->Ptr()), 0, iBlockSize + ProtocolOverhead() - 1); // - 1 because the first byte of the header isn't in the buffer. Note, can't use HBufC::Des because the max length of the resulting TPtr could be larger than the amount of data we are expecting. |
|
785 TBool timeoutOccurred; |
|
786 ReceiveWithTimeoutL(ptr, KLongTimeout, &timeoutOccurred); |
|
787 if (timeoutOccurred) |
|
788 { |
|
789 Progress(_L("Timed out, retrying...\r\n")); |
|
790 SendL(KLitNak); |
|
791 PurgeInputL(); |
|
792 goto again; |
|
793 } |
|
794 iBuf->Des().SetLength(ptr.Length()); |
|
795 |
|
796 TUint8 b0 = (*iBuf)[0]; |
|
797 TUint8 b1 = (*iBuf)[1]; |
|
798 // workaround for a compiler bug: and b0 and ~b1 with 0xff, otherwise some compilers will compare |
|
799 // the full 32-bit values, the top 3 bytes of which contain garbage. Observed with WINSW and GCCE |
|
800 // compilers. |
|
801 if (((b0 & 0xff) == ((~b1) & 0xff)) && ((b0 == iPacketNumber) || (b0 == (iPacketNumber - 1))) && CheckBlock()) |
|
802 { |
|
803 Progress(_L("Block successfully received\r\n")); |
|
804 if ((*iBuf)[0] == iPacketNumber) |
|
805 { |
|
806 Progress(_L("Packet number matched (%d)\r\n"), iPacketNumber); |
|
807 ret.Set(iBuf->Mid(2, iBuf->Length() - CheckSize() - 2)); |
|
808 ++iPacketNumber; |
|
809 } |
|
810 repeats = 0; |
|
811 SendL(KLitAck); |
|
812 } |
|
813 else if (++repeats >= KMaxRetries) |
|
814 { |
|
815 Progress(_L("Block not successfully received after %d retries, cancelling...\r\n"), KMaxRetries); |
|
816 SendL(KLitCancel); |
|
817 PurgeInputL(); |
|
818 User::Leave(KErrCancel); |
|
819 } |
|
820 else |
|
821 { |
|
822 Progress(_L("Block not successfully received, retrying...\r\n")); |
|
823 SendL(KLitNak); |
|
824 goto again; |
|
825 } |
|
826 |
|
827 return ret; |
|
828 } |
|
829 |
|
830 void CCmdXmodem::SendTerminateL() |
|
831 { |
|
832 for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries) |
|
833 { |
|
834 SendL(KLitEot); |
|
835 if (ReceiveByteL(KMediumTimeout) == KByteAck) |
|
836 { |
|
837 return; |
|
838 } |
|
839 } |
|
840 PurgeInputL(); |
|
841 User::Leave(KErrCompletion); |
|
842 } |
|
843 |
|
844 void CCmdXmodem::SendStdinL() |
|
845 { |
|
846 HBufC* buf = HBufC::NewLC(iBlockSize); |
|
847 TPtr bufPtr(buf->Des()); |
|
848 |
|
849 while (Stdin().Read(bufPtr) == KErrNone) |
|
850 { |
|
851 SendBlockL(*buf); |
|
852 bufPtr.Zero(); |
|
853 } |
|
854 SendTerminateL(); |
|
855 |
|
856 CleanupStack::PopAndDestroy(buf); |
|
857 } |
|
858 |
|
859 void CCmdXmodem::SendFileL(const TDesC& aFileName) |
|
860 { |
|
861 Progress(_L("Sending file \"%S\"...\r\n"), &aFileName); |
|
862 RFile file; |
|
863 User::LeaveIfError(file.Open(FsL(), aFileName, EFileRead)); |
|
864 CleanupClosePushL(file); |
|
865 |
|
866 HBufC8* buf = HBufC8::NewLC(iBlockSize); |
|
867 HBufC* wideBuf = HBufC::NewLC(iBlockSize); |
|
868 TPtr8 bufPtr(buf->Des()); |
|
869 while ((file.Read(bufPtr, iBlockSize) == KErrNone) && (buf->Length() > 0)) |
|
870 { |
|
871 wideBuf->Des().Copy(*buf); |
|
872 SendBlockL(*wideBuf); |
|
873 } |
|
874 SendTerminateL(); |
|
875 |
|
876 CleanupStack::PopAndDestroy(3, &file); |
|
877 } |
|
878 |
|
879 void CCmdXmodem::ReceiveToStdoutL() |
|
880 { |
|
881 TBool firstBlock(ETrue); |
|
882 TBool finalBlock(EFalse); |
|
883 while (!finalBlock) |
|
884 { |
|
885 TPtrC block(ReceiveBlockL(firstBlock, finalBlock)); |
|
886 User::LeaveIfError(Stdout().Write(block)); |
|
887 firstBlock = EFalse; |
|
888 } |
|
889 } |
|
890 |
|
891 void CCmdXmodem::ReceiveToFileL(const TDesC& aFileName) |
|
892 { |
|
893 RFile file; |
|
894 if (iOverwrite) |
|
895 { |
|
896 FsL().MkDirAll(aFileName); // Create the directory if it doesn't already exist |
|
897 User::LeaveIfError(file.Replace(FsL(), aFileName, EFileWrite)); |
|
898 } |
|
899 else |
|
900 { |
|
901 User::LeaveIfError(file.Create(FsL(), aFileName, EFileWrite)); |
|
902 } |
|
903 CleanupClosePushL(file); |
|
904 |
|
905 HBufC8* narrowBuf = HBufC8::NewLC(iBlockSize); |
|
906 TPtr8 narrowBufPtr(narrowBuf->Des()); |
|
907 TBool firstBlock(ETrue); |
|
908 TBool finalBlock(EFalse); |
|
909 while (!finalBlock) |
|
910 { |
|
911 TPtrC block(ReceiveBlockL(firstBlock, finalBlock)); |
|
912 firstBlock = EFalse; |
|
913 if (block.Length() > narrowBufPtr.MaxLength()) |
|
914 { |
|
915 HBufC8* newNarrowBuf = narrowBuf->ReAllocL(iBuf->Length()); |
|
916 CleanupStack::Pop(narrowBuf); |
|
917 narrowBuf = newNarrowBuf; |
|
918 CleanupStack::PushL(narrowBuf); |
|
919 narrowBufPtr.Set(narrowBuf->Des()); |
|
920 } |
|
921 narrowBufPtr.Copy(block); |
|
922 User::LeaveIfError(file.Write(*narrowBuf)); |
|
923 narrowBufPtr.Zero(); |
|
924 } |
|
925 |
|
926 CleanupStack::PopAndDestroy(2, &file); |
|
927 } |
|
928 |
|
929 void CCmdXmodem::ReceiveToNullL() |
|
930 { |
|
931 TBool firstBlock(EFalse); |
|
932 TBool finalBlock(EFalse); |
|
933 while (!finalBlock) |
|
934 { |
|
935 ReceiveBlockL(firstBlock, finalBlock); |
|
936 firstBlock = EFalse; |
|
937 } |
|
938 } |
|
939 |
|
940 const TDesC& CCmdXmodem::Name() const |
|
941 { |
|
942 _LIT(KName, "xmodem"); |
|
943 return KName; |
|
944 } |
|
945 |
|
946 void CCmdXmodem::DoRunL() |
|
947 { |
|
948 PrepareConsoleToTransferL(); |
|
949 |
|
950 if (iMode == EReceive) |
|
951 { |
|
952 if (iFileName.Length() > 0) |
|
953 { |
|
954 LeaveIfFileExists(iFileName); |
|
955 } |
|
956 TSyncResult syncResult = SendSyncL(); |
|
957 if (syncResult == ENormal) |
|
958 { |
|
959 if (iFileName.Length() > 0) |
|
960 { |
|
961 ReceiveToFileL(iFileName); |
|
962 } |
|
963 else |
|
964 { |
|
965 ReceiveToStdoutL(); |
|
966 } |
|
967 |
|
968 CleanupClonsoleAfterTransferL(); |
|
969 if (iFileName.Length() > 0) |
|
970 { |
|
971 Printf(_L("Successfully received \"%S\".\r\n"), &iFileName); |
|
972 } |
|
973 } |
|
974 } |
|
975 else if (iMode == ESend) |
|
976 { |
|
977 if (iFileName.Length() > 0) |
|
978 { |
|
979 LeaveIfFileNotFound(iFileName); |
|
980 } |
|
981 WaitForSyncL(); |
|
982 if (iFileName.Length() > 0) |
|
983 { |
|
984 SendFileL(iFileName); |
|
985 } |
|
986 else |
|
987 { |
|
988 SendStdinL(); |
|
989 } |
|
990 |
|
991 CleanupClonsoleAfterTransferL(); |
|
992 if (iFileName.Length() > 0) |
|
993 { |
|
994 Printf(_L("Successfully sent \"%S\".\r\n"), &iFileName); |
|
995 } |
|
996 else |
|
997 { |
|
998 Printf(_L("Successfully stdin.\r\n")); |
|
999 } |
|
1000 } |
|
1001 User::LeaveIfError(Stdin().CancelCaptureAllKeys()); |
|
1002 } |
|
1003 |
|
1004 void CCmdXmodem::ArgumentsL(RCommandArgumentList& aArguments) |
|
1005 { |
|
1006 _LIT(KArg1, "mode"); |
|
1007 aArguments.AppendEnumL((TInt&)iMode, KArg1); |
|
1008 _LIT(KArg2, "file_name"); |
|
1009 aArguments.AppendFileNameL(iFileName, KArg2); |
|
1010 } |
|
1011 |
|
1012 void CCmdXmodem::OptionsL(RCommandOptionList& aOptions) |
|
1013 { |
|
1014 _LIT(KOptVerbose, "verbose"); |
|
1015 aOptions.AppendBoolL(iVerbose, KOptVerbose); |
|
1016 |
|
1017 _LIT(KOptOverwrite, "overwrite"); |
|
1018 aOptions.AppendBoolL(iOverwrite, KOptOverwrite); |
|
1019 } |
|
1020 |
|
1021 void CCmdXmodem::HandleLeave(TInt aError) |
|
1022 { |
|
1023 if (iStarted) |
|
1024 { |
|
1025 Abort(); |
|
1026 CleanupClonsoleAfterTransferL(); |
|
1027 } |
|
1028 CCommandBase::HandleLeave(aError); |
|
1029 } |