Point to osuosl
[oweals/busybox.git] / miscutils / rx.c
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
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 */