3 Copyright 2005, Broadcom Corporation
6 THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
7 KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
8 SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
9 FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
14 Modified for Tomato Firmware
15 Portions, Copyright (C) 2006-2009 Jonathan Zarate
22 #include <sys/sysmacros.h>
23 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <sys/sysinfo.h>
32 #include <linux/compiler.h>
33 #include <mtd/mtd-user.h>
35 #include <linux/mtd/mtd.h>
43 #include <bcmendian.h>
47 // #define DEBUG_SIMULATE
59 unsigned char res3
[10];
62 // -----------------------------------------------------------------------------
64 static uint32
*crc_table
= NULL
;
66 static void crc_done(void)
72 // -----------------------------------------------------------------------------
75 static int mtd_open_old(const char *mtdname
, mtd_info_t
*mi
)
77 static int mtd_open(const char *mtdname
, mtd_info_t
*mi
)
85 if (mtd_getinfo(mtdname
, &part
, &size
)) {
86 sprintf(path
, MTD_DEV(%d
), part
);
87 if ((f
= open(path
, O_RDWR
|O_SYNC
)) >= 0) {
88 if ((mi
) && ioctl(f
, MEMGETINFO
, mi
) != 0) {
98 static int _unlock_erase(const char *mtdname
, int erase
)
105 if (!wait_action_idle(5)) return 0;
106 set_action(ACT_ERASE_NVRAM
);
111 #ifdef TCONFIG_BCMARM
112 if ((mf
= mtd_open_old(mtdname
, &mi
)) >= 0) {
114 if ((mf
= mtd_open(mtdname
, &mi
)) >= 0) {
118 ei
.length
= mi
.erasesize
;
119 for (ei
.start
= 0; ei
.start
< mi
.size
; ei
.start
+= mi
.erasesize
) {
120 printf("%sing 0x%x - 0x%x\n", erase
? "Eras" : "Unlock", ei
.start
, (ei
.start
+ ei
.length
) - 1);
124 loff_t offset
= ei
.start
;
126 if ((ret
= ioctl(mf
, MEMGETBADBLOCK
, &offset
)) > 0) {
127 printf("Skipping bad block at 0x%08x\n", ei
.start
);
129 } else if (ret
< 0) {
130 if (errno
== EOPNOTSUPP
) {
131 skipbb
= 1; // Not supported by this device
133 perror("MEMGETBADBLOCK");
139 if (ioctl(mf
, MEMUNLOCK
, &ei
) != 0) {
140 // perror("MEMUNLOCK");
145 if (ioctl(mf
, MEMERASE
, &ei
) != 0) {
156 printf("%sing 0x%x - 0x%x\n", erase
? "Eras" : "Unlock", ei
.start
, ei
.length
- 1);
159 if (ioctl(mf
, MEMUNLOCK
, &ei
) != 0) {
164 if (ioctl(mf
, MEMERASE
, &ei
) != 0) {
173 read(mf
, &buf
, sizeof(buf
));
177 set_action(ACT_IDLE
);
179 if (r
) printf("\"%s\" successfully %s.\n", mtdname
, erase
? "erased" : "unlocked");
180 else printf("\nError %sing MTD\n", erase
? "eras" : "unlock");
186 int mtd_unlock(const char *mtdname
)
188 return _unlock_erase(mtdname
, 0);
191 #ifdef TCONFIG_BCMARM
192 int mtd_erase_old(const char *mtdname
)
194 int mtd_erase(const char *mtdname
)
197 return _unlock_erase(mtdname
, 1);
200 #ifdef TCONFIG_BCMARM
201 int mtd_unlock_erase_main_old(int argc
, char *argv
[])
203 int mtd_unlock_erase_main(int argc
, char *argv
[])
209 while ((c
= getopt(argc
, argv
, "d:")) != -1) {
218 usage_exit(argv
[0], "-d part");
221 return _unlock_erase(dev
, strstr(argv
[0], "erase") ? 1 : 0);
224 #ifdef TCONFIG_BCMARM
225 int mtd_write_main_old(int argc
, char *argv
[])
227 int mtd_write_main(int argc
, char *argv
[])
233 #ifdef TCONFIG_BCMARM
235 struct code_header cth
;
239 unsigned char *buf
= NULL
, *p
, *bounce_buf
= NULL
;
241 long filelen
= 0, n
, wlen
, unit_len
;
249 int alloc
= 0, bounce
= 0, fd
;
250 #ifdef DEBUG_SIMULATE
254 while ((c
= getopt(argc
, argv
, "i:d:w")) != -1) {
268 if ((iname
== NULL
) || (dev
== NULL
)) {
269 usage_exit(argv
[0], "-i file -d part");
272 if (!wait_action_idle(10)) {
273 printf("System is busy\n");
277 set_action(ACT_WEB_UPGRADE
);
279 if ((f
= fopen(iname
, "r")) == NULL
) {
280 error
= "Error opening input file";
285 fseek( f
, 0, SEEK_END
);
287 fseek( f
, 0, SEEK_SET
);
288 _dprintf("file len=0x%x\n", filelen
);
290 #ifdef TCONFIG_BCMARM
291 if ((mf
= mtd_open_old(dev
, &mi
)) < 0) {
293 if ((mf
= mtd_open(dev
, &mi
)) < 0) {
295 snprintf(msg_buf
, sizeof(msg_buf
), "Error opening MTD device. (errno %d (%s))", errno
, strerror(errno
));
300 if (mi
.erasesize
< sizeof(struct trx_header
)) {
301 error
= "Error obtaining MTD information";
305 _dprintf("mtd size=%x, erasesize=%x, writesize=%x, type=%x\n", mi
.size
, mi
.erasesize
, mi
.writesize
, mi
.type
);
307 unit_len
= ROUNDUP(filelen
, mi
.erasesize
);
308 if (unit_len
> mi
.size
) {
309 error
= "File is too big to fit in MTD";
313 if ((buf
= mmap(0, filelen
, PROT_READ
, MAP_SHARED
, fd
, 0)) == (unsigned char*)MAP_FAILED
) {
314 _dprintf("mmap %x bytes fail!. errno %d (%s).\n", filelen
, errno
, strerror(errno
));
320 if ((si
.freeram
* si
.mem_unit
) <= (unit_len
+ (4096 * 1024)))
321 unit_len
= mi
.erasesize
;
324 if (mi
.type
== MTD_UBIVOLUME
) {
325 if (!(bounce_buf
= malloc(mi
.writesize
))) {
326 error
= "Not enough memory";
330 _dprintf("freeram=%lx unit_len=%lx filelen=%lx mi.erasesize=%x mi.writesize=%x\n",
331 si
.freeram
, unit_len
, filelen
, mi
.erasesize
, mi
.writesize
);
333 if (alloc
&& !(buf
= malloc(unit_len
))) {
334 error
= "Not enough memory";
338 #ifdef DEBUG_SIMULATE
339 if ((of
= fopen("/mnt/out.bin", "w")) == NULL
) {
340 error
= "Error creating test file";
345 for (ei
.start
= ofs
= 0, ei
.length
= unit_len
, n
= 0, error
= NULL
, p
= buf
;
347 ofs
+= n
, ei
.start
+= unit_len
)
349 wlen
= n
= MIN(unit_len
, filelen
- ofs
);
350 if (mi
.type
== MTD_UBIVOLUME
) {
351 if (n
>= mi
.writesize
) {
352 n
&= ~(mi
.writesize
- 1);
356 memcpy(bounce_buf
, p
, n
);
359 wlen
= ROUNDUP(n
, mi
.writesize
);
363 if (alloc
&& safe_fread(p
, 1, n
, f
) != n
) {
364 error
= "Error reading file";
368 _dprintf("ofs=%x n=%lx/%lx ei.start=%x ei.length=%x\n", ofs
, n
, wlen
, ei
.start
, ei
.length
);
370 #ifdef DEBUG_SIMULATE
371 if (fwrite(p
, 1, wlen
, of
) != wlen
) {
373 error
= "Error writing to test file";
377 if (ei
.start
== ofs
) {
378 ioctl(mf
, MEMUNLOCK
, &ei
);
379 if (ioctl(mf
, MEMERASE
, &ei
) != 0) {
380 snprintf(msg_buf
, sizeof(msg_buf
), "Error erasing MTD block. (errno %d (%s))", errno
, strerror(errno
));
385 if (write(mf
, p
, wlen
) != wlen
) {
386 snprintf(msg_buf
, sizeof(msg_buf
), "Error writing to MTD device. (errno %d (%s))", errno
, strerror(errno
));
392 if (!(alloc
|| bounce
))
396 #ifdef DEBUG_SIMULATE
402 munmap((void*) buf
, filelen
);
410 // dummy read to ensure chip(s) are out of lock/suspend state
411 read(mf
, &n
, sizeof(n
));
418 set_action(ACT_IDLE
);
420 _dprintf("%s\n", error
? error
: "Image successfully flashed");
421 return (error
? 1 : 0);
424 #ifdef TCONFIG_BCMARM
428 * @param mtd path to or partition name of MTD device
429 * @param flags open() flags
430 * @return return value of open()
433 mtd_open(const char *mtd
, int flags
)
439 if ((fp
= fopen("/proc/mtd", "r"))) {
440 while (fgets(dev
, sizeof(dev
), fp
)) {
441 if (sscanf(dev
, "mtd%d:", &i
) && strstr(dev
, mtd
)) {
443 snprintf(dev
, sizeof(dev
), "/dev/mtd%d", i
);
445 snprintf(dev
, sizeof(dev
), "/dev/mtd/%d", i
);
448 return open(dev
, flags
);
454 return open(mtd
, flags
);
458 * Erase an MTD device
459 * @param mtd path to or partition name of MTD device
460 * @return 0 on success and errno on failure
463 mtd_erase(const char *mtd
)
467 erase_info_t erase_info
;
469 /* Open MTD device */
470 if ((mtd_fd
= mtd_open(mtd
, O_RDWR
)) < 0) {
475 /* Get sector size */
476 if (ioctl(mtd_fd
, MEMGETINFO
, &mtd_info
) != 0) {
482 erase_info
.length
= mtd_info
.erasesize
;
484 for (erase_info
.start
= 0;
485 erase_info
.start
< mtd_info
.size
;
486 erase_info
.start
+= mtd_info
.erasesize
) {
487 (void) ioctl(mtd_fd
, MEMUNLOCK
, &erase_info
);
488 if (ioctl(mtd_fd
, MEMERASE
, &erase_info
) != 0) {
500 base64enc(const char *p
, char *buf
, int len
)
502 char al
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
509 *(s
++) = al
[(*p
>> 2) & 0x3F];
510 *(s
++) = al
[((*p
<< 4) & 0x30) | ((*(p
+1) >> 4) & 0x0F)];
514 *(s
++) = al
[((*p
<< 2) & 0x3C) | ((*(p
+1) >> 6) & 0x03)];
516 *(s
++) = al
[*(p
++) & 0x3F];
528 wget(int method
, const char *server
, char *buf
, size_t count
, off_t offset
)
530 char url
[PATH_MAX
] = { 0 }, *s
;
531 char *host
= url
, *path
= "", auth
[128] = { 0 }, line
[512];
532 unsigned short port
= 80;
535 struct sockaddr_in sin
;
536 int chunked
= 0, len
= 0;
538 if (server
== NULL
|| !strcmp(server
, "")) {
539 _dprintf("wget: null server input\n");
543 strncpy(url
, server
, sizeof(url
));
546 if (!strncmp(url
, "http://", 7)) {
550 if ((s
= strchr(host
, '/'))) {
554 if ((s
= strchr(host
, '@'))) {
556 base64enc(host
, auth
, sizeof(auth
));
559 if ((s
= strchr(host
, ':'))) {
565 if (!inet_aton(host
, &sin
.sin_addr
))
567 sin
.sin_family
= AF_INET
;
568 sin
.sin_port
= htons(port
);
569 _dprintf("Connecting to %s:%u...\n", host
, port
);
570 if ((fd
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0 ||
571 connect(fd
, (struct sockaddr
*) &sin
, sizeof(sin
)) < 0 ||
572 !(fp
= fdopen(fd
, "r+"))) {
578 _dprintf("connected!\n");
580 /* Send HTTP request */
581 fprintf(fp
, "%s /%s HTTP/1.1\r\n", method
== METHOD_POST
? "POST" : "GET", path
);
582 fprintf(fp
, "Host: %s\r\n", host
);
583 fprintf(fp
, "User-Agent: wget\r\n");
585 fprintf(fp
, "Authorization: Basic %s\r\n", auth
);
587 fprintf(fp
, "Range: bytes=%ld-\r\n", offset
);
588 if (method
== METHOD_POST
) {
589 fprintf(fp
, "Content-Type: application/x-www-form-urlencoded\r\n");
590 fprintf(fp
, "Content-Length: %d\r\n\r\n", (int) strlen(buf
));
593 fprintf(fp
, "Connection: close\r\n\r\n");
595 /* Check HTTP response */
596 _dprintf("HTTP request sent, awaiting response...\n");
597 if (fgets(line
, sizeof(line
), fp
)) {
598 _dprintf("%s", line
);
599 for (s
= line
; *s
&& !isspace((int)*s
); s
++);
600 for (; isspace((int)*s
); s
++);
602 case 200: if (offset
) goto done
; else break;
603 case 206: if (offset
) break; else goto done
;
608 while (fgets(line
, sizeof(line
), fp
)) {
609 _dprintf("%s", line
);
610 for (s
= line
; *s
== '\r'; s
++);
613 if (!strncasecmp(s
, "Content-Length:", 15)) {
614 for (s
+= 15; isblank(*s
); s
++);
618 else if (!strncasecmp(s
, "Transfer-Encoding:", 18)) {
619 for (s
+= 18; isblank(*s
); s
++);
621 if (!strncasecmp(s
, "chunked", 7))
626 if (chunked
&& fgets(line
, sizeof(line
), fp
))
627 len
= strtol(line
, NULL
, 16);
629 len
= (len
> count
) ? count
: len
;
630 len
= fread(buf
, 1, len
, fp
);
640 http_get(const char *server
, char *buf
, size_t count
, off_t offset
)
642 return wget(METHOD_GET
, server
, buf
, count
, offset
);
646 * Write a file to an MTD device
647 * @param path file to write or a URL
648 * @param mtd path to or partition name of MTD device
649 * @return 0 on success and errno on failure
652 mtd_write(const char *path
, const char *mtd
)
656 erase_info_t erase_info
;
659 struct trx_header trx
;
662 #ifdef CONFIG_FAILSAFE_UPGRADE
663 if (get_model() == MODEL_EA6700
)
665 if (nvram_match("bootpartition", "1")) {
667 nvram_set("bootpartition", "0");
671 nvram_set("bootpartition", "1");
678 long count
, len
, off
;
682 if ((fp
= fopen(path
, "r")))
683 count
= safe_fread(&trx
, 1, sizeof(struct trx_header
), fp
);
685 count
= http_get(path
, (char *) &trx
, sizeof(struct trx_header
), 0);
686 if (count
< sizeof(struct trx_header
)) {
687 fprintf(stderr
, "%s: File is too small (%ld bytes)\n", path
, count
);
691 /* Open MTD device and get sector size */
692 if ((mtd_fd
= mtd_open(mtd
, O_RDWR
)) < 0 ||
693 ioctl(mtd_fd
, MEMGETINFO
, &mtd_info
) != 0 ||
694 mtd_info
.erasesize
< sizeof(struct trx_header
)) {
699 if (trx
.magic
!= TRX_MAGIC
||
700 trx
.len
> mtd_info
.size
||
701 trx
.len
< sizeof(struct trx_header
)) {
702 fprintf(stderr
, "%s: Bad trx header\n", path
);
706 /* Allocate temporary buffer */
707 /* See if we have enough memory to store the whole file */
709 if (info
.freeram
>= trx
.len
) {
710 erase_info
.length
= ROUNDUP(trx
.len
, mtd_info
.erasesize
);
711 if (!(buf
= malloc(erase_info
.length
)))
712 erase_info
.length
= mtd_info
.erasesize
;
714 /* fallback to smaller buffer */
716 erase_info
.length
= mtd_info
.erasesize
;
719 if (!buf
&& (!(buf
= malloc(erase_info
.length
)))) {
724 /* Calculate CRC over header */
725 crc
= hndcrc32((uint8
*) &trx
.flag_version
,
726 sizeof(struct trx_header
) - OFFSETOF(struct trx_header
, flag_version
),
729 if (trx
.flag_version
& TRX_NO_HEADER
)
730 trx
.len
-= sizeof(struct trx_header
);
732 /* Write file or URL to MTD device */
733 for (erase_info
.start
= 0; erase_info
.start
< trx
.len
; erase_info
.start
+= count
) {
734 len
= MIN(erase_info
.length
, trx
.len
- erase_info
.start
);
735 if ((trx
.flag_version
& TRX_NO_HEADER
) || erase_info
.start
)
738 count
= off
= sizeof(struct trx_header
);
739 memcpy(buf
, &trx
, sizeof(struct trx_header
));
742 count
+= safe_fread(&buf
[off
], 1, len
- off
, fp
);
744 count
+= http_get(path
, &buf
[off
], len
- off
, erase_info
.start
+ off
);
746 fprintf(stderr
, "%s: Truncated file (actual %ld expect %ld)\n", path
,
747 count
- off
, len
- off
);
751 crc
= hndcrc32((uint8
*)&buf
[off
], count
- off
, crc
);
752 /* Check CRC before writing if possible */
753 if (count
== trx
.len
) {
754 if (crc
!= trx
.crc32
) {
755 fprintf(stderr
, "%s: Bad CRC\n", path
);
760 (void) ioctl(mtd_fd
, MEMUNLOCK
, &erase_info
);
761 if (ioctl(mtd_fd
, MEMERASE
, &erase_info
) != 0 ||
762 write(mtd_fd
, buf
, count
) != count
) {
769 eval("gigle_util restart");
770 nvram_set("plc_pconfig_state", "2");
774 printf("%s: CRC OK - Image successfully flashed\n", mtd
);
779 /* Dummy read to ensure chip(s) are out of lock/suspend state */
780 (void) read(mtd_fd
, buf
, 2);