diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2003-12-20 07:30:35 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2003-12-20 07:30:35 +0000 |
commit | 8f3bc4ce52448a6d1c188055d6920e5bb05334b1 (patch) | |
tree | 03eac311cc3e5d298158998aca9211cc82eb41b6 /miscutils/rx.c | |
parent | a8d82fe52e4a114dcb152ebb2fe0382e10b32267 (diff) | |
download | busybox-w32-8f3bc4ce52448a6d1c188055d6920e5bb05334b1.tar.gz busybox-w32-8f3bc4ce52448a6d1c188055d6920e5bb05334b1.tar.bz2 busybox-w32-8f3bc4ce52448a6d1c188055d6920e5bb05334b1.zip |
New applet, rx, by Christopher Hoover
Diffstat (limited to 'miscutils/rx.c')
-rw-r--r-- | miscutils/rx.c | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/miscutils/rx.c b/miscutils/rx.c new file mode 100644 index 000000000..2b16f2e22 --- /dev/null +++ b/miscutils/rx.c | |||
@@ -0,0 +1,344 @@ | |||
1 | /*------------------------------------------------------------------------- | ||
2 | * Filename: xmodem.c | ||
3 | * Version: $Id: rx.c,v 1.1 2003/12/20 07:30:35 bug1 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 | |||
35 | #include <stdlib.h> | ||
36 | #include <stdarg.h> | ||
37 | #include <stdio.h> | ||
38 | #include <unistd.h> | ||
39 | #include <errno.h> | ||
40 | #include <termios.h> | ||
41 | #include <signal.h> | ||
42 | #include <sys/types.h> | ||
43 | #include <sys/stat.h> | ||
44 | #include <fcntl.h> | ||
45 | #include <string.h> | ||
46 | #include "busybox.h" | ||
47 | |||
48 | |||
49 | #define SOH 0x01 | ||
50 | #define STX 0x02 | ||
51 | #define EOT 0x04 | ||
52 | #define ACK 0x06 | ||
53 | #define NAK 0x15 | ||
54 | #define CAN 0x18 | ||
55 | #define BS 0x08 | ||
56 | |||
57 | /* | ||
58 | |||
59 | Cf: | ||
60 | |||
61 | http://www.textfiles.com/apple/xmodem | ||
62 | http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt | ||
63 | http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt | ||
64 | http://www.phys.washington.edu/~belonis/xmodem/modmprot.col | ||
65 | |||
66 | */ | ||
67 | |||
68 | #define TIMEOUT 1 | ||
69 | #define TIMEOUT_LONG 10 | ||
70 | #define MAXERRORS 10 | ||
71 | |||
72 | static inline void write_byte(int fd, char cc) { | ||
73 | write(fd, &cc, 1); | ||
74 | } | ||
75 | |||
76 | static inline void write_flush(int fd) { | ||
77 | tcdrain(fd); | ||
78 | } | ||
79 | |||
80 | static inline void read_flush(int fd) { | ||
81 | tcflush(fd, TCIFLUSH); | ||
82 | } | ||
83 | |||
84 | static int read_byte(int fd, unsigned int timeout) { | ||
85 | char buf[1]; | ||
86 | int n; | ||
87 | |||
88 | alarm(timeout); | ||
89 | |||
90 | n = read(fd, &buf, 1); | ||
91 | |||
92 | alarm(0); | ||
93 | |||
94 | if (n == 1) | ||
95 | return buf[0] & 0xff; | ||
96 | else | ||
97 | return -1; | ||
98 | } | ||
99 | |||
100 | static int receive(char *error_buf, size_t error_buf_size, | ||
101 | int ttyfd, int filefd) | ||
102 | { | ||
103 | char blockBuf[1024]; | ||
104 | unsigned int errors = 0; | ||
105 | unsigned int wantBlockNo = 1; | ||
106 | unsigned int length = 0; | ||
107 | int docrc = 1; | ||
108 | char nak = 'C'; | ||
109 | unsigned int timeout = TIMEOUT_LONG; | ||
110 | |||
111 | #define note_error(fmt,args...) \ | ||
112 | snprintf(error_buf, error_buf_size, fmt,##args) | ||
113 | |||
114 | read_flush(ttyfd); | ||
115 | |||
116 | /* Ask for CRC; if we get errors, we will go with checksum */ | ||
117 | write_byte(ttyfd, nak); | ||
118 | write_flush(ttyfd); | ||
119 | |||
120 | for (;;) { | ||
121 | int blockBegin; | ||
122 | int blockNo, blockNoOnesCompl; | ||
123 | int blockLength; | ||
124 | int cksum = 0; | ||
125 | int crcHi = 0; | ||
126 | int crcLo = 0; | ||
127 | |||
128 | blockBegin = read_byte(ttyfd, timeout); | ||
129 | if (blockBegin < 0) | ||
130 | goto timeout; | ||
131 | |||
132 | timeout = TIMEOUT; | ||
133 | nak = NAK; | ||
134 | |||
135 | switch (blockBegin) { | ||
136 | case SOH: | ||
137 | case STX: | ||
138 | break; | ||
139 | |||
140 | case EOT: | ||
141 | write_byte(ttyfd, ACK); | ||
142 | write_flush(ttyfd); | ||
143 | goto done; | ||
144 | |||
145 | default: | ||
146 | goto error; | ||
147 | } | ||
148 | |||
149 | /* block no */ | ||
150 | blockNo = read_byte(ttyfd, TIMEOUT); | ||
151 | if (blockNo < 0) | ||
152 | goto timeout; | ||
153 | |||
154 | /* block no one's compliment */ | ||
155 | blockNoOnesCompl = read_byte(ttyfd, TIMEOUT); | ||
156 | if (blockNoOnesCompl < 0) | ||
157 | goto timeout; | ||
158 | |||
159 | if (blockNo != (255 - blockNoOnesCompl)) { | ||
160 | note_error("bad block ones compl"); | ||
161 | goto error; | ||
162 | } | ||
163 | |||
164 | blockLength = (blockBegin == SOH) ? 128 : 1024; | ||
165 | |||
166 | { | ||
167 | int i; | ||
168 | |||
169 | for (i = 0; i < blockLength; i++) { | ||
170 | int cc = read_byte(ttyfd, TIMEOUT); | ||
171 | if (cc < 0) | ||
172 | goto timeout; | ||
173 | blockBuf[i] = cc; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | if (docrc) { | ||
178 | crcHi = read_byte(ttyfd, TIMEOUT); | ||
179 | if (crcHi < 0) | ||
180 | goto timeout; | ||
181 | |||
182 | crcLo = read_byte(ttyfd, TIMEOUT); | ||
183 | if (crcLo < 0) | ||
184 | goto timeout; | ||
185 | } else { | ||
186 | cksum = read_byte(ttyfd, TIMEOUT); | ||
187 | if (cksum < 0) | ||
188 | goto timeout; | ||
189 | } | ||
190 | |||
191 | if (blockNo == ((wantBlockNo - 1) & 0xff)) { | ||
192 | /* a repeat of the last block is ok, just ignore it. */ | ||
193 | /* this also ignores the initial block 0 which is */ | ||
194 | /* meta data. */ | ||
195 | goto next; | ||
196 | } else if (blockNo != (wantBlockNo & 0xff)) { | ||
197 | note_error("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo); | ||
198 | goto error; | ||
199 | } | ||
200 | |||
201 | if (docrc) { | ||
202 | int crc = 0; | ||
203 | int i, j; | ||
204 | int expectedCrcHi; | ||
205 | int expectedCrcLo; | ||
206 | |||
207 | for (i = 0; i < blockLength; i++) { | ||
208 | crc = crc ^ (int) blockBuf[i] << 8; | ||
209 | for (j = 0; j < 8; j++) | ||
210 | if (crc & 0x8000) | ||
211 | crc = crc << 1 ^ 0x1021; | ||
212 | else | ||
213 | crc = crc << 1; | ||
214 | } | ||
215 | |||
216 | expectedCrcHi = (crc >> 8) & 0xff; | ||
217 | expectedCrcLo = crc & 0xff; | ||
218 | |||
219 | if ((crcHi != expectedCrcHi) || | ||
220 | (crcLo != expectedCrcLo)) { | ||
221 | note_error("crc error, expected 0x%02x 0x%02x, got 0x%02x 0x%02x", expectedCrcHi, expectedCrcLo, crcHi, crcLo); | ||
222 | goto error; | ||
223 | } | ||
224 | } else { | ||
225 | unsigned char expectedCksum = 0; | ||
226 | int i; | ||
227 | |||
228 | for (i = 0; i < blockLength; i++) | ||
229 | expectedCksum += blockBuf[i]; | ||
230 | |||
231 | if (cksum != expectedCksum) { | ||
232 | note_error("checksum error, expected 0x%02x, got 0x%02x", expectedCksum, cksum); | ||
233 | goto error; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | wantBlockNo++; | ||
238 | length += blockLength; | ||
239 | |||
240 | if (bb_full_write(filefd, blockBuf, blockLength) < 0) { | ||
241 | note_error("write to file failed: %m"); | ||
242 | goto fatal; | ||
243 | } | ||
244 | |||
245 | next: | ||
246 | errors = 0; | ||
247 | write_byte(ttyfd, ACK); | ||
248 | write_flush(ttyfd); | ||
249 | continue; | ||
250 | |||
251 | error: | ||
252 | timeout: | ||
253 | errors++; | ||
254 | if (errors == MAXERRORS) { | ||
255 | /* Abort */ | ||
256 | int i; | ||
257 | |||
258 | // if using crc, try again w/o crc | ||
259 | if (nak == 'C') { | ||
260 | nak = NAK; | ||
261 | errors = 0; | ||
262 | docrc = 0; | ||
263 | goto timeout; | ||
264 | } | ||
265 | |||
266 | note_error("too many errors; giving up"); | ||
267 | |||
268 | fatal: | ||
269 | for (i = 0; i < 5; i ++) | ||
270 | write_byte(ttyfd, CAN); | ||
271 | for (i = 0; i < 5; i ++) | ||
272 | write_byte(ttyfd, BS); | ||
273 | write_flush(ttyfd); | ||
274 | return -1; | ||
275 | } | ||
276 | |||
277 | read_flush(ttyfd); | ||
278 | write_byte(ttyfd, nak); | ||
279 | write_flush(ttyfd); | ||
280 | } | ||
281 | |||
282 | done: | ||
283 | return length; | ||
284 | |||
285 | #undef note_error | ||
286 | } | ||
287 | |||
288 | static void sigalrm_handler(int signum) | ||
289 | { | ||
290 | } | ||
291 | |||
292 | int rx_main(int argc, char **argv) | ||
293 | { | ||
294 | char *fn; | ||
295 | int ttyfd, filefd; | ||
296 | struct termios tty, orig_tty; | ||
297 | struct sigaction act; | ||
298 | int n; | ||
299 | char error_buf[256]; | ||
300 | |||
301 | if (argc != 2) | ||
302 | bb_show_usage(); | ||
303 | |||
304 | fn = argv[1]; | ||
305 | ttyfd = open("/dev/tty", O_RDWR); | ||
306 | if (ttyfd < 0) | ||
307 | bb_error_msg_and_die("%s: open on /dev/tty failed: %m\n", argv[0]); | ||
308 | |||
309 | filefd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0666); | ||
310 | if (filefd < 0) | ||
311 | bb_error_msg_and_die("%s: open on %s failed: %m\n", argv[0], fn); | ||
312 | |||
313 | if (tcgetattr(ttyfd, &tty) < 0) | ||
314 | bb_error_msg_and_die("%s: tcgetattr failed: %m\n", argv[0]); | ||
315 | |||
316 | orig_tty = tty; | ||
317 | |||
318 | cfmakeraw(&tty); | ||
319 | tcsetattr(ttyfd, TCSAFLUSH, &tty); | ||
320 | |||
321 | memset(&act, 0, sizeof(act)); | ||
322 | act.sa_handler = sigalrm_handler; | ||
323 | sigaction(SIGALRM, &act, 0); | ||
324 | |||
325 | n = receive(error_buf, sizeof(error_buf), ttyfd, filefd); | ||
326 | |||
327 | close(filefd); | ||
328 | |||
329 | tcsetattr(ttyfd, TCSAFLUSH, &orig_tty); | ||
330 | |||
331 | if (n < 0) | ||
332 | bb_error_msg_and_die("\n%s: receive failed:\n %s\n", | ||
333 | argv[0], error_buf); | ||
334 | |||
335 | bb_fflush_stdout_and_exit(EXIT_SUCCESS); | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | Local Variables: | ||
340 | c-file-style: "linux" | ||
341 | c-basic-offset: 4 | ||
342 | tab-width: 4 | ||
343 | End: | ||
344 | */ | ||