XYModem.Mod 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. MODULE XYModem; (** AUTHOR "ejz"; PURPOSE "X- and Y-Modem protocol implementation"; *)
  2. IMPORT SYSTEM, Objects, Kernel, Streams, Files, Serials, Strings, Commands;
  3. CONST
  4. SOH = 01X; STX = 02X; EOT = 04X; ACK = 06X; EOF = 1AX; NAK = 15X; CAN = 18X; C = 43X;
  5. XModem* = 0; XModem1K* = 1; YModem* = 2;
  6. DoYield = TRUE;
  7. TYPE
  8. Modem* = OBJECT
  9. VAR
  10. W: Streams.Writer; R: Streams.Reader;
  11. F: Files.File;
  12. mode: LONGINT; (* XModem, XModem1K, YModem *)
  13. timeout: LONGINT;
  14. data: ARRAY 1024 OF CHAR;
  15. error: ARRAY 64 OF CHAR;
  16. done, fail: BOOLEAN;
  17. bytesProcessed-, totalBytes- : LONGINT;
  18. PROCEDURE & Init*(W: Streams.Writer; R: Streams.Reader; F: Files.File; mode: LONGINT);
  19. BEGIN
  20. SELF.W := W; SELF.R := R;
  21. SELF.F := F; SELF.mode := mode; error := ""; done := FALSE
  22. END Init;
  23. PROCEDURE IsDone*() : BOOLEAN;
  24. BEGIN
  25. RETURN done;
  26. END IsDone;
  27. PROCEDURE Await*(VAR err: ARRAY OF CHAR);
  28. BEGIN {EXCLUSIVE}
  29. AWAIT(done); COPY(error, err)
  30. END Await;
  31. PROCEDURE AwaitF*(VAR F: Files.File; VAR err: ARRAY OF CHAR);
  32. BEGIN {EXCLUSIVE}
  33. ASSERT(mode = YModem);
  34. AWAIT(done);
  35. F := SELF.F; COPY(error, err)
  36. END AwaitF;
  37. PROCEDURE Stop;
  38. BEGIN {EXCLUSIVE}
  39. done := TRUE
  40. END Stop;
  41. PROCEDURE Read(VAR ch: CHAR): BOOLEAN;
  42. VAR n: LONGINT; milliTimer : Kernel.MilliTimer;
  43. BEGIN
  44. Kernel.SetTimer(milliTimer, timeout);
  45. REPEAT
  46. n := R.Available();
  47. IF DoYield & (n = 0) THEN Objects.Yield; END;
  48. UNTIL (n > 0) OR Kernel.Expired(milliTimer);
  49. IF n = 0 THEN
  50. error := "timeout"; ch := 0X; RETURN FALSE
  51. END;
  52. R.Char(ch); RETURN R.res = Streams.Ok
  53. END Read;
  54. END Modem;
  55. TYPE
  56. Sender* = OBJECT (Modem)
  57. PROCEDURE YHeader(): LONGINT;
  58. VAR fullname, name, pathname, path: Files.FileName; prefix : ARRAY Files.PrefixLength OF CHAR; len, i, j: LONGINT; ch: CHAR;
  59. BEGIN
  60. F.GetName(fullname); len := F.Length();
  61. Files.SplitName(fullname, prefix, pathname);
  62. Files.SplitPath(pathname, path, name);
  63. i := 0; j := 0; ch := name[0];
  64. WHILE ch # 0X DO
  65. IF ch = ":" THEN j := 0 ELSE data[j] := ch; INC(j) END;
  66. INC(i); ch := name[i]
  67. END;
  68. data[j] := 0X; INC(j);
  69. Strings.IntToStr(len, name);
  70. i := 0; ch := name[0];
  71. WHILE ch # 0X DO
  72. data[j] := ch; INC(j); INC(i); ch := name[i]
  73. END;
  74. IF j < 128 THEN len := 128 ELSE len := 1024 END;
  75. ASSERT(j < len);
  76. WHILE j < len DO data[j] := 0X; INC(j) END;
  77. RETURN len
  78. END YHeader;
  79. PROCEDURE SendFile;
  80. VAR fR: Files.Reader; len, blkn, blk, i, retry: LONGINT; start, ch: CHAR;
  81. PROCEDURE SendData;
  82. VAR crc: LONGINT;
  83. BEGIN
  84. W.Char(start); W.Char(CHR(blk)); W.Char(CHR(255-blk));
  85. W.Bytes(data, 0, blkn); crc := CRC16(data, blkn);
  86. W.Char(CHR(crc DIV 256)); W.Char(CHR(crc MOD 256));
  87. W.Update();
  88. IF Read(ch) THEN
  89. IF ch # ACK THEN
  90. IF (ch = NAK) & (retry < 5) THEN
  91. INC(retry)
  92. ELSE
  93. fail := TRUE; error := "expected ACK"
  94. END
  95. ELSE
  96. blk := (blk + 1) MOD 256; retry := 0
  97. END
  98. ELSE
  99. fail := TRUE
  100. END
  101. END SendData;
  102. BEGIN
  103. timeout := 5000;
  104. i := 0; REPEAT INC(i) UNTIL Read(ch) OR (i > 10);
  105. IF ch = C THEN
  106. error := ""
  107. ELSIF ch # 0X THEN
  108. error := "expected C"; RETURN
  109. ELSE
  110. RETURN
  111. END;
  112. timeout := 10000;
  113. IF mode = YModem THEN
  114. IF YHeader() > 128 THEN start := STX; blkn := 1024 ELSE start := SOH; blkn := 128 END;
  115. blk := 0; SendData();
  116. IF ~(Read(ch) & (ch = C)) THEN error := "expected C"; RETURN END
  117. END;
  118. Files.OpenReader(fR, F, 0); len := F.Length(); totalBytes := len;
  119. fail := FALSE; retry := 0; blk := 1; start := STX; blkn := 1024;
  120. REPEAT
  121. IF retry = 0 THEN
  122. IF (mode = XModem) OR (len < (1024-128)) THEN start := SOH; blkn := 128 END;
  123. fR.Bytes(data, 0, blkn, i); DEC(len, blkn); INC(bytesProcessed, i);
  124. IF (fR.res # Streams.Ok) OR (i < blkn) THEN
  125. WHILE i < blkn DO data[i] := EOF; INC(i) END
  126. END
  127. END;
  128. SendData();
  129. UNTIL (fR.res # Streams.Ok) OR fail;
  130. IF ~fail THEN
  131. W.Char(EOT); W.Update();
  132. IF mode = YModem THEN
  133. IF Read(ch) & ((ch = ACK) OR (ch = NAK)) THEN
  134. W.Char(EOT); W.Update();
  135. IF Read(ch) & ((ch = ACK) OR (ch = C)) THEN
  136. IF (ch = C) OR (Read(ch) & (ch = C)) THEN
  137. start := SOH; blkn := 128; blk := 0;
  138. i := 0; WHILE i < blkn DO data[i] := 0X; INC(i) END;
  139. SendData()
  140. END
  141. END
  142. END
  143. ELSE
  144. IF Read(ch) & (ch = ACK) THEN END
  145. END
  146. ELSE
  147. W.Char(CAN); W.Update()
  148. END
  149. END SendFile;
  150. BEGIN {ACTIVE}
  151. SendFile(); Stop()
  152. END Sender;
  153. Receiver* = OBJECT (Modem)
  154. PROCEDURE YHeader(len: LONGINT; VAR name: ARRAY OF CHAR; VAR size: LONGINT);
  155. VAR i, j: LONGINT; str: ARRAY 12 OF CHAR; ch: CHAR;
  156. BEGIN
  157. size := MAX(LONGINT);
  158. i := 0; j := 0; ch := data[0];
  159. WHILE (i < len) & (ch # 0X) DO
  160. name[j] := ch; INC(j); INC(i); ch := data[i]
  161. END;
  162. name[j] := 0X; INC(i);
  163. IF (i < len) & IsDigit(data[i]) THEN
  164. j := 0; ch := data[i];
  165. WHILE (i < len) & IsDigit(data[i]) DO
  166. str[j] := ch; INC(j); INC(i); ch := data[i]
  167. END;
  168. str[j] := 0X; Strings.StrToInt(str, size)
  169. END
  170. END YHeader;
  171. PROCEDURE ReceiveFile;
  172. VAR name: Files.FileName; fW: Files.Writer; len, blkn, i: LONGINT; ch, ch2: CHAR;
  173. PROCEDURE ReceiveData(): BOOLEAN;
  174. VAR len, crc, crcr: LONGINT;
  175. BEGIN
  176. R.Bytes(data, 0, blkn, len); fail := (R.res # Streams.Ok) OR (len # blkn);
  177. IF ~fail THEN
  178. IF Read(ch) & Read(ch2) THEN
  179. crcr := 256*LONG(ORD(ch)) + LONG(ORD(ch2));
  180. crc := CRC16(data, blkn);
  181. IF crc = crcr THEN
  182. W.Char(ACK); W.Update(); i := (i + 1) MOD 256;
  183. RETURN TRUE
  184. ELSE
  185. W.Char(NAK); W.Update()
  186. END
  187. ELSE
  188. fail := TRUE
  189. END
  190. ELSE
  191. error := "receive data failed"
  192. END;
  193. RETURN FALSE
  194. END ReceiveData;
  195. BEGIN
  196. timeout := 5000;
  197. i := 0; REPEAT W.Char(C); W.Update(); INC(i) UNTIL Read(ch) OR (i > 10);
  198. IF ch # 0X THEN error := "" END; len := MAX(LONGINT);
  199. IF F # NIL THEN
  200. Files.OpenWriter(fW, F, 0)
  201. ELSIF mode # YModem THEN
  202. error := "invalid file handle"; RETURN
  203. ELSE
  204. fW := NIL
  205. END;
  206. timeout := 10000;
  207. fail := FALSE; i := 1;
  208. WHILE ~fail & ((ch = SOH) OR (ch = STX)) DO
  209. IF ch = SOH THEN blkn := 128 ELSIF ch = STX THEN blkn := 1024 END;
  210. IF Read(ch) & Read(ch2) THEN
  211. IF (i = ORD(ch)) & (ORD(ch) = (255-ORD(ch2))) THEN
  212. IF fW = NIL THEN
  213. W.Char(CAN); W.Update();
  214. error := "invalid file handle"; RETURN
  215. END;
  216. IF ReceiveData() THEN
  217. IF len < blkn THEN blkn := len END;
  218. fW.Bytes(data, 0, blkn); DEC(len, blkn);
  219. INC(bytesProcessed, blkn);
  220. END
  221. ELSIF (i = 1) & (ch = 0X) & (ch2 = 0FFX) & (mode = YModem) THEN
  222. IF ReceiveData() THEN
  223. YHeader(blkn, name, len); W.Char(C); W.Update();
  224. IF F = NIL THEN F := Files.New(name); Files.OpenWriter(fW, F, 0) END
  225. END;
  226. i := 1
  227. ELSE
  228. fail := TRUE; error := "wrong block number"
  229. END;
  230. IF ~fail THEN fail := ~Read(ch) END
  231. ELSE
  232. fail := TRUE
  233. END
  234. END;
  235. IF ~fail & ((ch = EOT) OR (ch = CAN)) THEN
  236. IF ch = EOT THEN
  237. IF mode = YModem THEN
  238. W.Char(NAK); W.Update();
  239. IF Read(ch) & (ch = EOT) THEN
  240. W.Char(ACK); W.Char(C); W.Update();
  241. IF Read(ch) & (ch = SOH) THEN
  242. IF Read(ch) & Read(ch2) THEN
  243. (* end of single file transfer *)
  244. ASSERT((ch = 0X) & (ch2 = 0FFX));
  245. blkn := 128; fail := ~ReceiveData();
  246. ASSERT(data[0] = 0X)
  247. END
  248. END
  249. END
  250. ELSE
  251. W.Char(ACK); W.Update()
  252. END;
  253. error := ""; fW.Update()
  254. ELSE
  255. W.Char(ACK); W.Update();
  256. error := "transfer aborted"
  257. END
  258. ELSE
  259. W.Char(CAN); W.Update();
  260. IF error = "" THEN error := "wrong block header" END
  261. END
  262. END ReceiveFile;
  263. BEGIN {ACTIVE}
  264. ReceiveFile(); Stop()
  265. END Receiver;
  266. (*
  267. Wait for availability of data from a given reader
  268. minAvailable: minimal amount of available data in bytes to wait for
  269. timeout: timeout in milliseconds
  270. yield: procedure for yielding processing time to other processes; BOOLEAN return parameter allows to cancel the waiting operation
  271. Return: TRUE if at least minAvailable bytes is available
  272. *)
  273. PROCEDURE WaitForData(reader: Streams.Reader; minAvailable, timeout: LONGINT; yield: PROCEDURE{DELEGATE}(): BOOLEAN): BOOLEAN;
  274. VAR milliTimer: Kernel.MilliTimer;
  275. BEGIN
  276. Kernel.SetTimer(milliTimer, timeout);
  277. WHILE (reader.Available() < minAvailable) & (reader.res = Streams.Ok) & ~Kernel.Expired(milliTimer) DO
  278. IF (yield # NIL) & ~yield() THEN RETURN FALSE; END;
  279. END;
  280. (*TRACE(timeout,reader.Available(),reader.res,Kernel.Elapsed(milliTimer));*)
  281. RETURN reader.Available() >= minAvailable;
  282. END WaitForData;
  283. PROCEDURE AbortTransfer(modemWriter: Streams.Writer);
  284. BEGIN
  285. modemWriter.Char(CAN); modemWriter.Char(CAN); modemWriter.Update;
  286. END AbortTransfer;
  287. CONST
  288. TimeoutExpired = 1;
  289. CorruptedData = 2;
  290. InvalidHeader = 3;
  291. InvalidBlockNum = 4;
  292. TransferAborted = 5;
  293. EotError = 6;
  294. ModemIoError = 7;
  295. DataIoError = 8;
  296. FileNameUnspecified = 9;
  297. CreateFileFailure = 10;
  298. MaxNakCount = 3; (* maximal number of NAK's sent for a corrupted data packet *)
  299. PROCEDURE InitReceiveTransfer(
  300. modemReader: Streams.Reader;
  301. modemWriter: Streams.Writer;
  302. mode, initTimeout, dataTimeout: LONGINT;
  303. yield: PROCEDURE{DELEGATE}(): BOOLEAN;
  304. VAR fileName: ARRAY OF CHAR;
  305. VAR fileLength: LONGINT;
  306. VAR res: LONGINT
  307. );
  308. VAR
  309. i, blockSize, n: LONGINT;
  310. ch: CHAR;
  311. blockNum, blockNumCheck: LONGINT;
  312. crc, crcr: LONGINT;
  313. buf: ARRAY 1024 OF CHAR;
  314. (* Process block 0 of an YMODEM transfer *)
  315. PROCEDURE ProcessBlock0();
  316. VAR
  317. k, m: LONGINT;
  318. str: ARRAY 16 OF CHAR;
  319. BEGIN
  320. TRACE(blockSize);
  321. k := 0;
  322. WHILE (k < blockSize) & (buf[k] # 0X) DO
  323. IF k < LEN(fileName) THEN fileName[k] := buf[k]; END;
  324. INC(k);
  325. END;
  326. IF k < LEN(fileName) THEN fileName[k] := 0X;
  327. ELSE fileName[LEN(fileName)-1] := 0X;
  328. END;
  329. TRACE(fileName);
  330. m := 0;
  331. WHILE (k < blockSize) & (buf[k] # 0X) & (m < LEN(str)) DO
  332. str[m] := buf[k];
  333. INC(k); INC(m);
  334. END;
  335. IF m < LEN(str) THEN
  336. str[m] := 0X; Strings.StrToInt(str,fileLength);
  337. TRACE(str,fileLength);
  338. END;
  339. IF fileLength <= 0 THEN fileLength := MAX(LONGINT); END;
  340. END ProcessBlock0;
  341. BEGIN
  342. fileName := "";
  343. fileLength := MAX(LONGINT);
  344. res := 0;
  345. initTimeout := MAX(1,initTimeout DIV 10);
  346. i := 0;
  347. REPEAT
  348. modemWriter.Char(C); modemWriter.Update;
  349. INC(i);
  350. UNTIL WaitForData(modemReader,3,initTimeout,yield) OR (i > 10);
  351. ch := modemReader.Peek();
  352. IF (ch # SOH) & (ch # STX) THEN
  353. AbortTransfer(modemWriter); res := InvalidHeader; TRACE(ORD(ch)); RETURN;
  354. END;
  355. IF mode = YModem THEN
  356. modemReader.SkipBytes(1);
  357. IF ch = SOH THEN blockSize := 128; ELSE blockSize := 1024; END;
  358. blockNum := ORD(modemReader.Get());
  359. blockNumCheck := ORD(modemReader.Get());
  360. IF (blockNum # 0) OR (blockNumCheck # 255) THEN
  361. TRACE(blockNum,blockNumCheck);
  362. (* just in case abort the transfer *)
  363. AbortTransfer(modemWriter); res := InvalidBlockNum; RETURN;
  364. END;
  365. IF ~WaitForData(modemReader,blockSize,dataTimeout,yield) THEN
  366. AbortTransfer(modemWriter); res := TimeoutExpired; TRACE(modemReader.Available()); RETURN;
  367. END;
  368. modemReader.Bytes(buf,0,blockSize,n); ASSERT(n = blockSize);
  369. crcr := 256*LONGINT(ORD(modemReader.Get())) + LONGINT(ORD(modemReader.Get()));
  370. crc := CRC16(buf, blockSize);
  371. IF crc = crcr THEN
  372. ProcessBlock0;
  373. modemWriter.Char(ACK); modemWriter.Char(C); modemWriter.Update;
  374. ELSE
  375. TRACE(crc,crcr);
  376. AbortTransfer(modemWriter); res := CorruptedData; RETURN;
  377. END;
  378. IF (modemWriter.res # Streams.Ok) OR (modemReader.res # Streams.Ok) THEN
  379. res := ModemIoError; TRACE(res);
  380. END;
  381. END;
  382. END InitReceiveTransfer;
  383. PROCEDURE ReceiveFileData(
  384. modemReader: Streams.Reader;
  385. modemWriter, dataWriter: Streams.Writer;
  386. mode, length, timeout: LONGINT;
  387. yield: PROCEDURE{DELEGATE}(): BOOLEAN;
  388. VAR numBytesReceived: LONGINT;
  389. VAR res: LONGINT
  390. );
  391. VAR
  392. ch: CHAR;
  393. blockNum, blockSize, dataLen, n: LONGINT;
  394. blockNum1, blockNum1Check: LONGINT;
  395. data: ARRAY 1024 OF CHAR;
  396. crc, crcr: LONGINT;
  397. nakCount: LONGINT;
  398. PROCEDURE EndOfTransfer();
  399. BEGIN
  400. IF length = MAX(LONGINT) THEN
  401. (* remove EOF padding characters *)
  402. WHILE (dataLen > 0) & (data[dataLen-1] = EOF) DO
  403. DEC(dataLen);
  404. END;
  405. TRACE(dataLen);
  406. END;
  407. (* write the last data block *)
  408. dataWriter.Bytes(data,0,dataLen);
  409. IF dataWriter.res # Streams.Ok THEN res := DataIoError; TRACE(dataWriter.res); RETURN; END;
  410. IF mode = YModem THEN
  411. modemWriter.Char(NAK); modemWriter.Update;
  412. IF ~WaitForData(modemReader,1,timeout,yield) THEN
  413. AbortTransfer(modemWriter); res := EotError; TRACE(modemReader.Available()); RETURN;
  414. END;
  415. ch := modemReader.Get();
  416. IF ch # EOT THEN
  417. TRACE(ORD(ch)); AbortTransfer(modemWriter); res := EotError; RETURN;
  418. END;
  419. modemWriter.Char(ACK); modemWriter.Char(C); modemWriter.Update;
  420. IF ~WaitForData(modemReader,133,timeout,yield) THEN
  421. AbortTransfer(modemWriter); res := EotError;
  422. TRACE(modemReader.Available());
  423. WHILE modemReader.Available() # 0 DO
  424. TRACE(ORD(modemReader.Get()));
  425. END;
  426. RETURN;
  427. END;
  428. ch := modemReader.Get();
  429. blockNum1 := ORD(modemReader.Get());
  430. blockNum1Check := ORD(modemReader.Get());
  431. IF (ch # SOH) OR (blockNum1 # 0) OR (blockNum1Check # 255) THEN
  432. TRACE(ORD(ch),blockNum1,blockNum1Check);
  433. AbortTransfer(modemWriter); res := EotError; RETURN;
  434. END;
  435. modemReader.SkipBytes(128);
  436. crcr := 256*LONGINT(ORD(modemReader.Get())) + LONGINT(ORD(modemReader.Get()));
  437. TRACE(modemReader.Available());
  438. modemWriter.Char(ACK); modemWriter.Update;
  439. ELSE
  440. modemWriter.Char(ACK); modemWriter.Update;
  441. END;
  442. FINALLY
  443. END EndOfTransfer;
  444. BEGIN
  445. res := 0;
  446. numBytesReceived := 0;
  447. blockNum := 1;
  448. nakCount := 0;
  449. LOOP
  450. IF ~WaitForData(modemReader,1,timeout,yield) THEN
  451. AbortTransfer(modemWriter); res := TimeoutExpired; TRACE(modemReader.Available()); RETURN;
  452. END;
  453. ch := modemReader.Get();
  454. CASE ch OF
  455. SOH:
  456. blockSize := 128;
  457. |STX:
  458. blockSize := 1024;
  459. |EOT:
  460. IF numBytesReceived = 0 THEN (* got EOT when no data was received *)
  461. AbortTransfer(modemWriter); res := EotError; TRACE(res); RETURN;
  462. END;
  463. EndOfTransfer; RETURN;
  464. |CAN:
  465. AbortTransfer(modemWriter); res := TransferAborted; TRACE(res); RETURN;
  466. ELSE
  467. TRACE(ORD(ch));
  468. AbortTransfer(modemWriter); res := InvalidHeader; RETURN;
  469. END;
  470. IF ~WaitForData(modemReader,2,timeout,yield) THEN
  471. AbortTransfer(modemWriter); res := TimeoutExpired; TRACE(modemReader.Available()); RETURN;
  472. END;
  473. blockNum1 := ORD(modemReader.Get());
  474. blockNum1Check := ORD(modemReader.Get());
  475. IF (blockNum1 # blockNum) OR (blockNum1Check # 255-blockNum) THEN
  476. TRACE(blockNum,blockNum1,blockNum1Check);
  477. AbortTransfer(modemWriter); res := InvalidBlockNum; TRACE(res); RETURN;
  478. END;
  479. IF ~WaitForData(modemReader,blockSize,timeout,yield) THEN
  480. AbortTransfer(modemWriter); res := TimeoutExpired; TRACE(modemReader.Available()); RETURN;
  481. END;
  482. IF numBytesReceived > 0 THEN (* one block postponed data write out *)
  483. dataWriter.Bytes(data,0,dataLen);
  484. IF dataWriter.res # Streams.Ok THEN res := DataIoError; TRACE(res); RETURN; END;
  485. END;
  486. modemReader.Bytes(data,0,blockSize,n); ASSERT(n = blockSize);
  487. crcr := 256*LONGINT(ORD(modemReader.Get())) + LONGINT(ORD(modemReader.Get()));
  488. crc := CRC16(data, blockSize);
  489. IF crc = crcr THEN
  490. modemWriter.Char(ACK); modemWriter.Update;
  491. dataLen := blockSize;
  492. IF length # MAX(LONGINT) THEN
  493. dataLen := MIN(length,dataLen);
  494. DEC(length,dataLen);
  495. END;
  496. blockNum := (blockNum + 1) MOD 256;
  497. nakCount := 0;
  498. INC(numBytesReceived,dataLen);
  499. ELSE
  500. TRACE(crc,crcr,nakCount);
  501. IF nakCount < MaxNakCount THEN
  502. modemWriter.Char(NAK); modemWriter.Update;
  503. INC(nakCount);
  504. ELSE
  505. AbortTransfer(modemWriter); res := CorruptedData; TRACE(res); RETURN;
  506. END;
  507. END;
  508. IF (modemWriter.res # Streams.Ok) OR (modemReader.res # Streams.Ok) THEN
  509. res := ModemIoError; TRACE(res); RETURN;
  510. END;
  511. END;
  512. END ReceiveFileData;
  513. (** XMODEM or YMODEM receive
  514. modemReader, modemWriter: input/output modem streams
  515. fileName: name of the file where to store received data; can be "" in case of YMODEM, where the file name can be transmitted by the sender
  516. mode: receive mode XModem or XModem1K or YModem
  517. initTimeout: timeout in ms for initiation of the transfer
  518. dataTimeout: timeout in ms for receiving file data
  519. numBytesReceived: number of bytes received
  520. yield: procedure for yielding processing time to other processes while the receiver is waiting for data; BOOLEAN return parameter allows to cancel the waiting operation
  521. res: error code (0 for success)
  522. *)
  523. PROCEDURE Receive*
  524. (
  525. modemReader: Streams.Reader;
  526. modemWriter: Streams.Writer;
  527. VAR fileName: Files.FileName;
  528. mode: LONGINT;
  529. initTimeout, dataTimeout: LONGINT;
  530. VAR numBytesReceived: LONGINT;
  531. yield: PROCEDURE{DELEGATE}(): BOOLEAN;
  532. VAR res: LONGINT
  533. );
  534. VAR
  535. file: Files.File;
  536. length: LONGINT;
  537. name: Files.FileName;
  538. dataWriter: Files.Writer;
  539. BEGIN
  540. IF (mode # YModem) & (fileName = "") THEN
  541. res := FileNameUnspecified; RETURN;
  542. END;
  543. InitReceiveTransfer(modemReader,modemWriter,mode,initTimeout,dataTimeout,yield,name,length,res);
  544. IF res # 0 THEN RETURN; END;
  545. IF fileName # "" THEN
  546. COPY(fileName,name);
  547. ELSIF name = "" THEN
  548. AbortTransfer(modemWriter);
  549. res := FileNameUnspecified; RETURN;
  550. END;
  551. file := Files.New(name);
  552. IF file = NIL THEN
  553. AbortTransfer(modemWriter);
  554. res := CreateFileFailure; RETURN;
  555. END;
  556. Files.OpenWriter(dataWriter,file,0);
  557. ReceiveFileData(modemReader,modemWriter,dataWriter,mode,length,dataTimeout,yield,numBytesReceived,res);
  558. IF res = 0 THEN
  559. dataWriter.Update;
  560. Files.Register(file);
  561. END;
  562. file.Close;
  563. END Receive;
  564. PROCEDURE IsDigit(ch: CHAR): BOOLEAN;
  565. BEGIN
  566. RETURN (ch >= "0") & (ch <= "9")
  567. END IsDigit;
  568. PROCEDURE CRC16(VAR buf: ARRAY OF CHAR; len: LONGINT): LONGINT;
  569. VAR i, k, crc: LONGINT;
  570. BEGIN
  571. crc := 0; i := 0;
  572. WHILE i < len DO
  573. crc := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, crc) / SYSTEM.VAL(SET, LONG(ORD(buf[i]))*LONG(100H)));
  574. k := 0;
  575. WHILE k < 8 DO
  576. IF (15 IN SYSTEM.VAL(SET, crc)) THEN
  577. crc := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, crc*2) / SYSTEM.VAL(SET, 1021H))
  578. ELSE
  579. crc := crc*2
  580. END;
  581. INC(k)
  582. END;
  583. INC(i)
  584. END;
  585. RETURN crc MOD 10000H
  586. END CRC16;
  587. PROCEDURE GetPars(context :Commands.Context; VAR name: ARRAY OF CHAR; VAR port, bps, parity, stop: LONGINT): BOOLEAN;
  588. BEGIN
  589. port := 0; bps := 115200; parity := Serials.ParNo; stop := Serials.Stop1;
  590. IF context.arg.GetString(name) & IsDigit(name[0]) THEN
  591. Strings.StrToInt(name, port);
  592. context.arg.SkipWhitespace; context.arg.Int(bps, FALSE);
  593. context.arg.SkipWhitespace; context.arg.String(name);
  594. IF name = "odd" THEN
  595. parity := Serials.ParOdd
  596. ELSIF name = "even" THEN
  597. parity := Serials.ParEven
  598. ELSIF name = "mark" THEN
  599. parity := Serials.ParMark
  600. ELSIF name = "space" THEN
  601. parity := Serials.ParSpace
  602. ELSIF name # "no" THEN
  603. context.error.String("wrong parity"); context.error.Ln();
  604. context.result := Commands.CommandError; RETURN FALSE;
  605. END;
  606. context.arg.SkipWhitespace; context.arg.String(name);
  607. IF name = "1.5" THEN
  608. stop := Serials.Stop1dot5
  609. ELSIF name = "2" THEN
  610. stop := Serials.Stop2
  611. ELSIF name # "1" THEN
  612. context.error.String("wrong stop bits"); context.error.Ln();
  613. context.result := Commands.CommandError; RETURN FALSE;
  614. END;
  615. context.arg.SkipWhitespace; context.arg.String(name);
  616. END;
  617. RETURN TRUE
  618. END GetPars;
  619. PROCEDURE xySend(context : Commands.Context; mode: LONGINT);
  620. VAR
  621. name: Files.FileName; F: Files.File;
  622. port: Serials.Port; portn, bps, parity, stop: LONGINT; res: WORD;
  623. send: Sender; error: ARRAY 64 OF CHAR;
  624. W: Streams.Writer; R: Streams.Reader;
  625. BEGIN
  626. IF GetPars(context, name, portn, bps, parity, stop) THEN
  627. context.out.String(name); context.out.Ln;
  628. F := Files.Old(name);
  629. port := Serials.GetPort(portn);
  630. ASSERT(port # NIL);
  631. port.Open(bps, 8, parity, stop, res);
  632. ASSERT(res = Serials.Ok);
  633. Streams.OpenWriter(W, port.Send); Streams.OpenReader(R, port.Receive);
  634. NEW(send, W, R, F, mode);
  635. send.Await(error);
  636. port.Close();
  637. IF error # "" THEN
  638. context.error.String(" "); context.error.String(error);
  639. context.result := Commands.CommandError;
  640. ELSE
  641. context.out.String(" done");
  642. END;
  643. context.out.Ln;
  644. END;
  645. END xySend;
  646. PROCEDURE XSend*(context : Commands.Context);
  647. BEGIN
  648. context.out.String("XSend ");
  649. xySend(context, XModem)
  650. END XSend;
  651. PROCEDURE XSend1K*(context : Commands.Context);
  652. BEGIN
  653. context.out.String("XSend1K ");
  654. xySend(context, XModem1K)
  655. END XSend1K;
  656. PROCEDURE YSend*(context : Commands.Context);
  657. BEGIN
  658. context.out.String("YSend ");
  659. xySend(context, YModem)
  660. END YSend;
  661. PROCEDURE xyReceive(context : Commands.Context; mode: LONGINT);
  662. VAR
  663. name: Files.FileName; F: Files.File;
  664. port: Serials.Port; portn, bps, parity, stop: LONGINT; res: WORD;
  665. recv: Receiver; error: ARRAY 64 OF CHAR; awaitF: BOOLEAN;
  666. W: Streams.Writer; R: Streams.Reader;
  667. BEGIN
  668. IF GetPars(context, name, portn, bps, parity, stop) THEN
  669. context.out.String(name); context.out.Ln();
  670. IF name # "" THEN
  671. F := Files.New(name); awaitF := FALSE
  672. ELSE
  673. ASSERT(mode = YModem);
  674. F := NIL; awaitF := TRUE
  675. END;
  676. port := Serials.GetPort(portn);
  677. ASSERT(port # NIL);
  678. port.Open(bps, 8, parity, stop, res);
  679. ASSERT(res = Serials.Ok);
  680. Streams.OpenWriter(W, port.Send); Streams.OpenReader(R, port.Receive);
  681. NEW(recv, W, R, F, mode);
  682. IF ~awaitF THEN
  683. recv.Await(error)
  684. ELSE
  685. recv.AwaitF(F, error)
  686. END;
  687. port.Close();
  688. IF error # "" THEN
  689. context.error.String(" "); context.error.String(error);
  690. context.result := Commands.CommandError;
  691. ELSE
  692. Files.Register(F);
  693. IF awaitF THEN
  694. F.GetName(name);
  695. context.out.String(" "); context.out.String(name);
  696. END;
  697. context.out.String(" done");
  698. END;
  699. context.out.Ln;
  700. END;
  701. END xyReceive;
  702. PROCEDURE XReceive*(context : Commands.Context);
  703. BEGIN
  704. context.out.String("XReceive ");
  705. xyReceive(context, XModem)
  706. END XReceive;
  707. PROCEDURE YReceive*(context : Commands.Context);
  708. BEGIN
  709. context.out.String("YReceive ");
  710. xyReceive(context, YModem)
  711. END YReceive;
  712. END XYModem.
  713. Aos.Call XYModem.YSend 0 115200 no 1 test.dat ~
  714. Aos.Call XYModem.YReceive 0 115200 no 1 test.dat ~
  715. System.Free XYModem ~