rx.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /*-------------------------------------------------------------------------
  2. * Filename: xmodem.c
  3. * Version: $Id: rx.c,v 1.2 2004/03/15 08:28:46 andersen Exp $
  4. * Copyright: Copyright (C) 2001, Hewlett-Packard Company
  5. * Author: Christopher Hoover <ch@hpl.hp.com>
  6. * Description: xmodem functionality for uploading of kernels
  7. * and the like
  8. * Created at: Thu Dec 20 01:58:08 PST 2001
  9. *-----------------------------------------------------------------------*/
  10. /*
  11. * xmodem.c: xmodem functionality for uploading of kernels and
  12. * the like
  13. *
  14. * Copyright (C) 2001 Hewlett-Packard Laboratories
  15. *
  16. * This program is free software; you can redistribute it and/or modify
  17. * it under the terms of the GNU General Public License as published by
  18. * the Free Software Foundation; either version 2 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU General Public License
  27. * along with this program; if not, write to the Free Software
  28. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  29. *
  30. *
  31. * This was originally written for blob and then adapted for busybox.
  32. *
  33. */
  34. #include <stdlib.h>
  35. #include <stdarg.h>
  36. #include <stdio.h>
  37. #include <unistd.h>
  38. #include <errno.h>
  39. #include <termios.h>
  40. #include <signal.h>
  41. #include <sys/types.h>
  42. #include <sys/stat.h>
  43. #include <fcntl.h>
  44. #include <string.h>
  45. #include "busybox.h"
  46. #define SOH 0x01
  47. #define STX 0x02
  48. #define EOT 0x04
  49. #define ACK 0x06
  50. #define NAK 0x15
  51. #define CAN 0x18
  52. #define BS 0x08
  53. /*
  54. Cf:
  55. http://www.textfiles.com/apple/xmodem
  56. http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
  57. http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
  58. http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
  59. */
  60. #define TIMEOUT 1
  61. #define TIMEOUT_LONG 10
  62. #define MAXERRORS 10
  63. static inline void write_byte(int fd, char cc) {
  64. write(fd, &cc, 1);
  65. }
  66. static inline void write_flush(int fd) {
  67. tcdrain(fd);
  68. }
  69. static inline void read_flush(int fd) {
  70. tcflush(fd, TCIFLUSH);
  71. }
  72. static int read_byte(int fd, unsigned int timeout) {
  73. char buf[1];
  74. int n;
  75. alarm(timeout);
  76. n = read(fd, &buf, 1);
  77. alarm(0);
  78. if (n == 1)
  79. return buf[0] & 0xff;
  80. else
  81. return -1;
  82. }
  83. static int receive(char *error_buf, size_t error_buf_size,
  84. int ttyfd, int filefd)
  85. {
  86. char blockBuf[1024];
  87. unsigned int errors = 0;
  88. unsigned int wantBlockNo = 1;
  89. unsigned int length = 0;
  90. int docrc = 1;
  91. char nak = 'C';
  92. unsigned int timeout = TIMEOUT_LONG;
  93. #define note_error(fmt,args...) \
  94. snprintf(error_buf, error_buf_size, fmt,##args)
  95. read_flush(ttyfd);
  96. /* Ask for CRC; if we get errors, we will go with checksum */
  97. write_byte(ttyfd, nak);
  98. write_flush(ttyfd);
  99. for (;;) {
  100. int blockBegin;
  101. int blockNo, blockNoOnesCompl;
  102. int blockLength;
  103. int cksum = 0;
  104. int crcHi = 0;
  105. int crcLo = 0;
  106. blockBegin = read_byte(ttyfd, timeout);
  107. if (blockBegin < 0)
  108. goto timeout;
  109. timeout = TIMEOUT;
  110. nak = NAK;
  111. switch (blockBegin) {
  112. case SOH:
  113. case STX:
  114. break;
  115. case EOT:
  116. write_byte(ttyfd, ACK);
  117. write_flush(ttyfd);
  118. goto done;
  119. default:
  120. goto error;
  121. }
  122. /* block no */
  123. blockNo = read_byte(ttyfd, TIMEOUT);
  124. if (blockNo < 0)
  125. goto timeout;
  126. /* block no one's compliment */
  127. blockNoOnesCompl = read_byte(ttyfd, TIMEOUT);
  128. if (blockNoOnesCompl < 0)
  129. goto timeout;
  130. if (blockNo != (255 - blockNoOnesCompl)) {
  131. note_error("bad block ones compl");
  132. goto error;
  133. }
  134. blockLength = (blockBegin == SOH) ? 128 : 1024;
  135. {
  136. int i;
  137. for (i = 0; i < blockLength; i++) {
  138. int cc = read_byte(ttyfd, TIMEOUT);
  139. if (cc < 0)
  140. goto timeout;
  141. blockBuf[i] = cc;
  142. }
  143. }
  144. if (docrc) {
  145. crcHi = read_byte(ttyfd, TIMEOUT);
  146. if (crcHi < 0)
  147. goto timeout;
  148. crcLo = read_byte(ttyfd, TIMEOUT);
  149. if (crcLo < 0)
  150. goto timeout;
  151. } else {
  152. cksum = read_byte(ttyfd, TIMEOUT);
  153. if (cksum < 0)
  154. goto timeout;
  155. }
  156. if (blockNo == ((wantBlockNo - 1) & 0xff)) {
  157. /* a repeat of the last block is ok, just ignore it. */
  158. /* this also ignores the initial block 0 which is */
  159. /* meta data. */
  160. goto next;
  161. } else if (blockNo != (wantBlockNo & 0xff)) {
  162. note_error("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
  163. goto error;
  164. }
  165. if (docrc) {
  166. int crc = 0;
  167. int i, j;
  168. int expectedCrcHi;
  169. int expectedCrcLo;
  170. for (i = 0; i < blockLength; i++) {
  171. crc = crc ^ (int) blockBuf[i] << 8;
  172. for (j = 0; j < 8; j++)
  173. if (crc & 0x8000)
  174. crc = crc << 1 ^ 0x1021;
  175. else
  176. crc = crc << 1;
  177. }
  178. expectedCrcHi = (crc >> 8) & 0xff;
  179. expectedCrcLo = crc & 0xff;
  180. if ((crcHi != expectedCrcHi) ||
  181. (crcLo != expectedCrcLo)) {
  182. note_error("crc error, expected 0x%02x 0x%02x, got 0x%02x 0x%02x", expectedCrcHi, expectedCrcLo, crcHi, crcLo);
  183. goto error;
  184. }
  185. } else {
  186. unsigned char expectedCksum = 0;
  187. int i;
  188. for (i = 0; i < blockLength; i++)
  189. expectedCksum += blockBuf[i];
  190. if (cksum != expectedCksum) {
  191. note_error("checksum error, expected 0x%02x, got 0x%02x", expectedCksum, cksum);
  192. goto error;
  193. }
  194. }
  195. wantBlockNo++;
  196. length += blockLength;
  197. if (bb_full_write(filefd, blockBuf, blockLength) < 0) {
  198. note_error("write to file failed: %m");
  199. goto fatal;
  200. }
  201. next:
  202. errors = 0;
  203. write_byte(ttyfd, ACK);
  204. write_flush(ttyfd);
  205. continue;
  206. error:
  207. timeout:
  208. errors++;
  209. if (errors == MAXERRORS) {
  210. /* Abort */
  211. int i;
  212. // if using crc, try again w/o crc
  213. if (nak == 'C') {
  214. nak = NAK;
  215. errors = 0;
  216. docrc = 0;
  217. goto timeout;
  218. }
  219. note_error("too many errors; giving up");
  220. fatal:
  221. for (i = 0; i < 5; i ++)
  222. write_byte(ttyfd, CAN);
  223. for (i = 0; i < 5; i ++)
  224. write_byte(ttyfd, BS);
  225. write_flush(ttyfd);
  226. return -1;
  227. }
  228. read_flush(ttyfd);
  229. write_byte(ttyfd, nak);
  230. write_flush(ttyfd);
  231. }
  232. done:
  233. return length;
  234. #undef note_error
  235. }
  236. static void sigalrm_handler(int signum)
  237. {
  238. }
  239. int rx_main(int argc, char **argv)
  240. {
  241. char *fn;
  242. int ttyfd, filefd;
  243. struct termios tty, orig_tty;
  244. struct sigaction act;
  245. int n;
  246. char error_buf[256];
  247. if (argc != 2)
  248. bb_show_usage();
  249. fn = argv[1];
  250. ttyfd = open("/dev/tty", O_RDWR);
  251. if (ttyfd < 0)
  252. bb_error_msg_and_die("%s: open on /dev/tty failed: %m\n", argv[0]);
  253. filefd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0666);
  254. if (filefd < 0)
  255. bb_error_msg_and_die("%s: open on %s failed: %m\n", argv[0], fn);
  256. if (tcgetattr(ttyfd, &tty) < 0)
  257. bb_error_msg_and_die("%s: tcgetattr failed: %m\n", argv[0]);
  258. orig_tty = tty;
  259. cfmakeraw(&tty);
  260. tcsetattr(ttyfd, TCSAFLUSH, &tty);
  261. memset(&act, 0, sizeof(act));
  262. act.sa_handler = sigalrm_handler;
  263. sigaction(SIGALRM, &act, 0);
  264. n = receive(error_buf, sizeof(error_buf), ttyfd, filefd);
  265. close(filefd);
  266. tcsetattr(ttyfd, TCSAFLUSH, &orig_tty);
  267. if (n < 0)
  268. bb_error_msg_and_die("\n%s: receive failed:\n %s\n",
  269. argv[0], error_buf);
  270. bb_fflush_stdout_and_exit(EXIT_SUCCESS);
  271. }
  272. /*
  273. Local Variables:
  274. c-file-style: "linux"
  275. c-basic-offset: 4
  276. tab-width: 4
  277. End:
  278. */