implement ping using new network stack
[barebox-mini2440.git] / commands / loadb.c
blob6740ef4a48e701a8785c2c4fb38da2092eb62a5a
1 /**
2 * @file
3 * @brief LoadB and LoadY support.
5 * Provides loadb (over Kermit) and LoadY(over Y modem) support to download
6 * images.
8 * FileName: commands/loadb.c
9 */
11 * (C) Copyright 2000-2004
12 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
14 * See file CREDITS for list of people who contributed to this
15 * project.
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of
20 * the License, or (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30 * MA 02111-1307 USA
34 * Serial up- and download support
36 #include <common.h>
37 #include <command.h>
38 #include <xyzModem.h>
39 #include <console.h>
40 #include <errno.h>
41 #include <environment.h>
42 #include <cache.h>
43 #include <getopt.h>
44 #include <fs.h>
45 #include <fcntl.h>
46 #include <malloc.h>
48 #define XON_CHAR 17
49 #define XOFF_CHAR 19
50 #define START_CHAR 0x01
51 #define ETX_CHAR 0x03
52 #define END_CHAR 0x0D
53 #define SPACE 0x20
54 #define K_ESCAPE 0x23
55 #define SEND_TYPE 'S'
56 #define DATA_TYPE 'D'
57 #define ACK_TYPE 'Y'
58 #define NACK_TYPE 'N'
59 #define BREAK_TYPE 'B'
60 #define tochar(x) ((char) (((x) + SPACE) & 0xff))
61 #define untochar(x) ((int) (((x) - SPACE) & 0xff))
62 #define DEF_FILE "image.bin"
64 #ifdef CONFIG_CMD_LOADB
66 static int ofd; /* output file descriptor */
68 /* Size of my buffer to write to o/p file */
69 #define MAX_WRITE_BUFFER 4096 /* Write size to o/p file */
70 static char *write_buffer; /* buffer for finalized data to write */
71 static int write_idx; /* index to the current location in buffer */
73 static char his_eol; /* character he needs at end of packet */
74 static int his_pad_count; /* number of pad chars he needs */
75 static char his_pad_char; /* pad chars he needs */
76 static char his_quote; /* quote chars he'll use */
78 static void send_pad(void)
80 int count = his_pad_count;
82 while (count-- > 0)
83 console_putc(CONSOLE_STDOUT, his_pad_char);
86 /* converts escaped kermit char to binary char */
87 static char ktrans(char in)
89 if ((in & 0x60) == 0x40) {
90 return (char)(in & ~0x40);
91 } else if ((in & 0x7f) == 0x3f) {
92 return (char)(in | 0x40);
93 } else
94 return in;
97 static int chk1(char *buffer)
99 int total = 0;
101 while (*buffer) {
102 total += *buffer++;
104 return (int)((total + ((total >> 6) & 0x03)) & 0x3f);
107 static void s1_sendpacket(char *packet)
109 send_pad();
110 while (*packet) {
111 console_putc(CONSOLE_STDOUT, *packet++);
115 static char a_b[24];
116 static void send_ack(int n)
118 a_b[0] = START_CHAR;
119 a_b[1] = tochar(3);
120 a_b[2] = tochar(n);
121 a_b[3] = ACK_TYPE;
122 a_b[4] = '\0';
123 a_b[4] = tochar(chk1(&a_b[1]));
124 a_b[5] = his_eol;
125 a_b[6] = '\0';
126 s1_sendpacket(a_b);
129 static void send_nack(int n)
131 a_b[0] = START_CHAR;
132 a_b[1] = tochar(3);
133 a_b[2] = tochar(n);
134 a_b[3] = NACK_TYPE;
135 a_b[4] = '\0';
136 a_b[4] = tochar(chk1(&a_b[1]));
137 a_b[5] = his_eol;
138 a_b[6] = '\0';
139 s1_sendpacket(a_b);
142 /* os_data_* data buffer handling before flushing the data to the o/p device
143 * we will only dump valid packets to the device.
145 static int os_data_count, os_old_count, os_data_total;
146 static char *os_data_addr, *os_old_buffer;
148 static void os_data_init(void)
150 os_data_count = 0;
151 os_old_count = 0;
152 os_data_addr = NULL;
153 os_old_buffer = NULL;
156 static int os_data_alloc(int length)
158 /* If already allocated, free the memory
160 if (os_data_addr != NULL) {
161 os_data_addr -= os_data_count;
162 free(os_data_addr);
164 os_data_count = 0;
165 os_data_addr = (char *)malloc(length);
166 if (os_data_addr == NULL)
167 return -1;
168 return 0;
171 static int os_data_save(void)
173 int ret = 0;
174 int copy_size;
175 /* if there was an old data, flush it */
176 if (os_old_buffer) {
177 char *ori_buf = os_old_buffer;
178 do {
179 if (write_idx == MAX_WRITE_BUFFER) {
180 ret = write(ofd, write_buffer,
181 MAX_WRITE_BUFFER);
182 if (ret < 0) {
183 fprintf(stderr,
184 "write to device failed\n");
185 return ret;
187 write_idx = 0;
189 copy_size = min(os_old_count, (MAX_WRITE_BUFFER -
190 write_idx));
191 memcpy(write_buffer + write_idx, os_old_buffer,
192 copy_size);
193 write_idx += copy_size;
194 os_data_total += copy_size;
195 os_old_count -= copy_size;
196 os_old_buffer += copy_size;
197 } while (os_old_count); /* if remaining bytes */
198 free(ori_buf);
200 os_old_count = os_data_count;
201 os_old_buffer = os_data_addr - os_data_count;
202 os_data_count = 0;
203 os_data_addr = NULL;
204 return 0;
207 static void os_data_dump(void)
209 if (os_old_buffer)
210 free(os_old_buffer);
211 if (os_data_addr) {
212 os_data_addr -= os_data_count;
213 free(os_data_addr);
215 os_data_init();
217 static void os_data_char(char new_char)
219 *os_data_addr++ = new_char;
220 os_data_count++;
223 /* k_data_* simply handles the kermit escape translations */
224 static int k_data_escape, k_data_escape_saved;
225 static void k_data_init(void)
227 k_data_escape = 0;
229 static void k_data_save(void)
231 k_data_escape_saved = k_data_escape;
233 static void k_data_restore(void)
235 k_data_escape = k_data_escape_saved;
237 static void k_data_char(char new_char)
239 if (k_data_escape) {
240 /* last char was escape - translate this character */
241 os_data_char(ktrans(new_char));
242 k_data_escape = 0;
243 } else {
244 if (new_char == his_quote) {
245 /* this char is escape - remember */
246 k_data_escape = 1;
247 } else {
248 /* otherwise send this char as-is */
249 os_data_char(new_char);
254 #define SEND_DATA_SIZE 20
255 static char send_parms[SEND_DATA_SIZE];
256 static char *send_ptr;
259 * @brief interprets the protocol info and builds and
260 * sends an appropriate ack for what we can do
262 * @param n sequence
264 * @return void
266 static void handle_send_packet(int n)
268 int length = 3;
269 int bytes;
271 /* initialize some protocol parameters */
272 his_eol = END_CHAR; /* default end of line character */
273 his_pad_count = 0;
274 his_pad_char = '\0';
275 his_quote = K_ESCAPE;
277 /* ignore last character if it filled the buffer */
278 if (send_ptr == &send_parms[SEND_DATA_SIZE - 1])
279 --send_ptr;
280 bytes = send_ptr - send_parms; /* how many bytes we'll process */
281 do {
282 if (bytes-- <= 0)
283 break;
284 /* handle MAXL - max length */
285 /* ignore what he says - most I'll take (here) is 94 */
286 a_b[++length] = tochar(94);
287 if (bytes-- <= 0)
288 break;
289 /* handle TIME - time you should wait for my packets */
290 /* ignore what he says - don't wait for my ack longer than 1 second */
291 a_b[++length] = tochar(1);
292 if (bytes-- <= 0)
293 break;
294 /* handle NPAD - number of pad chars I need */
295 /* remember what he says - I need none */
296 his_pad_count = untochar(send_parms[2]);
297 a_b[++length] = tochar(0);
298 if (bytes-- <= 0)
299 break;
300 /* handle PADC - pad chars I need */
301 /* remember what he says - I need none */
302 his_pad_char = ktrans(send_parms[3]);
303 a_b[++length] = 0x40; /* He should ignore this */
304 if (bytes-- <= 0)
305 break;
306 /* handle EOL - end of line he needs */
307 /* remember what he says - I need CR */
308 his_eol = untochar(send_parms[4]);
309 a_b[++length] = tochar(END_CHAR);
310 if (bytes-- <= 0)
311 break;
312 /* handle QCTL - quote control char he'll use */
313 /* remember what he says - I'll use '#' */
314 his_quote = send_parms[5];
315 a_b[++length] = '#';
316 if (bytes-- <= 0)
317 break;
318 /* handle QBIN - 8-th bit prefixing */
319 /* ignore what he says - I refuse */
320 a_b[++length] = 'N';
321 if (bytes-- <= 0)
322 break;
323 /* handle CHKT - the clock check type */
324 /* ignore what he says - I do type 1 (for now) */
325 a_b[++length] = '1';
326 if (bytes-- <= 0)
327 break;
328 /* handle REPT - the repeat prefix */
329 /* ignore what he says - I refuse (for now) */
330 a_b[++length] = 'N';
331 if (bytes-- <= 0)
332 break;
333 /* handle CAPAS - the capabilities mask */
334 /* ignore what he says - I only do long packets - I don't do windows */
335 a_b[++length] = tochar(2); /* only long packets */
336 a_b[++length] = tochar(0); /* no windows */
337 a_b[++length] = tochar(94); /* large packet msb */
338 a_b[++length] = tochar(94); /* large packet lsb */
339 } while (0);
341 a_b[0] = START_CHAR;
342 a_b[1] = tochar(length);
343 a_b[2] = tochar(n);
344 a_b[3] = ACK_TYPE;
345 a_b[++length] = '\0';
346 a_b[length] = tochar(chk1(&a_b[1]));
347 a_b[++length] = his_eol;
348 a_b[++length] = '\0';
349 s1_sendpacket(a_b);
353 * @brief receives a OS Open image file over kermit line
355 * @return bytes read
357 static int k_recv(void)
359 char new_char;
360 char k_state, k_state_saved;
361 int sum;
362 int done;
363 int length;
364 int n, last_n;
365 int z = 0;
366 int len_lo, len_hi;
368 /* initialize some protocol parameters */
369 his_eol = END_CHAR; /* default end of line character */
370 his_pad_count = 0;
371 his_pad_char = '\0';
372 his_quote = K_ESCAPE;
374 /* initialize the k_recv and k_data state machine */
375 done = 0;
376 k_state = 0;
377 k_data_init();
378 k_state_saved = k_state;
379 k_data_save();
380 os_data_init();
381 os_data_total = 0;
382 n = 0; /* just to get rid of a warning */
383 last_n = -1;
385 /* expect this "type" sequence (but don't check):
386 S: send initiate
387 F: file header
388 D: data (multiple)
389 Z: end of file
390 B: break transmission
393 /* enter main loop */
394 while (!done) {
395 /* set the send packet pointer to begining of send packet parms */
396 send_ptr = send_parms;
398 /* With each packet, start summing the bytes starting with the length.
399 Save the current sequence number.
400 Note the type of the packet.
401 If a character less than SPACE (0x20) is received - error.
404 /* get a packet */
405 /* wait for the starting character or ^C */
406 for (;;) {
407 switch (getc()) {
408 case START_CHAR: /* start packet */
409 goto START;
410 case ETX_CHAR: /* ^C waiting for packet */
411 /* Save last success buffer and quit */
412 (void)os_data_save();
413 return (0);
414 default:
418 START:
419 /* get length of packet */
420 sum = 0;
421 new_char = getc();
422 if ((new_char & 0xE0) == 0)
423 goto packet_error;
424 sum += new_char & 0xff;
425 length = untochar(new_char);
426 /* get sequence number */
427 new_char = getc();
428 if ((new_char & 0xE0) == 0)
429 goto packet_error;
430 sum += new_char & 0xff;
431 n = untochar(new_char);
432 --length;
434 /* NEW CODE - check sequence numbers for retried packets */
435 /* Note - this new code assumes that the sequence number is correctly
436 * received. Handling an invalid sequence number adds another layer
437 * of complexity that may not be needed - yet! At this time, I'm hoping
438 * that I don't need to buffer the incoming data packets and can write
439 * the data into memory in real time.
441 if (n == last_n) {
442 /* same sequence number, restore the previous state */
443 k_state = k_state_saved;
444 k_data_restore();
445 /* Dump any previously allocated data including previous
446 * buffer host wishes to retry.. */
447 os_data_dump();
448 } else {
449 /* new sequence number, checkpoint the download */
450 last_n = n;
451 k_state_saved = k_state;
452 k_data_save();
454 /* END NEW CODE */
456 /* get packet type */
457 new_char = getc();
458 if ((new_char & 0xE0) == 0)
459 goto packet_error;
460 sum += new_char & 0xff;
461 k_state = new_char;
462 --length;
463 /* check for extended length */
464 if (length == -2) {
465 /* (length byte was 0, decremented twice) */
466 /* get the two length bytes */
467 new_char = getc();
468 if ((new_char & 0xE0) == 0)
469 goto packet_error;
470 sum += new_char & 0xff;
471 len_hi = untochar(new_char);
472 new_char = getc();
473 if ((new_char & 0xE0) == 0)
474 goto packet_error;
475 sum += new_char & 0xff;
476 len_lo = untochar(new_char);
477 length = len_hi * 95 + len_lo;
478 /* check header checksum */
479 new_char = getc();
480 if ((new_char & 0xE0) == 0)
481 goto packet_error;
482 if (new_char !=
483 tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
484 goto packet_error;
485 sum += new_char & 0xff;
486 /* --length;
487 * new length includes only data and block check to come
490 /* Try to allocate data chunk */
491 if (length > 1) {
492 if (os_data_alloc(length) < 0) {
493 /* too large.. NACK the data back */
494 goto packet_error;
497 while (length > 1) {
498 new_char = getc();
499 if ((new_char & 0xE0) == 0)
500 goto packet_error;
501 sum += new_char & 0xff;
502 --length;
503 if (k_state == DATA_TYPE) {
504 /* pass on the data if this is a data packet */
505 k_data_char(new_char);
506 } else if (k_state == SEND_TYPE) {
507 /* save send pack in buffer as is */
508 *send_ptr++ = new_char;
509 /* if too much data, back off the pointer */
510 if (send_ptr >= &send_parms[SEND_DATA_SIZE])
511 --send_ptr;
514 /* get and validate checksum character */
515 new_char = getc();
516 if ((new_char & 0xE0) == 0)
517 goto packet_error;
518 if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
519 goto packet_error;
520 /* get END_CHAR */
521 new_char = getc();
522 if (new_char != END_CHAR) {
523 packet_error:
524 /* restore state machines */
525 k_state = k_state_saved;
526 k_data_restore();
527 /* send a negative acknowledge packet in */
528 send_nack(n);
529 } else if (k_state == SEND_TYPE) {
530 /* crack the protocol parms, build an appropriate ack packet */
531 handle_send_packet(n);
532 } else {
533 /* send simple acknowledge packet in */
534 send_ack(n);
535 /* Flush old buffer,
536 * Store current buffer as old buffer */
537 os_data_save();
538 /* quit if end of transmission */
539 if (k_state == BREAK_TYPE) {
540 /* Flush remaining buffer */
541 os_data_save();
542 done = 1;
545 ++z;
547 return ((ulong) os_data_total);
551 * @brief loadb Support over kermit protocol
553 * @return download size
555 static ulong load_serial_bin(void)
557 int size, i;
558 char buf[32];
560 /* Try to allocate the buffer we shall write to files */
561 write_buffer = malloc(MAX_WRITE_BUFFER);
562 if (write_buffer == NULL) {
563 fprintf(stderr, "could not allocate file i/o buffer\n");
564 return -ENOMEM;
567 /* Lets get the image from host */
568 size = k_recv();
571 * Gather any trailing characters (for instance, the ^D which
572 * is sent by 'cu' after sending a file), and give the
573 * box some time (100 * 1 ms)
575 for (i = 0; i < 100; ++i) {
576 if (tstc())
577 (void)getc();
578 udelay(1000);
581 /* Flush out the remaining data if any */
582 if (write_idx > 0) {
583 i = write(ofd, write_buffer, write_idx);
584 if (i < 0) {
585 fprintf(stderr, "write to device failed\n");
586 size = i;
587 goto err_quit;
589 write_idx = 0;
591 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
592 sprintf(buf, "%X", size);
593 setenv("filesize", buf);
595 err_quit:
596 free(write_buffer);
597 return size;
600 #endif /* CONFIG_CMD_LOADB */
602 #ifdef CONFIG_CMD_LOADY
604 * @brief getcxmodem
606 * @return if character avaiable, return the same, else return -1
608 static int getcxmodem(void)
610 if (tstc())
611 return (getc());
612 return -1;
616 * @brief LoadY over ymodem protocol
618 * @return download size
620 static ulong load_serial_ymodem(void)
622 int size;
623 char buf[32];
624 int err;
625 int res, wr;
626 connection_info_t info;
627 char ymodemBuf[1024];
628 ulong store_addr = ~0;
629 ulong addr = 0;
631 size = 0;
632 info.mode = xyzModem_ymodem;
633 res = xyzModem_stream_open(&info, &err);
634 if (!res) {
635 while ((res = xyzModem_stream_read(ymodemBuf, 1024, &err)) >
636 0) {
637 size += res;
638 addr += res;
639 flush_cache((ulong) yModemBuf, res);
640 wr = write(ofd, ymodemBuf, res);
641 if (res != wr) {
642 perror("ymodem");
643 break;
647 } else {
648 printf("%s\n", xyzModem_error(err));
651 xyzModem_stream_close(&err);
652 xyzModem_stream_terminate(false, &getcxmodem);
654 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
655 sprintf(buf, "%X", size);
656 setenv("filesize", buf);
658 return res;
660 #endif
663 * @brief returns current used console device
665 * @return console device which is registered with CONSOLE_STDIN and
666 * CONSOLE_STDOUT
668 static struct console_device *get_current_console(void)
670 struct console_device *cdev;
672 * Assumption to have BOTH CONSOLE_STDIN AND STDOUT in the
673 * same output console
675 for_each_console(cdev) {
676 if ((cdev->f_active & (CONSOLE_STDIN | CONSOLE_STDOUT)))
677 return cdev;
679 return NULL;
683 * @brief provide the loadb(Kermit) or loadY mode support
685 * @param cmdtp
686 * @param argc
687 * @param argv
689 * @return success or failure
691 static int do_load_serial_bin(struct command *cmdtp, int argc, char *argv[])
693 ulong offset = 0;
694 ulong addr;
695 int load_baudrate = 0, current_baudrate;
696 int rcode = 0;
697 int opt;
698 int open_mode = O_WRONLY;
699 char *output_file = NULL;
700 struct console_device *cdev = NULL;
702 while ((opt = getopt(argc, argv, "f:b:o:c")) > 0) {
703 switch (opt) {
704 case 'f':
705 output_file = optarg;
706 break;
707 case 'b':
708 load_baudrate = (int)simple_strtoul(optarg, NULL, 10);
709 break;
710 case 'o':
711 offset = (int)simple_strtoul(optarg, NULL, 10);
712 break;
713 case 'c':
714 open_mode |= O_CREAT;
715 break;
716 default:
717 perror(argv[0]);
718 return 1;
722 cdev = get_current_console();
723 if (NULL == cdev) {
724 printf("%s:No console device with STDIN and STDOUT\n", argv[0]);
725 return -ENODEV;
727 current_baudrate = simple_strtoul(cdev->baudrate_string, NULL, 10);
729 /* Load Defaults */
730 if (load_baudrate == 0)
731 load_baudrate = current_baudrate;
732 if (NULL == output_file)
733 output_file = DEF_FILE;
735 /* File should exist */
736 ofd = open(output_file, open_mode);
737 if (ofd < 0) {
738 perror(argv[0]);
739 return 3;
741 /* Seek to the right offset */
742 if (offset) {
743 int seek = lseek(ofd, offset, SEEK_SET);
744 if (seek != offset) {
745 close(ofd);
746 ofd = 0;
747 perror(argv[0]);
748 return 4;
752 if (load_baudrate != current_baudrate) {
753 printf("## Switch baudrate to %d bps and press ENTER ...\n",
754 load_baudrate);
755 udelay(50000);
756 cdev->setbrg(cdev, load_baudrate);
757 udelay(50000);
758 for (;;) {
759 if (getc() == '\r')
760 break;
763 #ifdef CONFIG_CMD_LOADY
764 if (strcmp(argv[0], "loady") == 0) {
765 printf("## Ready for binary (ymodem) download "
766 "to 0x%08lX offset on %s device at %d bps...\n", offset,
767 output_file, load_baudrate);
768 addr = load_serial_ymodem();
770 #endif
771 #ifdef CONFIG_CMD_LOADB
772 if (strcmp(argv[0], "loadb") == 0) {
774 printf("## Ready for binary (kermit) download "
775 "to 0x%08lX offset on %s device at %d bps...\n", offset,
776 output_file, load_baudrate);
777 addr = load_serial_bin();
778 if (addr == 0) {
779 printf("## Binary (kermit) download aborted\n");
780 rcode = 1;
783 #endif
784 if (load_baudrate != current_baudrate) {
785 printf("## Switch baudrate to %d bps and press ESC ...\n",
786 current_baudrate);
787 udelay(50000);
788 cdev->setbrg(cdev, current_baudrate);
789 udelay(50000);
790 for (;;) {
791 if (getc() == 0x1B) /* ESC */
792 break;
796 close(ofd);
797 ofd = 0;
798 return rcode;
801 static const __maybe_unused char cmd_loadb_help[] =
802 "[OPTIONS]\n"
803 " -f file - where to download to - defaults to " DEF_FILE "\n"
804 " -o offset - what offset to download - defaults to 0\n"
805 " -b baud - baudrate at which to download - defaults to "
806 "console baudrate"
807 " -c - Create file if it is not present - default disabled";
808 #ifdef CONFIG_CMD_LOADB
809 BAREBOX_CMD_START(loadb)
810 .cmd = do_load_serial_bin,
811 .usage = "Load binary file over serial line (kermit mode)",
812 BAREBOX_CMD_HELP(cmd_loadb_help)
813 BAREBOX_CMD_END
814 #endif
815 #ifdef CONFIG_CMD_LOADY
816 BAREBOX_CMD_START(loady)
817 .cmd = do_load_serial_bin,
818 .usage = "Load binary file over serial line (ymodem mode)",
819 BAREBOX_CMD_HELP(cmd_loadb_help)
820 BAREBOX_CMD_END
821 #endif