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 kSUB = 26; 23 24 static const int kBlockSize = 128; 25 26 27 XModemSender::XModemSender(BDataIO* source, BSerialPort* sink, BHandler* listener) 28 : fSource(source), 29 fSink(sink), 30 fListener(listener), 31 fBlockNumber(0), 32 fEotSent(false), 33 fUseCRC(false) 34 { 35 fStatus = "Waiting for receiver" B_UTF8_ELLIPSIS; 36 37 BPositionIO* pos = dynamic_cast<BPositionIO*>(source); 38 if (pos) 39 pos->GetSize(&fSourceSize); 40 else 41 fSourceSize = 0; 42 43 NextBlock(); 44 } 45 46 47 XModemSender::~XModemSender() 48 { 49 delete fSource; 50 } 51 52 53 bool 54 XModemSender::BytesReceived(const uint8_t* data, size_t length) 55 { 56 size_t i; 57 58 for (i = 0; i < length; i++) 59 { 60 switch (data[i]) 61 { 62 case 'C': 63 // A 'C' to request the first block is a request to use a CRC 64 // in place of an 8-bit checksum. 65 // In any other place, it is ignored. 66 if (fBlockNumber <= 1) 67 fUseCRC = true; 68 else 69 break; 70 case kNAK: 71 if (fEotSent) { 72 fSink->Write(&kEOT, 1); 73 } else { 74 fStatus = "Checksum error, re-send block"; 75 SendBlock(); 76 } 77 break; 78 79 case kACK: 80 if (fEotSent) { 81 return true; 82 } 83 84 if (NextBlock() == B_OK) { 85 fStatus = "Sending" B_UTF8_ELLIPSIS; 86 SendBlock(); 87 } else { 88 fStatus = "Everything sent, waiting for acknowledge"; 89 fSink->Write(&kEOT, 1); 90 fEotSent = true; 91 } 92 break; 93 94 default: 95 break; 96 } 97 } 98 99 return false; 100 } 101 102 103 void 104 XModemSender::SendBlock() 105 { 106 uint8_t header[3]; 107 uint8_t checksum = 0; 108 int i; 109 110 header[0] = kSOH; 111 header[1] = fBlockNumber; 112 header[2] = 255 - fBlockNumber; 113 114 fSink->Write(header, 3); 115 fSink->Write(fBuffer, kBlockSize); 116 117 if (fUseCRC) { 118 uint16_t crc = CRC(fBuffer, kBlockSize); 119 uint8_t crcBuf[2]; 120 crcBuf[0] = crc >> 8; 121 crcBuf[1] = crc & 0xFF; 122 fSink->Write(crcBuf, 2); 123 } else { 124 // Use a traditional (and fragile) checksum 125 for (i = 0; i < kBlockSize; i++) 126 checksum += fBuffer[i]; 127 128 fSink->Write(&checksum, 1); 129 } 130 } 131 132 133 status_t 134 XModemSender::NextBlock() 135 { 136 memset(fBuffer, kSUB, kBlockSize); 137 138 if (fSource->Read(fBuffer, kBlockSize) > 0) { 139 // Notify for progress bar update 140 BMessage msg(kMsgProgress); 141 msg.AddInt32("pos", fBlockNumber); 142 msg.AddInt32("size", fSourceSize / kBlockSize); 143 msg.AddString("info", fStatus); 144 fListener.SendMessage(&msg); 145 146 // Remember that we moved to next block 147 fBlockNumber++; 148 return B_OK; 149 } 150 return B_ERROR; 151 } 152 153 uint16_t XModemSender::CRC(const uint8_t *buf, size_t len) 154 { 155 uint16_t crc = 0; 156 while( len-- ) { 157 int i; 158 crc ^= ((uint16_t)(*buf++)) << 8; 159 for( i = 0; i < 8; ++i ) { 160 if( crc & 0x8000 ) 161 crc = (crc << 1) ^ 0x1021; 162 else 163 crc = crc << 1; 164 } 165 } 166 return crc; 167 } 168