karo: bootce: suppress mtdparts related error message when CMD_MTDPARTS is not set
[karo-tx-uboot.git] / common / cmd_bootce.c
1 /*
2  * Copyright (C) 2012 Lothar WaƟmann <LW@KARO-electronics.de>
3  * based on: code from RedBoot (C) Uwe Steinkohl <US@KARO-electronics.de>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <command.h>
26 #include <net.h>
27 #include <wince.h>
28 #include <nand.h>
29 #include <malloc.h>
30 #include <asm/errno.h>
31 #include <jffs2/load_kernel.h>
32
33 DECLARE_GLOBAL_DATA_PTR;
34
35 #define WINCE_VRAM_BASE         0x80000000
36 #define CE_FIX_ADDRESS(a)       ((void *)((a) - WINCE_VRAM_BASE + CONFIG_SYS_SDRAM_BASE))
37
38 #ifndef INT_MAX
39 #define INT_MAX                 ((int)(~0U >> 1))
40 #endif
41
42 /* Bin image parse states */
43 #define CE_PS_RTI_ADDR          0
44 #define CE_PS_RTI_LEN           1
45 #define CE_PS_E_ADDR            2
46 #define CE_PS_E_LEN             3
47 #define CE_PS_E_CHKSUM          4
48 #define CE_PS_E_DATA            5
49
50 #define CE_MIN(a, b)            (((a) < (b)) ? (a) : (b))
51 #define CE_MAX(a, b)            (((a) > (b)) ? (a) : (b))
52
53 static ce_bin __attribute__ ((aligned (32))) g_bin;
54 static ce_net __attribute__ ((aligned (32))) g_net;
55 static struct in_addr server_ip;
56
57 static void ce_init_bin(ce_bin *bin, unsigned char *dataBuffer)
58 {
59         memset(bin, 0, sizeof(*bin));
60
61         bin->data = dataBuffer;
62         bin->parseState = CE_PS_RTI_ADDR;
63         bin->parsePtr = (unsigned char *)bin;
64 }
65
66 static int ce_is_bin_image(void *image, int imglen)
67 {
68         if (imglen < CE_BIN_SIGN_LEN) {
69                 return 0;
70         }
71
72         return memcmp(image, CE_BIN_SIGN, CE_BIN_SIGN_LEN) == 0;
73 }
74
75 static const struct ce_magic {
76         char magic[8];
77         size_t size;
78         ce_std_driver_globals drv_glb;
79 } ce_magic_template = {
80         .magic = "KARO_CE6",
81         .size = sizeof(ce_std_driver_globals),
82         .drv_glb = {
83                 .header = {
84                         .signature = STD_DRV_GLB_SIGNATURE,
85                         .oalVersion = 1,
86                         .bspVersion = 2,
87                 },
88         },
89 };
90
91 #ifdef DEBUG
92 static void __attribute__((unused)) ce_dump_block(void *ptr, int length)
93 {
94         char *p = ptr;
95         int i;
96         int j;
97
98         for (i = 0; i < length; i++) {
99                 if (!(i % 16)) {
100                         printf("\n%p: ", ptr + i);
101                 }
102
103                 printf("%02x ", p[i]);
104                 if (!((i + 1) % 16)){
105                         printf("      ");
106                         for (j = i - 15; j <= i; j++){
107                                 if((p[j] > 0x1f) && (p[j] < 0x7f)) {
108                                         printf("%c", p[j]);
109                                 } else {
110                                         printf(".");
111                                 }
112                         }
113                 }
114         }
115         printf("\n");
116 }
117 #else
118 static inline void ce_dump_block(void *ptr, int length)
119 {
120 }
121 #endif
122
123 static void ce_setup_std_drv_globals(ce_std_driver_globals *std_drv_glb)
124 {
125         char *mtdparts = getenv("mtdparts");
126         size_t max_len = ALIGN((unsigned long)std_drv_glb, SZ_4K) -
127                 (unsigned long)&std_drv_glb->mtdparts;
128
129         if (eth_get_dev()) {
130                 memcpy(&std_drv_glb->kitl.mac, eth_get_dev()->enetaddr,
131                         sizeof(std_drv_glb->kitl.mac));
132         }
133         snprintf(std_drv_glb->deviceId, sizeof(std_drv_glb->deviceId),
134                 "Triton%02X", eth_get_dev()->enetaddr[5]);
135
136         net_copy_ip(&std_drv_glb->kitl.ipAddress, &net_ip);
137         std_drv_glb->kitl.ipMask = getenv_ip("netmask").s_addr;
138         std_drv_glb->kitl.ipRoute = getenv_ip("gatewayip").s_addr;
139
140         if (mtdparts) {
141                 strncpy(std_drv_glb->mtdparts, mtdparts, max_len);
142                 std_drv_glb->mtdparts[max_len - 1] = '\0';
143         } else {
144                 if (CONFIG_IS_ENABLED(CMD_MTDPARTS))
145                         printf("Failed to get mtdparts environment variable\n");
146         }
147 }
148
149 static void ce_init_drv_globals(void)
150 {
151         struct ce_magic *ce_magic = (void *)CONFIG_SYS_SDRAM_BASE + 0x160;
152         ce_std_driver_globals *std_drv_glb = &ce_magic->drv_glb;
153
154         debug("Copying CE MAGIC from %p to %p..%p\n",
155                 &ce_magic_template, ce_magic,
156                 (void *)ce_magic + sizeof(*ce_magic) - 1);
157         memcpy(ce_magic, &ce_magic_template, sizeof(*ce_magic));
158
159         ce_setup_std_drv_globals(std_drv_glb);
160         ce_magic->size = sizeof(*std_drv_glb) +
161                 strlen(std_drv_glb->mtdparts) + 1;
162         ce_dump_block(ce_magic, offsetof(struct ce_magic, drv_glb) +
163                 ce_magic->size);
164 }
165
166 static void ce_prepare_run_bin(ce_bin *bin)
167 {
168         /* Clear os RAM area (if needed) */
169         if (bin->edbgConfig.flags & EDBG_FL_CLEANBOOT) {
170                 debug("cleaning memory from %p to %p\n",
171                         bin->eRamStart, bin->eRamStart + bin->eRamLen);
172
173                 printf("Preparing clean boot ... ");
174                 memset(bin->eRamStart, 0, bin->eRamLen);
175                 printf("ok\n");
176         }
177
178         ce_init_drv_globals();
179
180         /*
181          * Make sure, all the above makes it into SDRAM because
182          * WinCE switches the cache & MMU off, obviously without
183          * flushing it first!
184          */
185         flush_dcache_all();
186 }
187
188 static int ce_lookup_ep_bin(ce_bin *bin)
189 {
190         ce_rom_hdr *header;
191         ce_toc_entry *tentry;
192         e32_rom *e32;
193         unsigned int i;
194         uint32_t *sig = (uint32_t *)(bin->rtiPhysAddr + ROM_SIGNATURE_OFFSET);
195
196         debug("Looking for TOC signature at %p\n", sig);
197
198         /* Check image Table Of Contents (TOC) signature */
199         if (*sig != ROM_SIGNATURE) {
200                 printf("Error: Did not find image TOC signature!\n");
201                 printf("Expected %08x at address %p; found %08x instead\n",
202                         ROM_SIGNATURE, sig, *sig);
203                 return 0;
204         }
205
206         /* Lookup entry point */
207         header = CE_FIX_ADDRESS(*(unsigned int *)(bin->rtiPhysAddr +
208                                                 ROM_SIGNATURE_OFFSET +
209                                                 sizeof(unsigned int)));
210         tentry = (ce_toc_entry *)(header + 1);
211
212         for (i = 0; i < header->nummods; i++) {
213                 // Look for 'nk.exe' module
214                 if (strcmp(CE_FIX_ADDRESS(tentry[i].fileName), "nk.exe") == 0) {
215                         // Save entry point and RAM addresses
216
217                         e32 = CE_FIX_ADDRESS(tentry[i].e32Offset);
218
219                         bin->eEntryPoint = CE_FIX_ADDRESS(tentry[i].loadOffset) +
220                                 e32->e32_entryrva;
221                         bin->eRamStart = CE_FIX_ADDRESS(header->ramStart);
222                         bin->eRamLen = header->ramEnd - header->ramStart;
223                         return 1;
224                 }
225         }
226
227         // Error: Did not find 'nk.exe' module
228         return 0;
229 }
230
231 static int ce_parse_bin(ce_bin *bin)
232 {
233         unsigned char *pbData = bin->data;
234         int len = bin->dataLen;
235         int copyLen;
236
237         debug("starting ce image parsing:\n\tbin->binLen: 0x%08X\n", bin->binLen);
238
239         if (len) {
240                 if (bin->binLen == 0) {
241                         // Check for the .BIN signature first
242                         if (!ce_is_bin_image(pbData, len)) {
243                                 printf("Error: Invalid or corrupted .BIN image!\n");
244                                 return CE_PR_ERROR;
245                         }
246
247                         printf("Loading Windows CE .BIN image ...\n");
248                         // Skip signature
249                         len -= CE_BIN_SIGN_LEN;
250                         pbData += CE_BIN_SIGN_LEN;
251                 }
252
253                 while (len) {
254                         switch (bin->parseState) {
255                         case CE_PS_RTI_ADDR:
256                         case CE_PS_RTI_LEN:
257                         case CE_PS_E_ADDR:
258                         case CE_PS_E_LEN:
259                         case CE_PS_E_CHKSUM:
260                                 copyLen = CE_MIN(sizeof(unsigned int) - bin->parseLen, len);
261                                 memcpy(&bin->parsePtr[bin->parseLen], pbData, copyLen);
262
263                                 bin->parseLen += copyLen;
264                                 len -= copyLen;
265                                 pbData += copyLen;
266
267                                 if (bin->parseLen == sizeof(unsigned int)) {
268                                         if (bin->parseState == CE_PS_RTI_ADDR)
269                                                 bin->rtiPhysAddr = CE_FIX_ADDRESS(bin->rtiPhysAddr);
270                                         else if (bin->parseState == CE_PS_E_ADDR &&
271                                                 bin->ePhysAddr)
272                                                 bin->ePhysAddr = CE_FIX_ADDRESS(bin->ePhysAddr);
273
274                                         bin->parseState++;
275                                         bin->parseLen = 0;
276                                         bin->parsePtr += sizeof(unsigned int);
277
278                                         if (bin->parseState == CE_PS_E_DATA) {
279                                                 if (bin->ePhysAddr) {
280                                                         bin->parsePtr = bin->ePhysAddr;
281                                                         bin->parseChkSum = 0;
282                                                 } else {
283                                                         /* EOF */
284                                                         len = 0;
285                                                         bin->endOfBin = 1;
286                                                 }
287                                         }
288                                 }
289                                 break;
290
291                         case CE_PS_E_DATA:
292                                 debug("ePhysAddr=%p physlen=%08x parselen=%08x\n",
293                                         bin->ePhysAddr, bin->ePhysLen, bin->parseLen);
294                                 if (bin->ePhysAddr) {
295                                         copyLen = CE_MIN(bin->ePhysLen - bin->parseLen, len);
296                                         bin->parseLen += copyLen;
297                                         len -= copyLen;
298
299                                         while (copyLen--) {
300                                                 bin->parseChkSum += *pbData;
301                                                 *bin->parsePtr++ = *pbData++;
302                                         }
303
304                                         if (bin->parseLen == bin->ePhysLen) {
305                                                 printf("Section [%02d]: address %p, size 0x%08X, checksum %s\n",
306                                                         bin->section,
307                                                         bin->ePhysAddr,
308                                                         bin->ePhysLen,
309                                                         (bin->eChkSum == bin->parseChkSum) ? "ok" : "fail");
310
311                                                 if (bin->eChkSum != bin->parseChkSum) {
312                                                         printf("Error: Checksum error, corrupted .BIN file!\n");
313                                                         printf("checksum calculated: 0x%08x from file: 0x%08x\n",
314                                                                 bin->parseChkSum, bin->eChkSum);
315                                                         bin->binLen = 0;
316                                                         return CE_PR_ERROR;
317                                                 }
318
319                                                 bin->section++;
320                                                 bin->parseState = CE_PS_E_ADDR;
321                                                 bin->parseLen = 0;
322                                                 bin->parsePtr = (unsigned char *)&bin->ePhysAddr;
323                                         }
324                                 } else {
325                                         bin->parseLen = 0;
326                                         bin->endOfBin = 1;
327                                         len = 0;
328                                 }
329                                 break;
330                         }
331                 }
332         }
333
334         if (bin->endOfBin) {
335                 if (!ce_lookup_ep_bin(bin)) {
336                         printf("Error: entry point not found!\n");
337                         bin->binLen = 0;
338                         return CE_PR_ERROR;
339                 }
340
341                 printf("Entry point: %p, address range: %p-%p\n",
342                         bin->eEntryPoint,
343                         bin->rtiPhysAddr,
344                         bin->rtiPhysAddr + bin->rtiPhysLen);
345
346                 return CE_PR_EOF;
347         }
348
349         /* Need more data */
350         bin->binLen += bin->dataLen;
351         return CE_PR_MORE;
352 }
353
354 static int ce_bin_load(void *image, int imglen)
355 {
356         ce_init_bin(&g_bin, image);
357         g_bin.dataLen = imglen;
358         if (ce_parse_bin(&g_bin) == CE_PR_EOF) {
359                 ce_prepare_run_bin(&g_bin);
360                 return 1;
361         }
362
363         return 0;
364 }
365
366 static void ce_run_bin(void (*entry)(void))
367 {
368         printf("Launching Windows CE ...\n");
369 #ifdef TEST_LAUNCH
370 return;
371 #endif
372         entry();
373 }
374
375 static int do_bootce(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
376 {
377         void *addr;
378         size_t image_size;
379
380         if (argc > 1) {
381                 if (strcmp(argv[1], "-i") == 0) {
382                         ce_init_drv_globals();
383                         return CMD_RET_SUCCESS;
384                 }
385                 addr = (void *)simple_strtoul(argv[1], NULL, 16);
386                 image_size = INT_MAX;           /* actually we do not know the image size */
387         } else if (getenv("fileaddr") != NULL) {
388                 addr = (void *)getenv_ulong("fileaddr", 16, 0);
389                 image_size = getenv_ulong("filesize", 16, INT_MAX);
390         } else {
391                 return CMD_RET_USAGE;
392         }
393
394         printf ("## Booting Windows CE Image from address %p ...\n", addr);
395
396         /* check if there is a valid windows CE image */
397         if (ce_is_bin_image(addr, image_size)) {
398                 if (!ce_bin_load(addr, image_size)) {
399                         /* Ops! Corrupted .BIN image! */
400                         /* Handle error here ...      */
401                         printf("corrupted .BIN image !!!\n");
402                         return CMD_RET_FAILURE;
403                 }
404                 if (getenv_yesno("autostart") != 1) {
405                         /*
406                          * just use bootce to load the image to SDRAM;
407                          * Do not start it automatically.
408                          */
409                         setenv_addr("fileaddr", g_bin.eEntryPoint);
410                         return CMD_RET_SUCCESS;
411                 }
412                 ce_run_bin(g_bin.eEntryPoint);          /* start the image */
413         } else {
414                 printf("Image does not seem to be a valid Windows CE image!\n");
415                 return CMD_RET_FAILURE;
416         }
417         return CMD_RET_FAILURE; /* never reached - just to keep compiler happy */
418 }
419 U_BOOT_CMD(
420         bootce, 2, 0, do_bootce,
421         "Boot a Windows CE image from RAM",
422         "[addr]\n"
423         "\taddr\t\tboot image from address addr (default ${fileaddr})\n"
424         "or\n"
425         "\t-i\t\tinitialize the WinCE globals data structure (before loading a .nb0 image)"
426 );
427
428 #ifdef CONFIG_CMD_NAND
429 static int ce_nand_load(ce_bin *bin, loff_t *offset, void *buf, size_t max_len)
430 {
431         int ret;
432         size_t len = max_len;
433         nand_info_t *nand = &nand_info[0];
434
435         while (nand_block_isbad(nand, *offset & ~(max_len - 1))) {
436                 printf("Skipping bad block 0x%08llx\n",
437                         *offset & ~(max_len - 1));
438                 *offset += max_len;
439                 if (*offset + max_len > nand->size)
440                         return -EINVAL;
441         }
442
443         ret = nand_read(nand, *offset, &len, buf);
444         if (ret < 0)
445                 return ret;
446
447         bin->dataLen = len;
448         return len;
449 }
450
451 static int do_nbootce(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
452 {
453         int ret;
454         struct mtd_device *dev;
455         struct part_info *part_info;
456         u8 part_num;
457         loff_t offset;
458         char *end;
459         void *buffer;
460         size_t bufsize = nand_info[0].erasesize, len;
461
462         if (argc < 2 || argc > 3)
463                 return CMD_RET_USAGE;
464
465         ret = mtdparts_init();
466         if (ret)
467                 return CMD_RET_FAILURE;
468
469         offset = simple_strtoul(argv[1], &end, 16);
470         if (*end != '\0') {
471                 ret = find_dev_and_part(argv[1], &dev, &part_num,
472                                         &part_info);
473                 if (ret != 0) {
474                         printf("Partition '%s' not found\n", argv[1]);
475                         return CMD_RET_FAILURE;
476                 }
477                 offset = part_info->offset;
478                 printf ("## Booting Windows CE Image from NAND partition %s at offset %08llx\n",
479                         argv[1], offset);
480         } else {
481                 printf ("## Booting Windows CE Image from NAND offset %08llx\n",
482                         offset);
483         }
484
485         buffer = malloc(bufsize);
486         if (buffer == NULL) {
487                 printf("Failed to allocate %u byte buffer\n", bufsize);
488                 return CMD_RET_FAILURE;
489         }
490
491         ce_init_bin(&g_bin, buffer);
492
493         ret = ce_nand_load(&g_bin, &offset, buffer, bufsize);
494         if (ret < 0) {
495                 printf("Failed to read NAND: %d\n", ret);
496                 goto err;
497         }
498         len = ret;
499         /* check if there is a valid windows CE image header */
500         if (ce_is_bin_image(buffer, len)) {
501                 do {
502                         ret = ce_parse_bin(&g_bin);
503                         switch (ret) {
504                         case CE_PR_MORE:
505                         {
506                                 if (ctrlc()) {
507                                         printf("NBOOTCE - canceled by user\n");
508                                         goto err;
509                                 }
510                                 offset += len;
511                                 len = ce_nand_load(&g_bin, &offset, buffer,
512                                                 bufsize);
513                                 if (len < 0) {
514                                         printf("Nand read error: %d\n", len);
515                                         ret = len;
516                                         goto err;
517                                 }
518                         }
519                         break;
520
521                         case CE_PR_EOF:
522                                 ce_prepare_run_bin(&g_bin);
523                                 break;
524
525                         case CE_PR_ERROR:
526                                 break;
527                         }
528                 } while (ret == CE_PR_MORE);
529                 free(buffer);
530                 if (ret != CE_PR_EOF)
531                         return CMD_RET_FAILURE;
532
533                 if (getenv_yesno("autostart") != 1) {
534                         /*
535                          * just use bootce to load the image to SDRAM;
536                          * Do not start it automatically.
537                          */
538                         setenv_addr("fileaddr", g_bin.eEntryPoint);
539                         return CMD_RET_SUCCESS;
540                 }
541                 ce_run_bin(g_bin.eEntryPoint);          /* start the image */
542         } else {
543                 printf("Image does not seem to be a valid Windows CE image!\n");
544         }
545 err:
546         free(buffer);
547         return CMD_RET_FAILURE;
548 }
549 U_BOOT_CMD(
550         nbootce, 2, 0, do_nbootce,
551         "Boot a Windows CE image from NAND",
552         "off|partitition\n"
553         "\toff\t\t- flash offset (hex)\n"
554         "\tpartition\t- partition name"
555 );
556 #endif
557
558 static int ce_send_write_ack(ce_net *net)
559 {
560         int ret;
561         unsigned short wdata[2];
562         int retries = 0;
563
564         wdata[0] = htons(EDBG_CMD_WRITE_ACK);
565         wdata[1] = htons(net->blockNum);
566         net->dataLen = sizeof(wdata);
567         memcpy(net->data, wdata, net->dataLen);
568
569         do {
570                 ret = bootme_send_frame(net->data, net->dataLen);
571                 if (ret) {
572                         printf("Failed to send write ack %d; retries=%d\n",
573                                 ret, retries);
574                 }
575         } while (ret != 0 && retries-- > 0);
576         return ret;
577 }
578
579 static enum bootme_state ce_process_download(ce_net *net, ce_bin *bin)
580 {
581         int ret = net->state;
582
583         if (net->dataLen >= 4) {
584                 unsigned short command;
585                 unsigned short blknum;
586
587                 memcpy(&command, net->data, sizeof(command));
588                 command = ntohs(command);
589                 debug("command found: 0x%04X\n", command);
590
591                 if (net->state == BOOTME_DOWNLOAD) {
592                         unsigned short nxt = net->blockNum + 1;
593
594                         memcpy(&blknum, &net->data[2], sizeof(blknum));
595                         blknum = ntohs(blknum);
596                         if (blknum == nxt) {
597                                 net->blockNum = blknum;
598                         } else {
599                                 int rc = ce_send_write_ack(net);
600
601                                 if (net->verbose)
602                                         printf("Dropping out of sequence packet with ID %d (expected %d)\n",
603                                                 blknum, nxt);
604                                 if (rc != 0)
605                                         return rc;
606
607                                 return ret;
608                         }
609                 }
610
611                 switch (command) {
612                 case EDBG_CMD_WRITE_REQ:
613                         if (net->state == BOOTME_INIT) {
614                                 // Check file name for WRITE request
615                                 // CE EShell uses "boot.bin" file name
616                                 if (strncmp((char *)&net->data[2],
617                                                 "boot.bin", 8) == 0) {
618                                         // Some diag output
619                                         if (net->verbose) {
620                                                 printf("Locked Down download link, IP: %pI4\n",
621                                                         &net_server_ip);
622                                                 printf("Sending BOOTME request [%d] to %pI4\n",
623                                                         net->seqNum, &net_server_ip);
624                                         }
625
626                                         // Lock down EShell download link
627                                         ret = BOOTME_DOWNLOAD;
628                                 } else {
629                                         // Unknown link
630                                         printf("Unknown link\n");
631                                 }
632
633                                 if (ret == BOOTME_DOWNLOAD) {
634                                         int rc = ce_send_write_ack(net);
635                                         if (rc != 0)
636                                                 return rc;
637                                 }
638                         }
639                         break;
640
641                 case EDBG_CMD_WRITE:
642                         /* Fixup data len */
643                         bin->data = &net->data[4];
644                         bin->dataLen = net->dataLen - 4;
645                         ret = ce_parse_bin(bin);
646                         if (ret != CE_PR_ERROR) {
647                                 int rc = ce_send_write_ack(net);
648                                 if (rc)
649                                         return rc;
650                                 if (ret == CE_PR_EOF)
651                                         ret = BOOTME_DONE;
652                         } else {
653                                 ret = BOOTME_ERROR;
654                         }
655                         break;
656
657                 case EDBG_CMD_READ_REQ:
658                         printf("Ignoring EDBG_CMD_READ_REQ\n");
659                         /* Read requests are not supported
660                          * Do nothing ...
661                          */
662                         break;
663
664                 case EDBG_CMD_ERROR:
665                         printf("Error: unknown error on the host side\n");
666
667                         bin->binLen = 0;
668                         ret = BOOTME_ERROR;
669                         break;
670
671                 default:
672                         printf("unknown command 0x%04X\n", command);
673                         net->state = BOOTME_ERROR;
674                 }
675         }
676         return ret;
677 }
678
679 static enum bootme_state ce_process_edbg(ce_net *net, ce_bin *bin)
680 {
681         enum bootme_state ret = net->state;
682         eth_dbg_hdr header;
683
684         if (net->dataLen < sizeof(header)) {
685                 /* Bad packet */
686                 printf("Invalid packet size %u\n", net->dataLen);
687                 net->dataLen = 0;
688                 return ret;
689         }
690         memcpy(&header, net->data, sizeof(header));
691         if (header.id != EDBG_ID) {
692                 /* Bad packet */
693                 printf("Bad EDBG ID %08x\n", header.id);
694                 net->dataLen = 0;
695                 return ret;
696         }
697
698         if (header.service != EDBG_SVC_ADMIN) {
699                 /* Unknown service */
700                 printf("Bad EDBG service %02x\n", header.service);
701                 net->dataLen = 0;
702                 return ret;
703         }
704
705         if (net->state == BOOTME_INIT) {
706                 /* Some diag output */
707                 if (net->verbose) {
708                         printf("Locked Down EDBG service link, IP: %pI4\n",
709                                 &net_server_ip);
710                 }
711
712                 /* Lock down EDBG link */
713                 net->state = BOOTME_DEBUG;
714         }
715
716         switch (header.cmd) {
717         case EDBG_CMD_JUMPIMG:
718                 net->gotJumpingRequest = 1;
719
720                 if (net->verbose) {
721                         printf("Received JUMPING command\n");
722                 }
723                 /* Just pass through and copy CONFIG structure */
724                 ret = BOOTME_DONE;
725         case EDBG_CMD_OS_CONFIG:
726                 /* Copy config structure */
727                 memcpy(&bin->edbgConfig, &net->data[sizeof(header)],
728                         sizeof(edbg_os_config_data));
729                 if (net->verbose) {
730                         printf("Received CONFIG command\n");
731                         if (bin->edbgConfig.flags & EDBG_FL_DBGMSG) {
732                                 printf("--> Enabling DBGMSG service, IP: %pI4, port: %d\n",
733                                         &bin->edbgConfig.dbgMsgIPAddr,
734                                         ntohs(bin->edbgConfig.dbgMsgPort));
735                         }
736
737                         if (bin->edbgConfig.flags & EDBG_FL_PPSH) {
738                                 printf("--> Enabling PPSH service, IP: %pI4, port: %d\n",
739                                         &bin->edbgConfig.ppshIPAddr,
740                                         ntohs(bin->edbgConfig.ppshPort));
741                         }
742
743                         if (bin->edbgConfig.flags & EDBG_FL_KDBG) {
744                                 printf("--> Enabling KDBG service, IP: %pI4, port: %d\n",
745                                         &bin->edbgConfig.kdbgIPAddr,
746                                         ntohs(bin->edbgConfig.kdbgPort));
747                         }
748
749                         if (bin->edbgConfig.flags & EDBG_FL_CLEANBOOT) {
750                                 printf("--> Force clean boot\n");
751                         }
752                 }
753                 break;
754
755         default:
756                 if (net->verbose) {
757                         printf("Received unknown command: %08X\n", header.cmd);
758                 }
759                 return BOOTME_ERROR;
760         }
761
762         /* Respond with ack */
763         header.flags = EDBG_FL_FROM_DEV | EDBG_FL_ACK;
764         memcpy(net->data, &header, sizeof(header));
765         net->dataLen = EDBG_DATA_OFFSET;
766
767         int retries = 10;
768         int rc;
769         do {
770                 rc = bootme_send_frame(net->data, net->dataLen);
771                 if (rc != 0) {
772                         printf("Failed to send ACK: %d\n", rc);
773                 }
774         } while (rc && retries-- > 0);
775         return rc ?: ret;
776 }
777
778 static enum bootme_state ce_edbg_handler(const void *buf, size_t len)
779 {
780         if (len == 0)
781                 return BOOTME_DONE;
782
783         g_net.data = (void *)buf;
784         g_net.dataLen = len;
785
786         return ce_process_edbg(&g_net, &g_bin);
787 }
788
789 static void ce_init_edbg_link(ce_net *net)
790 {
791         /* Initialize EDBG link for commands */
792         net->state = BOOTME_INIT;
793 }
794
795 static enum bootme_state ce_download_handler(const void *buf, size_t len)
796 {
797         g_net.data = (void *)buf;
798         g_net.dataLen = len;
799
800         g_net.state = ce_process_download(&g_net, &g_bin);
801         return g_net.state;
802 }
803
804 static int ce_send_bootme(ce_net *net)
805 {
806         eth_dbg_hdr *header;
807         edbg_bootme_data *data;
808         unsigned char txbuf[PKTSIZE_ALIGN];
809 #ifdef DEBUG
810         int     i;
811         unsigned char   *pkt;
812 #endif
813         /* Fill out BOOTME packet */
814         net->data = txbuf;
815
816         memset(net->data, 0, PKTSIZE);
817         header = (eth_dbg_hdr *)net->data;
818         data = (edbg_bootme_data *)header->data;
819
820         header->id = EDBG_ID;
821         header->service = EDBG_SVC_ADMIN;
822         header->flags = EDBG_FL_FROM_DEV;
823         header->seqNum = net->seqNum++;
824         header->cmd = EDBG_CMD_BOOTME;
825
826         data->versionMajor = 0;
827         data->versionMinor = 0;
828         data->cpuId = EDBG_CPU_TYPE_ARM;
829         data->bootmeVer = EDBG_CURRENT_BOOTME_VERSION;
830         data->bootFlags = 0;
831         data->downloadPort = 0;
832         data->svcPort = 0;
833
834         /* MAC address from environment*/
835         if (!eth_getenv_enetaddr("ethaddr", data->macAddr)) {
836                 printf("'ethaddr' is not set or invalid\n");
837                 memset(data->macAddr, 0, sizeof(data->macAddr));
838         }
839
840         /* IP address from active config */
841         net_copy_ip(&data->ipAddr, &net_ip);
842
843         // Device name string (NULL terminated). Should include
844         // platform and number based on Ether address (e.g. Odo42, CEPCLS2346, etc)
845
846         // We will use lower MAC address segment to create device name
847         // eg. MAC '00-0C-C6-69-09-05', device name 'Triton05'
848
849         strncpy(data->platformId, "Triton", sizeof(data->platformId));
850         snprintf(data->deviceName, sizeof(data->deviceName), "%s%02X",
851                 data->platformId, data->macAddr[5]);
852
853 #ifdef DEBUG
854         printf("header->id: %08X\n", header->id);
855         printf("header->service: %08X\n", header->service);
856         printf("header->flags: %08X\n", header->flags);
857         printf("header->seqNum: %08X\n", header->seqNum);
858         printf("header->cmd: %08X\n\n", header->cmd);
859
860         printf("data->versionMajor: %08X\n", data->versionMajor);
861         printf("data->versionMinor: %08X\n", data->versionMinor);
862         printf("data->cpuId: %08X\n", data->cpuId);
863         printf("data->bootmeVer: %08X\n", data->bootmeVer);
864         printf("data->bootFlags: %08X\n", data->bootFlags);
865         printf("data->svcPort: %08X\n\n", ntohs(data->svcPort));
866
867         printf("data->macAddr: %pM\n", data->macAddr);
868         printf("data->ipAddr: %pI4\n", &data->ipAddr);
869         printf("data->platformId: %s\n", data->platformId);
870         printf("data->deviceName: %s\n", data->deviceName);
871 #endif
872         // Some diag output ...
873         if (net->verbose) {
874                 printf("Sending BOOTME request [%d] to %pI4\n", net->seqNum,
875                         &server_ip);
876         }
877
878         net->dataLen = BOOTME_PKT_SIZE;
879 //      net->status = CE_PR_MORE;
880         net->state = BOOTME_INIT;
881 #ifdef DEBUG
882         debug("Start of buffer:      %p\n", net->data);
883         debug("Start of ethernet buffer:   %p\n", net->data);
884         debug("Start of CE header:         %p\n", header);
885         debug("Start of CE data:           %p\n", data);
886
887         pkt = net->data;
888         debug("packet to send (ceconnect): \n");
889         for (i = 0; i < net->dataLen; i++) {
890                 debug("0x%02X ", pkt[i]);
891                 if (!((i + 1) % 16))
892                         debug("\n");
893         }
894         debug("\n");
895 #endif
896         return BootMeRequest(server_ip, net->data, net->dataLen, 1);
897 }
898
899 static inline int ce_init_download_link(ce_net *net, ce_bin *bin, int verbose)
900 {
901         if (!eth_get_dev()) {
902                 printf("No network interface available\n");
903                 return -ENODEV;
904         }
905         printf("Using device '%s'\n", eth_get_name());
906
907         /* Initialize EDBG link for download */
908         memset(net, 0, sizeof(*net));
909
910         net->verbose = verbose;
911
912         /* buffer will be dynamically assigned in ce_download_handler() */
913         ce_init_bin(bin, NULL);
914         return 0;
915 }
916
917 static inline int ce_download_file(ce_net *net, ulong timeout)
918 {
919         ulong start = get_timer_masked();
920
921         while (net->state == BOOTME_INIT) {
922                 int ret;
923
924                 if (timeout && get_timer(start) > timeout) {
925                         printf("CELOAD - Canceled, timeout\n");
926                         return 1;
927                 }
928
929                 if (ctrlc()) {
930                         printf("CELOAD - canceled by user\n");
931                         return 1;
932                 }
933
934                 if (ce_send_bootme(&g_net)) {
935                         printf("CELOAD - error while sending BOOTME request\n");
936                         return 1;
937                 }
938                 if (net->verbose) {
939                         if (timeout) {
940                                 printf("Waiting for connection, timeout %lu sec\n",
941                                         DIV_ROUND_UP(timeout - get_timer(start),
942                                                 CONFIG_SYS_HZ));
943                         } else {
944                                 printf("Waiting for connection, enter ^C to abort\n");
945                         }
946                 }
947
948                 ret = BootMeDownload(ce_download_handler);
949                 if (ret == BOOTME_ERROR) {
950                         printf("CELOAD - aborted\n");
951                         return 1;
952                 }
953         }
954         return 0;
955 }
956
957 static void ce_disconnect(void)
958 {
959         net_set_udp_handler(NULL);
960         eth_halt();
961 }
962
963 static int do_ceconnect(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
964 {
965         int verbose = 0;
966         ulong timeout = 0;
967         int ret = 1;
968         int i;
969
970         server_ip.s_addr = 0;
971
972         for (i = 1; i < argc; i++){
973                 if (*argv[i] != '-')
974                         break;
975                 if (argv[i][1] == 'v') {
976                         verbose = 1;
977                 } else if (argv[i][1] == 't') {
978                         i++;
979                         if (argc > i) {
980                                 timeout = simple_strtoul(argv[i],
981                                                         NULL, 0);
982                                 if (timeout >= UINT_MAX / CONFIG_SYS_HZ) {
983                                         printf("Timeout value %lu out of range (max.: %u)\n",
984                                                 timeout, UINT_MAX / CONFIG_SYS_HZ - 1);
985                                         return CMD_RET_USAGE;
986                                 }
987                                 timeout *= CONFIG_SYS_HZ;
988                         } else {
989                                 printf("Option requires an argument - t\n");
990                                 return CMD_RET_USAGE;
991                         }
992                 } else if (argv[i][1] == 'h') {
993                         i++;
994                         if (argc > i) {
995                                 server_ip = string_to_ip(argv[i]);
996                                 printf("Using server %pI4\n", &server_ip);
997                         } else {
998                                 printf("Option requires an argument - h\n");
999                                 return CMD_RET_USAGE;
1000                         }
1001                 }
1002         }
1003
1004         if (ce_init_download_link(&g_net, &g_bin, verbose) != 0)
1005                 goto err;
1006
1007         if (ce_download_file(&g_net, timeout))
1008                 goto err;
1009
1010         if (g_bin.binLen) {
1011                 // Try to receive edbg commands from host
1012                 ce_init_edbg_link(&g_net);
1013                 if (verbose)
1014                         printf("Waiting for EDBG commands ...\n");
1015
1016                 ret = BootMeDebugStart(ce_edbg_handler);
1017                 if (ret != BOOTME_DONE)
1018                         goto err;
1019
1020                 // Prepare WinCE image for execution
1021                 ce_prepare_run_bin(&g_bin);
1022
1023                 // Launch WinCE, if necessary
1024                 if (g_net.gotJumpingRequest)
1025                         ce_run_bin(g_bin.eEntryPoint);
1026         }
1027         ret = 0;
1028 err:
1029         ce_disconnect();
1030         return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
1031 }
1032 U_BOOT_CMD(
1033         ceconnect, 6, 1, do_ceconnect,
1034         "Set up a connection to the CE host PC over TCP/IP and download the run-time image",
1035         "[-v] [-t <timeout>] [-h host]\n"
1036         "  -v            - verbose operation\n"
1037         "  -t <timeout>  - max wait time (#sec) for the connection\n"
1038         "  -h <host>     - send BOOTME requests to <host> (default: broadcast address 255.255.255.255)"
1039 );