tools: kwboot: Properly finish xmodem transfer
[platform/kernel/u-boot.git] / tools / kwboot.c
1 /*
2  * Boot a Marvell SoC, with Xmodem over UART0.
3  *  supports Kirkwood, Dove, Armada 370, Armada XP
4  *
5  * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
6  *
7  * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
8  *   Integrated Controller: Functional Specifications" December 2,
9  *   2008. Chapter 24.2 "BootROM Firmware".
10  */
11
12 #include "kwbimage.h"
13 #include "mkimage.h"
14 #include "version.h"
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdarg.h>
20 #include <image.h>
21 #include <libgen.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <stdint.h>
26 #include <termios.h>
27 #include <time.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30
31 /*
32  * Marvell BootROM UART Sensing
33  */
34
35 static unsigned char kwboot_msg_boot[] = {
36         0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
37 };
38
39 static unsigned char kwboot_msg_debug[] = {
40         0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
41 };
42
43 /* Defines known to work on Kirkwood */
44 #define KWBOOT_MSG_REQ_DELAY    10 /* ms */
45 #define KWBOOT_MSG_RSP_TIMEO    50 /* ms */
46
47 /* Defines known to work on Armada XP */
48 #define KWBOOT_MSG_REQ_DELAY_AXP        1000 /* ms */
49 #define KWBOOT_MSG_RSP_TIMEO_AXP        1000 /* ms */
50
51 /*
52  * Xmodem Transfers
53  */
54
55 #define SOH     1       /* sender start of block header */
56 #define EOT     4       /* sender end of block transfer */
57 #define ACK     6       /* target block ack */
58 #define NAK     21      /* target block negative ack */
59 #define CAN     24      /* target/sender transfer cancellation */
60
61 #define KWBOOT_XM_BLKSZ 128 /* xmodem block size */
62
63 struct kwboot_block {
64         uint8_t soh;
65         uint8_t pnum;
66         uint8_t _pnum;
67         uint8_t data[KWBOOT_XM_BLKSZ];
68         uint8_t csum;
69 } __packed;
70
71 #define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
72 #define KWBOOT_HDR_RSP_TIMEO 10000 /* ms */
73
74 static int kwboot_verbose;
75
76 static int msg_req_delay = KWBOOT_MSG_REQ_DELAY;
77 static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO;
78 static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO;
79
80 static ssize_t
81 kwboot_write(int fd, const char *buf, size_t len)
82 {
83         size_t tot = 0;
84
85         while (tot < len) {
86                 ssize_t wr = write(fd, buf + tot, len - tot);
87
88                 if (wr < 0)
89                         return -1;
90
91                 tot += wr;
92         }
93
94         return tot;
95 }
96
97 static void
98 kwboot_printv(const char *fmt, ...)
99 {
100         va_list ap;
101
102         if (kwboot_verbose) {
103                 va_start(ap, fmt);
104                 vprintf(fmt, ap);
105                 va_end(ap);
106                 fflush(stdout);
107         }
108 }
109
110 static void
111 __spinner(void)
112 {
113         const char seq[] = { '-', '\\', '|', '/' };
114         const int div = 8;
115         static int state, bs;
116
117         if (state % div == 0) {
118                 fputc(bs, stdout);
119                 fputc(seq[state / div % sizeof(seq)], stdout);
120                 fflush(stdout);
121         }
122
123         bs = '\b';
124         state++;
125 }
126
127 static void
128 kwboot_spinner(void)
129 {
130         if (kwboot_verbose)
131                 __spinner();
132 }
133
134 static void
135 __progress(int pct, char c)
136 {
137         const int width = 70;
138         static const char *nl = "";
139         static int pos;
140
141         if (pos % width == 0)
142                 printf("%s%3d %% [", nl, pct);
143
144         fputc(c, stdout);
145
146         nl = "]\n";
147         pos = (pos + 1) % width;
148
149         if (pct == 100) {
150                 while (pos && pos++ < width)
151                         fputc(' ', stdout);
152                 fputs(nl, stdout);
153                 nl = "";
154                 pos = 0;
155         }
156
157         fflush(stdout);
158
159 }
160
161 static void
162 kwboot_progress(int _pct, char c)
163 {
164         static int pct;
165
166         if (_pct != -1)
167                 pct = _pct;
168
169         if (kwboot_verbose)
170                 __progress(pct, c);
171
172         if (pct == 100)
173                 pct = 0;
174 }
175
176 static int
177 kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
178 {
179         int rc, nfds;
180         fd_set rfds;
181         struct timeval tv;
182         ssize_t n;
183
184         rc = -1;
185
186         FD_ZERO(&rfds);
187         FD_SET(fd, &rfds);
188
189         tv.tv_sec = 0;
190         tv.tv_usec = timeo * 1000;
191         if (tv.tv_usec > 1000000) {
192                 tv.tv_sec += tv.tv_usec / 1000000;
193                 tv.tv_usec %= 1000000;
194         }
195
196         do {
197                 nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
198                 if (nfds < 0)
199                         goto out;
200                 if (!nfds) {
201                         errno = ETIMEDOUT;
202                         goto out;
203                 }
204
205                 n = read(fd, buf, len);
206                 if (n <= 0)
207                         goto out;
208
209                 buf = (char *)buf + n;
210                 len -= n;
211         } while (len > 0);
212
213         rc = 0;
214 out:
215         return rc;
216 }
217
218 static int
219 kwboot_tty_send(int fd, const void *buf, size_t len)
220 {
221         if (!buf)
222                 return 0;
223
224         if (kwboot_write(fd, buf, len) < 0)
225                 return -1;
226
227         return tcdrain(fd);
228 }
229
230 static int
231 kwboot_tty_send_char(int fd, unsigned char c)
232 {
233         return kwboot_tty_send(fd, &c, 1);
234 }
235
236 static speed_t
237 kwboot_tty_speed(int baudrate)
238 {
239         switch (baudrate) {
240         case 115200:
241                 return B115200;
242         case 57600:
243                 return B57600;
244         case 38400:
245                 return B38400;
246         case 19200:
247                 return B19200;
248         case 9600:
249                 return B9600;
250         }
251
252         return -1;
253 }
254
255 static int
256 kwboot_open_tty(const char *path, speed_t speed)
257 {
258         int rc, fd;
259         struct termios tio;
260
261         rc = -1;
262
263         fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
264         if (fd < 0)
265                 goto out;
266
267         memset(&tio, 0, sizeof(tio));
268
269         tio.c_iflag = 0;
270         tio.c_cflag = CREAD|CLOCAL|CS8;
271
272         tio.c_cc[VMIN] = 1;
273         tio.c_cc[VTIME] = 10;
274
275         cfsetospeed(&tio, speed);
276         cfsetispeed(&tio, speed);
277
278         rc = tcsetattr(fd, TCSANOW, &tio);
279         if (rc)
280                 goto out;
281
282         rc = fd;
283 out:
284         if (rc < 0) {
285                 if (fd >= 0)
286                         close(fd);
287         }
288
289         return rc;
290 }
291
292 static int
293 kwboot_bootmsg(int tty, void *msg)
294 {
295         int rc;
296         char c;
297         int count;
298
299         if (msg == NULL)
300                 kwboot_printv("Please reboot the target into UART boot mode...");
301         else
302                 kwboot_printv("Sending boot message. Please reboot the target...");
303
304         do {
305                 rc = tcflush(tty, TCIOFLUSH);
306                 if (rc)
307                         break;
308
309                 for (count = 0; count < 128; count++) {
310                         rc = kwboot_tty_send(tty, msg, 8);
311                         if (rc) {
312                                 usleep(msg_req_delay * 1000);
313                                 continue;
314                         }
315                 }
316
317                 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
318
319                 kwboot_spinner();
320
321         } while (rc || c != NAK);
322
323         kwboot_printv("\n");
324
325         return rc;
326 }
327
328 static int
329 kwboot_debugmsg(int tty, void *msg)
330 {
331         int rc;
332
333         kwboot_printv("Sending debug message. Please reboot the target...");
334
335         do {
336                 char buf[16];
337
338                 rc = tcflush(tty, TCIOFLUSH);
339                 if (rc)
340                         break;
341
342                 rc = kwboot_tty_send(tty, msg, 8);
343                 if (rc) {
344                         usleep(msg_req_delay * 1000);
345                         continue;
346                 }
347
348                 rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo);
349
350                 kwboot_spinner();
351
352         } while (rc);
353
354         kwboot_printv("\n");
355
356         return rc;
357 }
358
359 static size_t
360 kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
361                     size_t size, int pnum)
362 {
363         size_t i, n;
364
365         block->soh = SOH;
366         block->pnum = pnum;
367         block->_pnum = ~block->pnum;
368
369         n = size < KWBOOT_XM_BLKSZ ? size : KWBOOT_XM_BLKSZ;
370         memcpy(&block->data[0], data, n);
371         memset(&block->data[n], 0, KWBOOT_XM_BLKSZ - n);
372
373         block->csum = 0;
374         for (i = 0; i < n; i++)
375                 block->csum += block->data[i];
376
377         return n;
378 }
379
380 static uint64_t
381 _now(void)
382 {
383         struct timespec ts;
384
385         if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
386                 static int err_print;
387
388                 if (!err_print) {
389                         perror("clock_gettime() does not work");
390                         err_print = 1;
391                 }
392
393                 /* this will just make the timeout not work */
394                 return -1ULL;
395         }
396
397         return ts.tv_sec * 1000ULL + (ts.tv_nsec + 500000) / 1000000;
398 }
399
400 static int
401 _is_xm_reply(char c)
402 {
403         return c == ACK || c == NAK || c == CAN;
404 }
405
406 static int
407 _xm_reply_to_error(int c)
408 {
409         int rc = -1;
410
411         switch (c) {
412         case ACK:
413                 rc = 0;
414                 break;
415         case NAK:
416                 errno = EBADMSG;
417                 break;
418         case CAN:
419                 errno = ECANCELED;
420                 break;
421         default:
422                 errno = EPROTO;
423                 break;
424         }
425
426         return rc;
427 }
428
429 static int
430 kwboot_xm_recv_reply(int fd, char *c, int allow_non_xm, int *non_xm_print)
431 {
432         int timeout = allow_non_xm ? KWBOOT_HDR_RSP_TIMEO : blk_rsp_timeo;
433         uint64_t recv_until = _now() + timeout;
434         int rc;
435
436         if (non_xm_print)
437                 *non_xm_print = 0;
438
439         while (1) {
440                 rc = kwboot_tty_recv(fd, c, 1, timeout);
441                 if (rc) {
442                         if (errno != ETIMEDOUT)
443                                 return rc;
444                         else if (allow_non_xm && *non_xm_print)
445                                 return -1;
446                         else
447                                 *c = NAK;
448                 }
449
450                 /* If received xmodem reply, end. */
451                 if (_is_xm_reply(*c))
452                         break;
453
454                 /*
455                  * If printing non-xmodem text output is allowed and such a byte
456                  * was received, print it and increase receiving time.
457                  * Otherwise decrease timeout by time elapsed.
458                  */
459                 if (allow_non_xm) {
460                         recv_until = _now() + timeout;
461                         putchar(*c);
462                         fflush(stdout);
463                         *non_xm_print = 1;
464                 } else {
465                         timeout = recv_until - _now();
466                         if (timeout < 0) {
467                                 errno = ETIMEDOUT;
468                                 return -1;
469                         }
470                 }
471         }
472
473         return 0;
474 }
475
476 static int
477 kwboot_xm_sendblock(int fd, struct kwboot_block *block, int allow_non_xm,
478                     int *done_print)
479 {
480         int non_xm_print;
481         int rc, retries;
482         char c;
483
484         *done_print = 0;
485
486         retries = 16;
487         do {
488                 rc = kwboot_tty_send(fd, block, sizeof(*block));
489                 if (rc)
490                         return rc;
491
492                 if (allow_non_xm && !*done_print) {
493                         kwboot_progress(100, '.');
494                         kwboot_printv("Done\n");
495                         *done_print = 1;
496                 }
497
498                 rc = kwboot_xm_recv_reply(fd, &c, allow_non_xm, &non_xm_print);
499                 if (rc)
500                         return rc;
501
502                 if (!allow_non_xm && c != ACK)
503                         kwboot_progress(-1, '+');
504         } while (c == NAK && retries-- > 0);
505
506         if (non_xm_print)
507                 kwboot_printv("\n");
508
509         return _xm_reply_to_error(c);
510 }
511
512 static int
513 kwboot_xm_finish(int fd)
514 {
515         int rc, retries;
516         char c;
517
518         kwboot_printv("Finishing transfer\n");
519
520         retries = 16;
521         do {
522                 rc = kwboot_tty_send_char(fd, EOT);
523                 if (rc)
524                         return rc;
525
526                 rc = kwboot_xm_recv_reply(fd, &c, 0, NULL);
527                 if (rc)
528                         return rc;
529         } while (c == NAK && retries-- > 0);
530
531         return _xm_reply_to_error(c);
532 }
533
534 static int
535 kwboot_xmodem_one(int tty, int *pnum, int header, const uint8_t *data,
536                   size_t size)
537 {
538         int done_print = 0;
539         size_t sent, left;
540         int rc;
541
542         kwboot_printv("Sending boot image %s (%zu bytes)...\n",
543                       header ? "header" : "data", size);
544
545         left = size;
546         sent = 0;
547
548         while (sent < size) {
549                 struct kwboot_block block;
550                 int last_block;
551                 size_t blksz;
552
553                 blksz = kwboot_xm_makeblock(&block, data, left, (*pnum)++);
554                 data += blksz;
555
556                 last_block = (left <= blksz);
557
558                 rc = kwboot_xm_sendblock(tty, &block, header && last_block,
559                                          &done_print);
560                 if (rc)
561                         goto out;
562
563                 sent += blksz;
564                 left -= blksz;
565
566                 if (!done_print)
567                         kwboot_progress(sent * 100 / size, '.');
568         }
569
570         if (!done_print)
571                 kwboot_printv("Done\n");
572
573         return 0;
574 out:
575         kwboot_printv("\n");
576         return rc;
577 }
578
579 static int
580 kwboot_xmodem(int tty, const void *_img, size_t size)
581 {
582         const uint8_t *img = _img;
583         int rc, pnum;
584         size_t hdrsz;
585
586         if (image_version(img) == 0)
587                 hdrsz = KWBHEADER_V0_SIZE((struct main_hdr_v0 *)img);
588         else
589                 hdrsz = KWBHEADER_V1_SIZE((struct main_hdr_v1 *)img);
590
591         kwboot_printv("Waiting 2s and flushing tty\n");
592         sleep(2); /* flush isn't effective without it */
593         tcflush(tty, TCIOFLUSH);
594
595         pnum = 1;
596
597         rc = kwboot_xmodem_one(tty, &pnum, 1, img, hdrsz);
598         if (rc)
599                 return rc;
600
601         img += hdrsz;
602         size -= hdrsz;
603
604         rc = kwboot_xmodem_one(tty, &pnum, 0, img, size);
605         if (rc)
606                 return rc;
607
608         return kwboot_xm_finish(tty);
609 }
610
611 static int
612 kwboot_term_pipe(int in, int out, const char *quit, int *s)
613 {
614         ssize_t nin;
615         char _buf[128], *buf = _buf;
616
617         nin = read(in, buf, sizeof(_buf));
618         if (nin <= 0)
619                 return -1;
620
621         if (quit) {
622                 int i;
623
624                 for (i = 0; i < nin; i++) {
625                         if (*buf == quit[*s]) {
626                                 (*s)++;
627                                 if (!quit[*s])
628                                         return 0;
629                                 buf++;
630                                 nin--;
631                         } else {
632                                 if (kwboot_write(out, quit, *s) < 0)
633                                         return -1;
634                                 *s = 0;
635                         }
636                 }
637         }
638
639         if (kwboot_write(out, buf, nin) < 0)
640                 return -1;
641
642         return 0;
643 }
644
645 static int
646 kwboot_terminal(int tty)
647 {
648         int rc, in, s;
649         const char *quit = "\34c";
650         struct termios otio, tio;
651
652         rc = -1;
653
654         in = STDIN_FILENO;
655         if (isatty(in)) {
656                 rc = tcgetattr(in, &otio);
657                 if (!rc) {
658                         tio = otio;
659                         cfmakeraw(&tio);
660                         rc = tcsetattr(in, TCSANOW, &tio);
661                 }
662                 if (rc) {
663                         perror("tcsetattr");
664                         goto out;
665                 }
666
667                 kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
668                               quit[0]|0100, quit[1]);
669         } else
670                 in = -1;
671
672         rc = 0;
673         s = 0;
674
675         do {
676                 fd_set rfds;
677                 int nfds = 0;
678
679                 FD_SET(tty, &rfds);
680                 nfds = nfds < tty ? tty : nfds;
681
682                 if (in >= 0) {
683                         FD_SET(in, &rfds);
684                         nfds = nfds < in ? in : nfds;
685                 }
686
687                 nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
688                 if (nfds < 0)
689                         break;
690
691                 if (FD_ISSET(tty, &rfds)) {
692                         rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
693                         if (rc)
694                                 break;
695                 }
696
697                 if (in >= 0 && FD_ISSET(in, &rfds)) {
698                         rc = kwboot_term_pipe(in, tty, quit, &s);
699                         if (rc)
700                                 break;
701                 }
702         } while (quit[s] != 0);
703
704         if (in >= 0)
705                 tcsetattr(in, TCSANOW, &otio);
706         printf("\n");
707 out:
708         return rc;
709 }
710
711 static void *
712 kwboot_mmap_image(const char *path, size_t *size, int prot)
713 {
714         int rc, fd, flags;
715         struct stat st;
716         void *img;
717
718         rc = -1;
719         img = NULL;
720
721         fd = open(path, O_RDONLY);
722         if (fd < 0)
723                 goto out;
724
725         rc = fstat(fd, &st);
726         if (rc)
727                 goto out;
728
729         flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
730
731         img = mmap(NULL, st.st_size, prot, flags, fd, 0);
732         if (img == MAP_FAILED) {
733                 img = NULL;
734                 goto out;
735         }
736
737         rc = 0;
738         *size = st.st_size;
739 out:
740         if (rc && img) {
741                 munmap(img, st.st_size);
742                 img = NULL;
743         }
744         if (fd >= 0)
745                 close(fd);
746
747         return img;
748 }
749
750 static uint8_t
751 kwboot_img_csum8(void *_data, size_t size)
752 {
753         uint8_t *data = _data, csum;
754
755         for (csum = 0; size-- > 0; data++)
756                 csum += *data;
757
758         return csum;
759 }
760
761 static int
762 kwboot_img_patch_hdr(void *img, size_t size)
763 {
764         int rc;
765         struct main_hdr_v1 *hdr;
766         uint8_t csum;
767         size_t hdrsz = sizeof(*hdr);
768         int image_ver;
769
770         rc = -1;
771         hdr = img;
772
773         if (size < hdrsz) {
774                 errno = EINVAL;
775                 goto out;
776         }
777
778         image_ver = image_version(img);
779         if (image_ver != 0 && image_ver != 1) {
780                 fprintf(stderr, "Invalid image header version\n");
781                 errno = EINVAL;
782                 goto out;
783         }
784
785         if (image_ver == 0)
786                 hdrsz = sizeof(*hdr);
787         else
788                 hdrsz = KWBHEADER_V1_SIZE(hdr);
789
790         if (size < hdrsz) {
791                 errno = EINVAL;
792                 goto out;
793         }
794
795         csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum;
796         if (csum != hdr->checksum) {
797                 errno = EINVAL;
798                 goto out;
799         }
800
801         if (hdr->blockid == IBR_HDR_UART_ID) {
802                 rc = 0;
803                 goto out;
804         }
805
806         hdr->blockid = IBR_HDR_UART_ID;
807
808         if (image_ver == 0) {
809                 struct main_hdr_v0 *hdr_v0 = img;
810
811                 hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED;
812                 hdr_v0->nandpagesize = 0;
813
814                 hdr_v0->srcaddr = hdr_v0->ext
815                         ? sizeof(struct kwb_header)
816                         : sizeof(*hdr_v0);
817         }
818
819         hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum;
820
821         rc = 0;
822 out:
823         return rc;
824 }
825
826 static void
827 kwboot_usage(FILE *stream, char *progname)
828 {
829         fprintf(stream, "kwboot version %s\n", PLAIN_VERSION);
830         fprintf(stream,
831                 "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n",
832                 progname);
833         fprintf(stream, "\n");
834         fprintf(stream,
835                 "  -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n");
836         fprintf(stream, "  -p: patch <image> to type 0x69 (uart boot)\n");
837         fprintf(stream,
838                 "  -D <image>: boot <image> without preamble (Dove)\n");
839         fprintf(stream, "  -d: enter debug mode\n");
840         fprintf(stream, "  -a: use timings for Armada XP\n");
841         fprintf(stream, "  -q <req-delay>:  use specific request-delay\n");
842         fprintf(stream, "  -s <resp-timeo>: use specific response-timeout\n");
843         fprintf(stream,
844                 "  -o <block-timeo>: use specific xmodem block timeout\n");
845         fprintf(stream, "\n");
846         fprintf(stream, "  -t: mini terminal\n");
847         fprintf(stream, "\n");
848         fprintf(stream, "  -B <baud>: set baud rate\n");
849         fprintf(stream, "\n");
850 }
851
852 int
853 main(int argc, char **argv)
854 {
855         const char *ttypath, *imgpath;
856         int rv, rc, tty, term, prot, patch;
857         void *bootmsg;
858         void *debugmsg;
859         void *img;
860         size_t size;
861         speed_t speed;
862
863         rv = 1;
864         tty = -1;
865         bootmsg = NULL;
866         debugmsg = NULL;
867         imgpath = NULL;
868         img = NULL;
869         term = 0;
870         patch = 0;
871         size = 0;
872         speed = B115200;
873
874         kwboot_verbose = isatty(STDOUT_FILENO);
875
876         do {
877                 int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:");
878                 if (c < 0)
879                         break;
880
881                 switch (c) {
882                 case 'b':
883                         bootmsg = kwboot_msg_boot;
884                         imgpath = optarg;
885                         break;
886
887                 case 'D':
888                         bootmsg = NULL;
889                         imgpath = optarg;
890                         break;
891
892                 case 'd':
893                         debugmsg = kwboot_msg_debug;
894                         break;
895
896                 case 'p':
897                         patch = 1;
898                         break;
899
900                 case 't':
901                         term = 1;
902                         break;
903
904                 case 'a':
905                         msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP;
906                         msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP;
907                         break;
908
909                 case 'q':
910                         msg_req_delay = atoi(optarg);
911                         break;
912
913                 case 's':
914                         msg_rsp_timeo = atoi(optarg);
915                         break;
916
917                 case 'o':
918                         blk_rsp_timeo = atoi(optarg);
919                         break;
920
921                 case 'B':
922                         speed = kwboot_tty_speed(atoi(optarg));
923                         if (speed == -1)
924                                 goto usage;
925                         break;
926
927                 case 'h':
928                         rv = 0;
929                 default:
930                         goto usage;
931                 }
932         } while (1);
933
934         if (!bootmsg && !term && !debugmsg)
935                 goto usage;
936
937         if (patch && !imgpath)
938                 goto usage;
939
940         if (argc - optind < 1)
941                 goto usage;
942
943         ttypath = argv[optind++];
944
945         tty = kwboot_open_tty(ttypath, speed);
946         if (tty < 0) {
947                 perror(ttypath);
948                 goto out;
949         }
950
951         if (imgpath) {
952                 prot = PROT_READ | (patch ? PROT_WRITE : 0);
953
954                 img = kwboot_mmap_image(imgpath, &size, prot);
955                 if (!img) {
956                         perror(imgpath);
957                         goto out;
958                 }
959         }
960
961         if (patch) {
962                 rc = kwboot_img_patch_hdr(img, size);
963                 if (rc) {
964                         fprintf(stderr, "%s: Invalid image.\n", imgpath);
965                         goto out;
966                 }
967         }
968
969         if (debugmsg) {
970                 rc = kwboot_debugmsg(tty, debugmsg);
971                 if (rc) {
972                         perror("debugmsg");
973                         goto out;
974                 }
975         } else if (bootmsg) {
976                 rc = kwboot_bootmsg(tty, bootmsg);
977                 if (rc) {
978                         perror("bootmsg");
979                         goto out;
980                 }
981         }
982
983         if (img) {
984                 rc = kwboot_xmodem(tty, img, size);
985                 if (rc) {
986                         perror("xmodem");
987                         goto out;
988                 }
989         }
990
991         if (term) {
992                 rc = kwboot_terminal(tty);
993                 if (rc && !(errno == EINTR)) {
994                         perror("terminal");
995                         goto out;
996                 }
997         }
998
999         rv = 0;
1000 out:
1001         if (tty >= 0)
1002                 close(tty);
1003
1004         if (img)
1005                 munmap(img, size);
1006
1007         return rv;
1008
1009 usage:
1010         kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
1011         goto out;
1012 }