Fixed USB LED GPIO Writes
[tomato.git] / release / src / router / rc / mtd.c
blob7ed1e9b1bf1e4c9767cd1115848ad49738363f6e
1 /*
3 Copyright 2003, CyberTAN Inc. All Rights Reserved
5 This is UNPUBLISHED PROPRIETARY SOURCE CODE of CyberTAN Inc.
6 the contents of this file may not be disclosed to third parties,
7 copied or duplicated in any form without the prior written
8 permission of CyberTAN Inc.
10 This software should be used as a reference only, and it not
11 intended for production use!
13 THIS SOFTWARE IS OFFERED "AS IS", AND CYBERTAN GRANTS NO WARRANTIES OF ANY
14 KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. CYBERTAN
15 SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
16 FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE
21 Copyright 2005, Broadcom Corporation
22 All Rights Reserved.
24 THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
25 KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
26 SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
27 FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
32 Modified for Tomato Firmware
33 Portions, Copyright (C) 2006-2009 Jonathan Zarate
37 #include "rc.h"
39 #include <limits.h>
40 #include <sys/sysmacros.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <error.h>
46 #include <sys/ioctl.h>
47 #include <sys/sysinfo.h>
48 #include <bcmendian.h>
49 #ifdef LINUX26
50 #include <linux/compiler.h>
51 #include <mtd/mtd-user.h>
52 #else
53 #include <linux/mtd/mtd.h>
54 #endif
55 #include <stdint.h>
57 #include <trxhdr.h>
58 #include <bcmutils.h>
61 // #define DEBUG_SIMULATE
63 struct code_header {
64 char magic[4];
65 char res1[4];
66 char fwdate[3];
67 char fwvern[3];
68 char id[4];
69 char hw_ver;
70 char res2;
71 unsigned short flags;
72 unsigned char res3[10];
73 } ;
75 // Netgear CHK Header -> contains needed checksum information (for Netgear)
76 // Information here is stored big endian (vs. later information in TRX header and linux / rootfs, which is little endian)
77 // First two entries are commented out, as they are read manually to determine the file format and header length (and rewind is not working?)
78 struct chk_header {
79 //uint32_t magic;
80 //uint32_t header_len;
81 uint8_t reserved[8];
82 uint32_t kernel_chksum;
83 uint32_t rootfs_chksum;
84 uint32_t kernel_len;
85 uint32_t rootfs_len;
86 uint32_t image_chksum;
87 uint32_t header_chksum;
88 /* char board_id[] - upto MAX_BOARD_ID_LEN, not NULL terminated! */
91 // -----------------------------------------------------------------------------
93 static uint32 *crc_table = NULL;
95 static void crc_done(void)
97 free(crc_table);
98 crc_table = NULL;
101 static int crc_init(void)
103 uint32 c;
104 int i, j;
106 if (crc_table == NULL) {
107 if ((crc_table = malloc(sizeof(uint32) * 256)) == NULL) return 0;
108 for (i = 255; i >= 0; --i) {
109 c = i;
110 for (j = 8; j > 0; --j) {
111 if (c & 1) c = (c >> 1) ^ 0xEDB88320L;
112 else c >>= 1;
114 crc_table[i] = c;
117 return 1;
120 static uint32 crc_calc(uint32 crc, char *buf, int len)
122 while (len-- > 0) {
123 crc = crc_table[(crc ^ *((char *)buf)) & 0xFF] ^ (crc >> 8);
124 buf++;
126 return crc;
129 // -----------------------------------------------------------------------------
131 static int mtd_open(const char *mtdname, mtd_info_t *mi)
133 char path[256];
134 int part;
135 int size;
136 int f;
138 if (mtd_getinfo(mtdname, &part, &size)) {
139 sprintf(path, MTD_DEV(%d), part);
140 if ((f = open(path, O_RDWR|O_SYNC)) >= 0) {
141 if ((mi) && ioctl(f, MEMGETINFO, mi) != 0) {
142 close(f);
143 return -1;
145 return f;
148 return -1;
151 static int _unlock_erase(const char *mtdname, int erase)
153 int mf;
154 mtd_info_t mi;
155 erase_info_t ei;
156 #ifdef CONFIG_BCMWL6
157 int r, ret, skipbb;
158 #else
159 int r;
160 #endif
162 if (!wait_action_idle(5)) return 0;
163 set_action(ACT_ERASE_NVRAM);
164 if (erase) led(LED_DIAG, 1);
166 r = 0;
167 #ifdef CONFIG_BCMWL6
168 skipbb = 0;
169 #endif
170 if ((mf = mtd_open(mtdname, &mi)) >= 0) {
171 r = 1;
172 #if 1
173 ei.length = mi.erasesize;
174 for (ei.start = 0; ei.start < mi.size; ei.start += mi.erasesize) {
175 printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, (ei.start + ei.length) - 1);
176 fflush(stdout);
178 #ifdef CONFIG_BCMWL6
179 if (!skipbb) {
180 loff_t offset = ei.start;
182 if ((ret = ioctl(mf, MEMGETBADBLOCK, &offset)) > 0) {
183 printf("Skipping bad block at 0x%08x\n", ei.start);
184 continue;
185 } else if (ret < 0) {
186 if (errno == EOPNOTSUPP) {
187 skipbb = 1; // Not supported by this device
188 } else {
189 perror("MEMGETBADBLOCK");
190 r = 0;
191 break;
195 #endif
196 if (ioctl(mf, MEMUNLOCK, &ei) != 0) {
197 // perror("MEMUNLOCK");
198 // r = 0;
199 // break;
201 if (erase) {
202 if (ioctl(mf, MEMERASE, &ei) != 0) {
203 perror("MEMERASE");
204 r = 0;
205 break;
209 #else
210 ei.start = 0;
211 ei.length = mi.size;
213 printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, ei.length - 1);
214 fflush(stdout);
216 if (ioctl(mf, MEMUNLOCK, &ei) != 0) {
217 perror("MEMUNLOCK");
218 r = 0;
220 else if (erase) {
221 if (ioctl(mf, MEMERASE, &ei) != 0) {
222 perror("MEMERASE");
223 r = 0;
226 #endif
228 // checkme:
229 char buf[2];
230 read(mf, &buf, sizeof(buf));
231 close(mf);
234 if (erase) led(LED_DIAG, 0);
235 set_action(ACT_IDLE);
237 if (r) printf("\"%s\" successfully %s.\n", mtdname, erase ? "erased" : "unlocked");
238 else printf("\nError %sing MTD\n", erase ? "eras" : "unlock");
240 sleep(1);
241 return r;
244 int mtd_unlock(const char *mtdname)
246 return _unlock_erase(mtdname, 0);
249 int mtd_erase(const char *mtdname)
251 return _unlock_erase(mtdname, 1);
254 int mtd_unlock_erase_main(int argc, char *argv[])
256 char c;
257 char *dev = NULL;
259 while ((c = getopt(argc, argv, "d:")) != -1) {
260 switch (c) {
261 case 'd':
262 dev = optarg;
263 break;
267 if (!dev) {
268 usage_exit(argv[0], "-d part");
271 return _unlock_erase(dev, strstr(argv[0], "erase") ? 1 : 0);
274 int mtd_write_main(int argc, char *argv[])
276 int mf = -1;
277 mtd_info_t mi;
278 erase_info_t ei;
279 uint32 sig;
280 struct trx_header trx;
281 struct code_header cth;
282 struct chk_header netgear_hdr;
283 uint32 netgear_chk_len;
284 uint32 crc;
285 FILE *f;
286 char *buf = NULL;
287 const char *error;
288 uint32 total;
289 uint32 n;
290 struct sysinfo si;
291 uint32 ofs;
292 char c;
293 int web = 0;
294 char *iname = NULL;
295 char *dev = NULL;
296 int model;
298 while ((c = getopt(argc, argv, "i:d:w")) != -1) {
299 switch (c) {
300 case 'i':
301 iname = optarg;
302 break;
303 case 'd':
304 dev = optarg;
305 break;
306 case 'w':
307 web = 1;
308 break;
312 if ((iname == NULL) || (dev == NULL)) {
313 usage_exit(argv[0], "-i file -d part");
316 if (!wait_action_idle(10)) {
317 printf("System is busy\n");
318 return 1;
320 set_action(ACT_WEB_UPGRADE);
322 if ((f = fopen(iname, "r")) == NULL) {
323 error = "Error opening input file";
324 goto ERROR;
327 error = "File contains an invalid header";
329 if (safe_fread(&sig, 1, sizeof(sig), f) != sizeof(sig)) {
330 goto ERROR;
333 switch (sig) {
334 case 0x47343557: // W54G G, GL
335 case 0x53343557: // W54S GS
336 case 0x73343557: // W54s GS v4
337 case 0x55343557: // W54U SL
338 case 0x31345257: // WR41 WRH54G
339 case 0x4E303233: // 320N WRT320N
340 case 0x4E583233: // 32XN E2000
341 case 0x4E303136: // 610N WRT610N v2
342 case 0x4E583136: // 61XN E3000
343 case 0x30303845: // E800 E800
344 case 0x30303945: // E900 E900
345 case 0x30303145: // E100 E1000
346 case 0x30323145: // E120 E1200v1
347 case 0x32323145: // E122 E1200v2
348 case 0x30353145: // E150 E1500
349 case 0x30353531: // 1550 E1550
350 case 0x3031304D: // M010 M10
351 case 0x3032304D: // M020 M20
352 case 0x3036314E: // N160 WRT160N
353 case 0x42435745: // EWCB WRT300N v1
354 case 0x4E303133: // 310N WRT310N v1/v2
355 // case 0x32435745: // EWC2 WRT300N?
356 case 0x3035314E: // N150 WRT150N
357 case 0x58353245: // E25X E2500
358 case 0x30303233: // 3200 E3200
359 case 0x30303234: // 4200 E4200
360 if (safe_fread(((char *)&cth) + 4, 1, sizeof(cth) - 4, f) != (sizeof(cth) - 4)) {
361 goto ERROR;
363 if (memcmp(cth.id, "U2ND", 4) != 0) {
364 goto ERROR;
367 // trx should be next...
368 if (safe_fread(&sig, 1, sizeof(sig), f) != sizeof(sig)) {
369 goto ERROR;
371 break;
372 case 0x5E24232A: // Netgear
373 // Get the Netgear header length
374 if (safe_fread(&n, 1, sizeof(n), f) != sizeof(n)) {
375 goto ERROR;
376 } else {
377 // And Byte Swap, Netgear header is big endian, machine is little endian
378 n = BCMSWAP32(n);
379 _dprintf("Read Netgear Header Length: 0x%x\n", n);
382 // Read (formatted) Netgear CHK header (now that we know how long it is)
383 //rewind(f); ... disabled, not working for some reason? Adjust structure above to account for this.
384 if (safe_fread(&netgear_hdr, 1, n-sizeof(sig)-sizeof(n), f) != n-sizeof(sig)-sizeof(n)) {
385 goto ERROR;
386 } else
387 _dprintf("Read Netgear Header, Magic=0x%x, Length=0x%x\n", sig, n);
389 // TRX (MAGIC) should be next...
390 if (safe_fread(&sig, 1, sizeof(sig), f) != sizeof(sig)) {
391 goto ERROR;
392 } else
393 _dprintf("Read TRX Header, Magic=0x%x\n", sig);
394 break;
395 case TRX_MAGIC:
396 break;
397 #ifdef CONFIG_BCMWL5
398 #ifndef CONFIG_BCMWL6
399 case TRX_MAGIC_F7D3301:
400 case TRX_MAGIC_F7D3302:
401 case TRX_MAGIC_F7D4302:
402 case TRX_MAGIC_F5D8235V3:
403 case TRX_MAGIC_QA:
404 sig = TRX_MAGIC;
405 break;
406 #endif
407 #endif
408 default:
409 // moto
410 if (safe_fread(&sig, 1, sizeof(sig), f) != sizeof(sig)) {
411 goto ERROR;
413 switch (sig) {
414 case 0x50705710: // WR850G
415 // trx
416 if (safe_fread(&sig, 1, sizeof(sig), f) != sizeof(sig)) {
417 goto ERROR;
419 break;
420 default:
421 goto ERROR;
423 break;
426 if (sig != TRX_MAGIC) {
427 goto ERROR;
429 if ((safe_fread(((char *)&trx) + 4, 1, sizeof(trx) - 4, f) != (sizeof(trx) - 4)) || (trx.len <= sizeof(trx))) {
430 goto ERROR;
433 model = get_model();
435 switch (model) {
436 #ifdef CONFIG_BCMWL5
437 #ifndef CONFIG_BCMWL6
438 case MODEL_F7D3301:
439 trx.magic = TRX_MAGIC_F7D3301;
440 break;
441 case MODEL_F7D3302:
442 trx.magic = TRX_MAGIC_F7D3302;
443 break;
444 case MODEL_F7D4302:
445 trx.magic = TRX_MAGIC_F7D4302;
446 break;
447 case MODEL_F5D8235v3:
448 trx.magic = TRX_MAGIC_F5D8235V3;
449 break;
450 #endif
451 #endif
452 default:
453 trx.magic = sig;
454 break;
457 if (!crc_init()) {
458 error = "Not enough memory";
459 goto ERROR;
461 crc = crc_calc(0xFFFFFFFF, (char *)&trx.flag_version, sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version));
463 if (trx.flag_version & TRX_NO_HEADER) {
464 trx.len -= sizeof(struct trx_header);
465 _dprintf("don't write header\n");
468 _dprintf("trx len=%db 0x%x\n", trx.len, trx.len);
470 if ((mf = mtd_open(dev, &mi)) < 0) {
471 error = "Error opening MTD device";
472 goto ERROR;
475 if (mi.erasesize < sizeof(struct trx_header)) {
476 error = "Error obtaining MTD information";
477 goto ERROR;
480 _dprintf("mtd size=%6x, erasesize=%6x\n", mi.size, mi.erasesize);
482 total = ROUNDUP(trx.len, mi.erasesize);
483 if (total > mi.size) {
484 error = "File is too big to fit in MTD";
485 goto ERROR;
488 sysinfo(&si);
489 if ((si.freeram * si.mem_unit) > (total + (256 * 1024))) {
490 ei.length = total;
492 else {
493 // ei.length = ROUNDUP((si.freeram - (256 * 1024)), mi.erasesize);
494 ei.length = mi.erasesize;
496 _dprintf("freeram=%ld ei.length=%d total=%u\n", si.freeram, ei.length, total);
498 if ((buf = malloc(ei.length)) == NULL) {
499 error = "Not enough memory";
500 goto ERROR;
503 #ifdef DEBUG_SIMULATE
504 FILE *of;
505 if ((of = fopen("/mnt/out.bin", "w")) == NULL) {
506 error = "Error creating test file";
507 goto ERROR;
509 #endif
511 if (trx.flag_version & TRX_NO_HEADER) {
512 ofs = 0;
514 else {
515 memcpy(buf, &trx, sizeof(trx));
516 ofs = sizeof(trx);
518 _dprintf("trx.len=%ub 0x%x ofs=%ub 0x%x\n", trx.len, trx.len, ofs, ofs);
519 netgear_chk_len = trx.len;
521 error = NULL;
523 for (ei.start = 0; ei.start < total; ei.start += ei.length) {
524 n = MIN(ei.length, trx.len) - ofs;
525 if (safe_fread(buf + ofs, 1, n, f) != n) {
526 error = "Error reading file";
527 break;
529 trx.len -= (n + ofs);
531 crc = crc_calc(crc, buf + ofs, n);
533 if (trx.len == 0) {
534 _dprintf("crc=%8x trx.crc=%8x\n", crc, trx.crc32);
535 if (crc != trx.crc32) {
536 error = "Image is corrupt";
537 break;
541 if (!web) {
542 printf("Writing %x-%x\r", ei.start, (ei.start + ei.length) - 1);
545 _dprintf("ofs=%ub n=%ub 0x%x trx.len=%ub ei.start=0x%x ei.length=0x%x mi.erasesize=0x%x\n",
546 ofs, n, n, trx.len, ei.start, ei.length, mi.erasesize);
548 n += ofs;
550 _dprintf(" erase start=%x len=%x\n", ei.start, ei.length);
551 _dprintf(" write %x\n", n);
553 #ifdef DEBUG_SIMULATE
554 if (fwrite(buf, 1, n, of) != n) {
555 fclose(of);
556 error = "Error writing to test file";
557 break;
559 #else
560 ioctl(mf, MEMUNLOCK, &ei);
561 if (ioctl(mf, MEMERASE, &ei) != 0 && model != MODEL_WNR3500LV2) {
562 error = "Error erasing MTD block";
563 break;
565 if (write(mf, buf, n) != n) {
566 error = "Error writing to MTD device";
567 break;
569 #endif
570 ofs = 0;
573 // Netgear WNR3500L: write fake len and checksum at the end of mtd
574 // Add Netgear WNDR4000 - write real len and checksum (arrmo, Dec 21/13)
575 char *tmp;
576 char imageInfo[8];
578 switch (model) {
579 case MODEL_WNR3500L:
580 case MODEL_WNR2000v2:
581 case MODEL_WNDR4000:
582 error = "Error writing Netgear CRC";
584 // Netgear CFE has the offset of the checksum hardcoded as
585 // 0x78FFF8 on 8MB flash, and 0x38FFF8 on 4MB flash - in both
586 // cases this is 8 last bytes in the block exactly 6 blocks to the end.
587 // We rely on linux partition to be sized correctly by the kernel,
588 // so the checksum area doesn't fall outside of the linux partition,
589 // and doesn't override the rootfs.
590 // Note: For WNDR4000, the target address (offset) inside Linux is 0x6FFFF8 (displayed by CFE when programmed via tftp)
591 if (model == MODEL_WNDR4000) {
592 ofs = 0x6FFFF8;
593 // Endian "convert" - machine is little endian, but header is big endian
594 crc = BCMSWAP32(netgear_hdr.kernel_chksum);
595 n = netgear_chk_len;
596 } else {
597 ofs = (mi.size > (4 *1024 * 1024) ? 0x78FFF8 : 0x38FFF8) - 0x040000;
598 n = 0x00000004; // fake length - little endian
599 crc = 0x02C0010E; // fake crc - little endian
601 _dprintf("Netgear CRC, Data to Write: CRC=0x%x, Len=0x%x, Offset=0x%x\n", crc, n, ofs);
602 memcpy(&imageInfo[0], (char *)&n, 4);
603 memcpy(&imageInfo[4], (char *)&crc, 4);
605 ei.start = (ofs / mi.erasesize) * mi.erasesize;
606 ei.length = mi.erasesize;
607 _dprintf("Netgear CRC: Erase Start=0x%x, Length=0x%x\n", ei.start, ei.length);
609 if (lseek(mf, ei.start, SEEK_SET) < 0) {
610 _dprintf("Netgear CRC: lseek() error\n");
611 goto ERROR2;
613 if (buf) free(buf);
614 if (!(buf = malloc(mi.erasesize))) {
615 _dprintf("Netgear CRC: malloc() error\n");
616 goto ERROR2;
618 if (read(mf, buf, mi.erasesize) != mi.erasesize) {
619 _dprintf("Netgear CRC: read() error\n");
620 goto ERROR2;
622 if (lseek(mf, ei.start, SEEK_SET) < 0) {
623 _dprintf("Netgear CRC: lseed() error\n");
624 goto ERROR2;
627 tmp = buf + (ofs % mi.erasesize);
628 memcpy(tmp, imageInfo, sizeof(imageInfo));
630 #ifdef DEBUG_SIMULATE
631 if (fseek(of, ei.start, SEEK_SET) < 0)
632 goto ERROR2;
633 if (fwrite(buf, 1, mi.erasesize, of) != n)
634 goto ERROR2;
635 error = NULL;
636 #else
637 ioctl(mf, MEMUNLOCK, &ei);
638 if (ioctl(mf, MEMERASE, &ei) != 0) {
639 _dprintf("Netgear CRC: ioctl() error\n");
640 goto ERROR2;
642 if (write(mf, buf, mi.erasesize) != mi.erasesize) {
643 _dprintf("Netgear CRC: write() error\n");
644 goto ERROR2;
646 error = NULL;
647 #endif
649 ERROR2:
650 _dprintf("%s.\n", error ? : "Write Netgear Length/CRC Completed.");
651 // ignore crc write errors
652 error = NULL;
653 break;
656 #ifdef DEBUG_SIMULATE
657 fclose(of);
658 #endif
660 ERROR:
661 if (buf) free(buf);
662 if (mf >= 0) {
663 // dummy read to ensure chip(s) are out of lock/suspend state
664 read(mf, &n, sizeof(n));
665 close(mf);
667 if (f) fclose(f);
669 crc_done();
671 #ifdef DEBUG_SIMULATE
672 set_action(ACT_IDLE);
673 #endif
675 printf("%s\n", error ? error : "Image successfully flashed");
676 _dprintf("%s\n", error ? error : "Image successfully flashed");
677 return (error ? 1 : 0);