libbb: introduce and use common crc32 routine
[platform/upstream/busybox.git] / miscutils / rx.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Copyright:     Copyright (C) 2001, Hewlett-Packard Company
4  * Author:        Christopher Hoover <ch@hpl.hp.com>
5  * Description:   xmodem functionality for uploading of kernels
6  *                and the like
7  * Created at:    Thu Dec 20 01:58:08 PST 2001
8  *
9  * xmodem functionality for uploading of kernels and the like
10  *
11  * Copyright (C) 2001 Hewlett-Packard Laboratories
12  *
13  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
14  *
15  * This was originally written for blob and then adapted for busybox.
16  */
17
18 #include "libbb.h"
19
20 #define SOH 0x01
21 #define STX 0x02
22 #define EOT 0x04
23 #define ACK 0x06
24 #define NAK 0x15
25 #define BS  0x08
26 #define PAD 0x1A
27
28 /*
29 Cf:
30   http://www.textfiles.com/apple/xmodem
31   http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
32   http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
33   http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
34 */
35
36 #define TIMEOUT 1
37 #define TIMEOUT_LONG 10
38 #define MAXERRORS 10
39
40 #define read_fd  STDIN_FILENO
41 #define write_fd STDOUT_FILENO
42
43 static int read_byte(unsigned timeout)
44 {
45         unsigned char buf;
46         int n;
47
48         alarm(timeout);
49         /* NOT safe_read! We want ALRM to interrupt us */
50         n = read(read_fd, &buf, 1);
51         alarm(0);
52         if (n == 1)
53                 return buf;
54         return -1;
55 }
56
57 static int receive(/*int read_fd, */int file_fd)
58 {
59         unsigned char blockBuf[1024];
60         unsigned blockLength = 0;
61         unsigned errors = 0;
62         unsigned wantBlockNo = 1;
63         unsigned length = 0;
64         int do_crc = 1;
65         char reply_char;
66         unsigned timeout = TIMEOUT_LONG;
67
68         /* Flush pending input */
69         tcflush(read_fd, TCIFLUSH);
70
71         /* Ask for CRC; if we get errors, we will go with checksum */
72         reply_char = 'C';
73         full_write(write_fd, &reply_char, 1);
74
75         for (;;) {
76                 int blockBegin;
77                 int blockNo, blockNoOnesCompl;
78                 int cksum_or_crc;
79                 int expected;
80                 int i, j;
81
82                 blockBegin = read_byte(timeout);
83                 if (blockBegin < 0)
84                         goto timeout;
85
86                 /* If last block, remove padding */
87                 if (blockBegin == EOT) {
88                         /* Data blocks can be padded with ^Z characters */
89                         /* This code tries to detect and remove them */
90                         if (blockLength >= 3
91                          && blockBuf[blockLength - 1] == PAD
92                          && blockBuf[blockLength - 2] == PAD
93                          && blockBuf[blockLength - 3] == PAD
94                         ) {
95                                 while (blockLength
96                                    && blockBuf[blockLength - 1] == PAD
97                                 ) {
98                                         blockLength--;
99                                 }
100                         }
101                 }
102                 /* Write previously received block */
103                 if (blockLength) {
104                         errno = 0;
105                         if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
106                                 bb_perror_msg("can't write to file");
107                                 goto fatal;
108                         }
109                 }
110
111                 timeout = TIMEOUT;
112                 reply_char = NAK;
113
114                 switch (blockBegin) {
115                 case SOH:
116                 case STX:
117                         break;
118                 case EOT:
119                         reply_char = ACK;
120                         full_write(write_fd, &reply_char, 1);
121                         return length;
122                 default:
123                         goto error;
124                 }
125
126                 /* Block no */
127                 blockNo = read_byte(TIMEOUT);
128                 if (blockNo < 0)
129                         goto timeout;
130
131                 /* Block no, in one's complement form */
132                 blockNoOnesCompl = read_byte(TIMEOUT);
133                 if (blockNoOnesCompl < 0)
134                         goto timeout;
135
136                 if (blockNo != (255 - blockNoOnesCompl)) {
137                         bb_error_msg("bad block ones compl");
138                         goto error;
139                 }
140
141                 blockLength = (blockBegin == SOH) ? 128 : 1024;
142
143                 for (i = 0; i < blockLength; i++) {
144                         int cc = read_byte(TIMEOUT);
145                         if (cc < 0)
146                                 goto timeout;
147                         blockBuf[i] = cc;
148                 }
149
150                 if (do_crc) {
151                         cksum_or_crc = read_byte(TIMEOUT);
152                         if (cksum_or_crc < 0)
153                                 goto timeout;
154                         cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT);
155                         if (cksum_or_crc < 0)
156                                 goto timeout;
157                 } else {
158                         cksum_or_crc = read_byte(TIMEOUT);
159                         if (cksum_or_crc < 0)
160                                 goto timeout;
161                 }
162
163                 if (blockNo == ((wantBlockNo - 1) & 0xff)) {
164                         /* a repeat of the last block is ok, just ignore it. */
165                         /* this also ignores the initial block 0 which is */
166                         /* meta data. */
167                         goto next;
168                 }
169                 if (blockNo != (wantBlockNo & 0xff)) {
170                         bb_error_msg("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
171                         goto error;
172                 }
173
174                 expected = 0;
175                 if (do_crc) {
176                         for (i = 0; i < blockLength; i++) {
177                                 expected = expected ^ blockBuf[i] << 8;
178                                 for (j = 0; j < 8; j++) {
179                                         if (expected & 0x8000)
180                                                 expected = (expected << 1) ^ 0x1021;
181                                         else
182                                                 expected = (expected << 1);
183                                 }
184                         }
185                         expected &= 0xffff;
186                 } else {
187                         for (i = 0; i < blockLength; i++)
188                                 expected += blockBuf[i];
189                         expected &= 0xff;
190                 }
191                 if (cksum_or_crc != expected) {
192                         bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x"
193                                            : "checksum error, expected 0x%02x, got 0x%02x",
194                                         expected, cksum_or_crc);
195                         goto error;
196                 }
197
198                 wantBlockNo++;
199                 length += blockLength;
200  next:
201                 errors = 0;
202                 reply_char = ACK;
203                 full_write(write_fd, &reply_char, 1);
204                 continue;
205  error:
206  timeout:
207                 errors++;
208                 if (errors == MAXERRORS) {
209                         /* Abort */
210
211                         /* If were asking for crc, try again w/o crc */
212                         if (reply_char == 'C') {
213                                 reply_char = NAK;
214                                 errors = 0;
215                                 do_crc = 0;
216                                 goto timeout;
217                         }
218                         bb_error_msg("too many errors; giving up");
219  fatal:
220                         /* 5 CAN followed by 5 BS. Don't try too hard... */
221                         safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10);
222                         return -1;
223                 }
224
225                 /* Flush pending input */
226                 tcflush(read_fd, TCIFLUSH);
227
228                 full_write(write_fd, &reply_char, 1);
229         } /* for (;;) */
230 }
231
232 static void sigalrm_handler(int UNUSED_PARAM signum)
233 {
234 }
235
236 int rx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
237 int rx_main(int argc UNUSED_PARAM, char **argv)
238 {
239         struct termios tty, orig_tty;
240         int termios_err;
241         int file_fd;
242         int n;
243
244         /* Disabled by vda:
245          * why we can't receive from stdin? Why we *require*
246          * controlling tty?? */
247         /*read_fd = xopen(CURRENT_TTY, O_RDWR);*/
248         file_fd = xopen(single_argv(argv), O_RDWR|O_CREAT|O_TRUNC);
249
250         termios_err = tcgetattr(read_fd, &tty);
251         if (termios_err == 0) {
252                 orig_tty = tty;
253                 cfmakeraw(&tty);
254                 tcsetattr(read_fd, TCSAFLUSH, &tty);
255         }
256
257         /* No SA_RESTART: we want ALRM to interrupt read() */
258         signal_no_SA_RESTART_empty_mask(SIGALRM, sigalrm_handler);
259
260         n = receive(file_fd);
261
262         if (termios_err == 0)
263                 tcsetattr(read_fd, TCSAFLUSH, &orig_tty);
264         if (ENABLE_FEATURE_CLEAN_UP)
265                 close(file_fd);
266         fflush_stdout_and_exit(n >= 0);
267 }