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 <String.h> 12 13 #include <stdio.h> 14 #include <string.h> 15 16 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; 24 25 static const int kBlockSize = 128; 26 27 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) 35 { 36 fStatus = "Waiting for receiver" B_UTF8_ELLIPSIS; 37 38 BPositionIO* pos = dynamic_cast<BPositionIO*>(source); 39 if (pos) 40 pos->GetSize(&fSourceSize); 41 else 42 fSourceSize = 0; 43 44 NextBlock(); 45 } 46 47 48 XModemSender::~XModemSender() 49 { 50 delete fSource; 51 } 52 53 54 bool 55 XModemSender::BytesReceived(const uint8_t* data, size_t length) 56 { 57 size_t i; 58 59 for (i = 0; i < length; i++) 60 { 61 switch (data[i]) 62 { 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(); 79 } 80 break; 81 82 case kACK: 83 if (fEotSent) { 84 return true; 85 } 86 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; 94 } 95 break; 96 97 case kCAN: 98 { 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; 105 } 106 107 default: 108 break; 109 } 110 } 111 112 return false; 113 } 114 115 116 void 117 XModemSender::SendBlock() 118 { 119 uint8_t header[3]; 120 uint8_t checksum = 0; 121 int i; 122 123 header[0] = kSOH; 124 header[1] = fBlockNumber; 125 header[2] = 255 - fBlockNumber; 126 127 fSink->Write(header, 3); 128 fSink->Write(fBuffer, kBlockSize); 129 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]; 140 141 fSink->Write(&checksum, 1); 142 } 143 } 144 145 146 status_t 147 XModemSender::NextBlock() 148 { 149 memset(fBuffer, kSUB, kBlockSize); 150 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); 158 159 // Remember that we moved to next block 160 fBlockNumber++; 161 return B_OK; 162 } 163 return B_ERROR; 164 } 165 166 uint16_t XModemSender::CRC(const uint8_t *buf, size_t len) 167 { 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; 177 } 178 } 179 return crc; 180 } 181