summaryrefslogtreecommitdiff
path: root/misc-utils
diff options
context:
space:
mode:
authorDongsheng Yang <yangds.fnst@cn.fujitsu.com>2015-10-31 11:12:01 +0800
committerBrian Norris <computersforpeace@gmail.com>2015-11-11 14:38:40 -0800
commit7d81790ced345585b1e647ca9d0f6678e7062fa4 (patch)
tree02f61270c7a0fff7bb6b2e28f247a3d2fd6ff490 /misc-utils
parent344753f2aacb94d98ce238f81fc4a4b6ef6adea9 (diff)
mtd-utils: Restructure the mtd-utils source.
* There is no code modification in this commit, only moving * the files to proper place. The user tools looks a little messy as we place almost the all tools in the root directory of mtd-utils. To make it more clear, I propose to introduce the following structure for our source code. mtd-utils/ |-- lib |-- include |-- misc-utils |-- jffsX-utils |-- nand-utils |-- nor-utils |-- ubi-utils |-- ubifs-utils `-- tests Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'misc-utils')
-rwxr-xr-xmisc-utils/MAKEDEV41
-rw-r--r--misc-utils/doc_loadbios.c150
-rw-r--r--misc-utils/docfdisk.c318
-rw-r--r--misc-utils/fectest.c91
-rw-r--r--misc-utils/flash_erase.c295
-rwxr-xr-xmisc-utils/flash_eraseall4
-rw-r--r--misc-utils/flash_lock.c8
-rw-r--r--misc-utils/flash_otp_dump.c56
-rw-r--r--misc-utils/flash_otp_info.c65
-rw-r--r--misc-utils/flash_otp_lock.c72
-rw-r--r--misc-utils/flash_otp_write.c122
-rw-r--r--misc-utils/flash_unlock.c220
-rw-r--r--misc-utils/flashcp.c389
-rw-r--r--misc-utils/ftl_check.c217
-rw-r--r--misc-utils/ftl_format.c324
-rw-r--r--misc-utils/mcast_image.h54
-rw-r--r--misc-utils/mtd_debug.c397
-rw-r--r--misc-utils/mtdpart.c194
-rw-r--r--misc-utils/recv_image.c484
-rw-r--r--misc-utils/serve_image.c300
20 files changed, 3801 insertions, 0 deletions
diff --git a/misc-utils/MAKEDEV b/misc-utils/MAKEDEV
new file mode 100755
index 0000000..b59e90e
--- /dev/null
+++ b/misc-utils/MAKEDEV
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+function mkftl () {
+ mknod /dev/ftl$1 b 44 $2
+ for a in `seq 1 15`; do
+ mknod /dev/ftl$1$a b 44 `expr $2 + $a`
+ done
+}
+function mknftl () {
+ mknod /dev/nftl$1 b 93 $2
+ for a in `seq 1 15`; do
+ mknod /dev/nftl$1$a b 93 `expr $2 + $a`
+ done
+}
+function mkrfd () {
+ mknod /dev/rfd$1 b 256 $2
+ for a in `seq 1 15`; do
+ mknod /dev/rfd$1$a b 256 `expr $2 + $a`
+ done
+}
+function mkinftl () {
+ mknod /dev/inftl$1 b 96 $2
+ for a in `seq 1 15`; do
+ mknod /dev/inftl$1$a b 96 `expr $2 + $a`
+ done
+}
+
+M=0
+for C in a b c d e f g h i j k l m n o p; do
+ mkftl $C $M
+ mknftl $C $M
+ mkrfd $C $M
+ mkinftl $C $M
+ let M=M+16
+done
+
+for a in `seq 0 16` ; do
+ mknod /dev/mtd$a c 90 `expr $a + $a`
+ mknod /dev/mtdr$a c 90 `expr $a + $a + 1`
+ mknod /dev/mtdblock$a b 31 $a
+done
diff --git a/misc-utils/doc_loadbios.c b/misc-utils/doc_loadbios.c
new file mode 100644
index 0000000..b999c73
--- /dev/null
+++ b/misc-utils/doc_loadbios.c
@@ -0,0 +1,150 @@
+#define PROGRAM_NAME "doc_loadbios"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <mtd/mtd-user.h>
+
+unsigned char databuf[512];
+
+int main(int argc,char **argv)
+{
+ mtd_info_t meminfo;
+ int ifd,ofd;
+ struct stat statbuf;
+ erase_info_t erase;
+ unsigned long retlen, ofs, iplsize, ipltailsize;
+ unsigned char *iplbuf;
+ iplbuf = NULL;
+
+ if (argc < 3) {
+ fprintf(stderr,"You must specify a device,"
+ " the source firmware file and the offset\n");
+ return 1;
+ }
+
+ // Open and size the device
+ if ((ofd = open(argv[1],O_RDWR)) < 0) {
+ perror("Open flash device");
+ return 1;
+ }
+
+ if ((ifd = open(argv[2], O_RDONLY)) < 0) {
+ perror("Open firmware file\n");
+ close(ofd);
+ return 1;
+ }
+
+ if (fstat(ifd, &statbuf) != 0) {
+ perror("Stat firmware file");
+ goto error;
+ }
+
+#if 0
+ if (statbuf.st_size > 65536) {
+ printf("Firmware too large (%ld bytes)\n",statbuf.st_size);
+ goto error;
+ }
+#endif
+
+ if (ioctl(ofd,MEMGETINFO,&meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ goto error;
+ }
+
+ iplsize = (ipltailsize = 0);
+ if (argc >= 4) {
+ /* DoC Millennium has IPL in the first 1K of flash memory */
+ /* You may want to specify the offset 1024 to store
+ the firmware next to IPL. */
+ iplsize = strtoul(argv[3], NULL, 0);
+ ipltailsize = iplsize % meminfo.erasesize;
+ }
+
+ if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) {
+ perror("lseek");
+ goto error;
+ }
+
+ if (ipltailsize) {
+ iplbuf = malloc(ipltailsize);
+ if (iplbuf == NULL) {
+ fprintf(stderr, "Not enough memory for IPL tail buffer of"
+ " %lu bytes\n", (unsigned long) ipltailsize);
+ goto error;
+ }
+ printf("Reading IPL%s area of length %lu at offset %lu\n",
+ (iplsize - ipltailsize) ? " tail" : "",
+ (long unsigned) ipltailsize,
+ (long unsigned) (iplsize - ipltailsize));
+ if (read(ofd, iplbuf, ipltailsize) != ipltailsize) {
+ perror("read");
+ goto error;
+ }
+ }
+
+ erase.length = meminfo.erasesize;
+
+ for (ofs = iplsize - ipltailsize ;
+ ofs < iplsize + statbuf.st_size ;
+ ofs += meminfo.erasesize) {
+ erase.start = ofs;
+ printf("Performing Flash Erase of length %lu at offset %lu\n",
+ (long unsigned) erase.length, (long unsigned) erase.start);
+
+ if (ioctl(ofd,MEMERASE,&erase) != 0) {
+ perror("ioctl(MEMERASE)");
+ goto error;
+ }
+ }
+
+ if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) {
+ perror("lseek");
+ goto error;
+ }
+
+ if (ipltailsize) {
+ printf("Writing IPL%s area of length %lu at offset %lu\n",
+ (iplsize - ipltailsize) ? " tail" : "",
+ (long unsigned) ipltailsize,
+ (long unsigned) (iplsize - ipltailsize));
+ if (write(ofd, iplbuf, ipltailsize) != ipltailsize) {
+ perror("write");
+ goto error;
+ }
+ }
+
+ printf("Writing the firmware of length %lu at %lu... ",
+ (unsigned long) statbuf.st_size,
+ (unsigned long) iplsize);
+ do {
+ retlen = read(ifd, databuf, 512);
+ if (retlen < 512)
+ memset(databuf+retlen, 0xff, 512-retlen);
+ if (write(ofd, databuf, 512) != 512) {
+ perror("write");
+ goto error;
+ }
+ } while (retlen == 512);
+ printf("Done.\n");
+
+ if (iplbuf != NULL)
+ free(iplbuf);
+ close(ifd);
+ close(ofd);
+ return 0;
+
+error:
+ if (iplbuf != NULL)
+ free(iplbuf);
+ close(ifd);
+ close(ofd);
+ return 1;
+}
diff --git a/misc-utils/docfdisk.c b/misc-utils/docfdisk.c
new file mode 100644
index 0000000..9956de5
--- /dev/null
+++ b/misc-utils/docfdisk.c
@@ -0,0 +1,318 @@
+/*
+ * docfdisk.c: Modify INFTL partition tables
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PROGRAM_NAME "docfdisk"
+
+#define _XOPEN_SOURCE 500 /* for pread/pwrite */
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <string.h>
+
+#include <asm/types.h>
+#include <mtd/mtd-user.h>
+#include <mtd/inftl-user.h>
+#include <mtd_swab.h>
+
+unsigned char *buf;
+
+mtd_info_t meminfo;
+erase_info_t erase;
+int fd;
+struct INFTLMediaHeader *mh;
+
+#define MAXSCAN 10
+
+void show_header(int mhoffs) {
+ int i, unitsize, numunits, bmbits, numpart;
+ int start, end, num, nextunit;
+ unsigned int flags;
+ struct INFTLPartition *ip;
+
+ bmbits = le32_to_cpu(mh->BlockMultiplierBits);
+ printf(" bootRecordID = %s\n"
+ " NoOfBootImageBlocks = %d\n"
+ " NoOfBinaryPartitions = %d\n"
+ " NoOfBDTLPartitions = %d\n"
+ " BlockMultiplierBits = %d\n"
+ " FormatFlags = %d\n"
+ " OsakVersion = %d.%d.%d.%d\n"
+ " PercentUsed = %d\n",
+ mh->bootRecordID, le32_to_cpu(mh->NoOfBootImageBlocks),
+ le32_to_cpu(mh->NoOfBinaryPartitions),
+ le32_to_cpu(mh->NoOfBDTLPartitions),
+ bmbits,
+ le32_to_cpu(mh->FormatFlags),
+ ((unsigned char *) &mh->OsakVersion)[0] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[1] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
+ le32_to_cpu(mh->PercentUsed));
+
+ numpart = le32_to_cpu(mh->NoOfBinaryPartitions) +
+ le32_to_cpu(mh->NoOfBDTLPartitions);
+ unitsize = meminfo.erasesize >> bmbits;
+ numunits = meminfo.size / unitsize;
+ nextunit = mhoffs / unitsize;
+ nextunit++;
+ printf("Unitsize is %d bytes. Device has %d units.\n",
+ unitsize, numunits);
+ if (numunits > 32768) {
+ printf("WARNING: More than 32768 units! Unexpectedly small BlockMultiplierBits.\n");
+ }
+ if (bmbits && (numunits <= 16384)) {
+ printf("NOTICE: Unexpectedly large BlockMultiplierBits.\n");
+ }
+ for (i = 0; i < 4; i++) {
+ ip = &(mh->Partitions[i]);
+ flags = le32_to_cpu(ip->flags);
+ start = le32_to_cpu(ip->firstUnit);
+ end = le32_to_cpu(ip->lastUnit);
+ num = le32_to_cpu(ip->virtualUnits);
+ if (start < nextunit) {
+ printf("ERROR: Overlapping or misordered partitions!\n");
+ }
+ if (start > nextunit) {
+ printf(" Unpartitioned space: %d bytes\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n",
+ (start - nextunit) * unitsize, start - nextunit,
+ nextunit, start - 1);
+ }
+ if (flags & INFTL_BINARY)
+ printf(" Partition %d (BDK):", i+1);
+ else
+ printf(" Partition %d (BDTL):", i+1);
+ printf(" %d bytes\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n"
+ " flags = 0x%x\n"
+ " spareUnits = %d\n",
+ num * unitsize, num, start, end,
+ le32_to_cpu(ip->flags), le32_to_cpu(ip->spareUnits));
+ if (num > (1 + end - start)) {
+ printf("ERROR: virtualUnits not consistent with first/lastUnit!\n");
+ }
+ end++;
+ if (end > nextunit)
+ nextunit = end;
+ if (flags & INFTL_LAST)
+ break;
+ }
+ if (i >= 4) {
+ printf("Odd. Last partition was not marked with INFTL_LAST.\n");
+ i--;
+ }
+ if ((i+1) != numpart) {
+ printf("ERROR: Number of partitions != (NoOfBinaryPartitions + NoOfBDTLPartitions)\n");
+ }
+ if (nextunit > numunits) {
+ printf("ERROR: Partitions appear to extend beyond end of device!\n");
+ }
+ if (nextunit < numunits) {
+ printf(" Unpartitioned space: %d bytes\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n",
+ (numunits - nextunit) * unitsize, numunits - nextunit,
+ nextunit, numunits - 1);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret, i, mhblock, unitsize, block;
+ unsigned int nblocks[4], npart;
+ unsigned int totblocks;
+ struct INFTLPartition *ip;
+ unsigned char *oobbuf;
+ struct mtd_oob_buf oob;
+ char line[20];
+ int mhoffs;
+ struct INFTLMediaHeader *mh2;
+
+ if (argc < 2) {
+ printf(
+ "Usage: %s <mtddevice> [<size1> [<size2> [<size3> [<size4]]]]\n"
+ " Sizes are in device units (run with no sizes to show unitsize and current\n"
+ " partitions). Last size = 0 means go to end of device.\n",
+ PROGRAM_NAME);
+ return 1;
+ }
+
+ npart = argc - 2;
+ if (npart > 4) {
+ printf("Max 4 partitions allowed.\n");
+ return 1;
+ }
+
+ for (i = 0; i < npart; i++) {
+ nblocks[i] = strtoul(argv[2+i], NULL, 0);
+ if (i && !nblocks[i-1]) {
+ printf("No sizes allowed after 0\n");
+ return 1;
+ }
+ }
+
+ // Open and size the device
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ perror("Open flash device");
+ return 1;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ return 1;
+ }
+
+ printf("Device size is %d bytes. Erasesize is %d bytes.\n",
+ meminfo.size, meminfo.erasesize);
+
+ buf = malloc(meminfo.erasesize);
+ oobbuf = malloc((meminfo.erasesize / meminfo.writesize) * meminfo.oobsize);
+ if (!buf || !oobbuf) {
+ printf("Can't malloc block buffer\n");
+ return 1;
+ }
+ oob.length = meminfo.oobsize;
+
+ mh = (struct INFTLMediaHeader *) buf;
+
+ for (mhblock = 0; mhblock < MAXSCAN; mhblock++) {
+ if ((ret = pread(fd, buf, meminfo.erasesize, mhblock * meminfo.erasesize)) < 0) {
+ if (errno == EBADMSG) {
+ printf("ECC error at eraseblock %d\n", mhblock);
+ continue;
+ }
+ perror("Read eraseblock");
+ return 1;
+ }
+ if (ret != meminfo.erasesize) {
+ printf("Short read!\n");
+ return 1;
+ }
+ if (!strcmp("BNAND", mh->bootRecordID)) break;
+ }
+ if (mhblock >= MAXSCAN) {
+ printf("Unable to find INFTL Media Header\n");
+ return 1;
+ }
+ printf("Found INFTL Media Header at block %d:\n", mhblock);
+ mhoffs = mhblock * meminfo.erasesize;
+
+ oob.ptr = oobbuf;
+ oob.start = mhoffs;
+ for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) {
+ if (ioctl(fd, MEMREADOOB, &oob)) {
+ perror("ioctl(MEMREADOOB)");
+ return 1;
+ }
+ oob.start += meminfo.writesize;
+ oob.ptr += meminfo.oobsize;
+ }
+
+ show_header(mhoffs);
+
+ if (!npart)
+ return 0;
+
+ printf("\n-------------------------------------------------------------------------\n");
+
+ unitsize = meminfo.erasesize >> le32_to_cpu(mh->BlockMultiplierBits);
+ totblocks = meminfo.size / unitsize;
+ block = mhoffs / unitsize;
+ block++;
+
+ mh->NoOfBDTLPartitions = 0;
+ mh->NoOfBinaryPartitions = npart;
+
+ for (i = 0; i < npart; i++) {
+ ip = &(mh->Partitions[i]);
+ ip->firstUnit = cpu_to_le32(block);
+ if (!nblocks[i])
+ nblocks[i] = totblocks - block;
+ ip->virtualUnits = cpu_to_le32(nblocks[i]);
+ block += nblocks[i];
+ ip->lastUnit = cpu_to_le32(block-1);
+ ip->spareUnits = 0;
+ ip->flags = cpu_to_le32(INFTL_BINARY);
+ }
+ if (block > totblocks) {
+ printf("Requested partitions extend beyond end of device.\n");
+ return 1;
+ }
+ ip->flags = cpu_to_le32(INFTL_BINARY | INFTL_LAST);
+
+ /* update the spare as well */
+ mh2 = (struct INFTLMediaHeader *) (buf + 4096);
+ memcpy((void *) mh2, (void *) mh, sizeof(struct INFTLMediaHeader));
+
+ printf("\nProposed new Media Header:\n");
+ show_header(mhoffs);
+
+ printf("\nReady to update device. Type 'yes' to proceed, anything else to abort: ");
+ fgets(line, sizeof(line), stdin);
+ if (strcmp("yes\n", line))
+ return 0;
+ printf("Updating MediaHeader...\n");
+
+ erase.start = mhoffs;
+ erase.length = meminfo.erasesize;
+ if (ioctl(fd, MEMERASE, &erase)) {
+ perror("ioctl(MEMERASE)");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+
+ oob.ptr = oobbuf;
+ oob.start = mhoffs;
+ for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) {
+ memset(oob.ptr, 0xff, 6); // clear ECC.
+ if (ioctl(fd, MEMWRITEOOB, &oob)) {
+ perror("ioctl(MEMWRITEOOB)");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+ if ((ret = pwrite(fd, buf, meminfo.writesize, oob.start)) < 0) {
+ perror("Write page");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+ if (ret != meminfo.writesize) {
+ printf("Short write!\n");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+
+ oob.start += meminfo.writesize;
+ oob.ptr += meminfo.oobsize;
+ buf += meminfo.writesize;
+ }
+
+ printf("Success. REBOOT or unload the diskonchip module to update partitions!\n");
+ return 0;
+}
diff --git a/misc-utils/fectest.c b/misc-utils/fectest.c
new file mode 100644
index 0000000..fd577f3
--- /dev/null
+++ b/misc-utils/fectest.c
@@ -0,0 +1,91 @@
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "mcast_image.h"
+#include <crc32.h>
+
+#define ERASE_SIZE 131072
+#define NR_PKTS ((ERASE_SIZE + PKT_SIZE - 1) / PKT_SIZE)
+#define DROPS 8
+
+int main(void)
+{
+ int i, j;
+ unsigned char buf[NR_PKTS * PKT_SIZE];
+ unsigned char pktbuf[(NR_PKTS + DROPS) * PKT_SIZE];
+ struct fec_parms *fec;
+ unsigned char *srcs[NR_PKTS];
+ unsigned char *pkt[NR_PKTS + DROPS];
+ int pktnr[NR_PKTS + DROPS];
+ struct timeval then, now;
+
+ srand(3453);
+ for (i=0; i < sizeof(buf); i++)
+ if (i < ERASE_SIZE)
+ buf[i] = rand();
+ else
+ buf[i] = 0;
+
+ for (i=0; i < NR_PKTS + DROPS; i++)
+ srcs[i] = buf + (i * PKT_SIZE);
+
+ for (i=0; i < NR_PKTS + DROPS; i++) {
+ pkt[i] = malloc(PKT_SIZE);
+ pktnr[i] = -1;
+ }
+ fec = fec_new(NR_PKTS, NR_PKTS + DROPS);
+ if (!fec) {
+ printf("fec_init() failed\n");
+ exit(1);
+ }
+ j = 0;
+ for (i=0; i < NR_PKTS + DROPS; i++) {
+#if 1
+ if (i == 27 || i == 40 || i == 44 || i == 45 || i == 56 )
+ continue;
+#endif
+ if (i == 69 || i == 93 || i == 103)
+ continue;
+ fec_encode(fec, srcs, pkt[j], i, PKT_SIZE);
+ pktnr[j] = i;
+ j++;
+ }
+ gettimeofday(&then, NULL);
+ if (fec_decode(fec, pkt, pktnr, PKT_SIZE)) {
+ printf("Decode failed\n");
+ exit(1);
+ }
+
+ for (i=0; i < NR_PKTS; i++)
+ memcpy(pktbuf + (i*PKT_SIZE), pkt[i], PKT_SIZE);
+ gettimeofday(&now, NULL);
+ now.tv_sec -= then.tv_sec;
+ now.tv_usec -= then.tv_usec;
+ if (now.tv_usec < 0) {
+ now.tv_usec += 1000000;
+ now.tv_sec--;
+ }
+
+ if (memcmp(pktbuf, buf, ERASE_SIZE)) {
+ int fd;
+ printf("Compare failed\n");
+ fd = open("before", O_WRONLY|O_TRUNC|O_CREAT, 0644);
+ if (fd >= 0)
+ write(fd, buf, ERASE_SIZE);
+ close(fd);
+ fd = open("after", O_WRONLY|O_TRUNC|O_CREAT, 0644);
+ if (fd >= 0)
+ write(fd, pktbuf, ERASE_SIZE);
+
+ exit(1);
+ }
+
+ printf("Decoded in %ld.%06lds\n", now.tv_sec, now.tv_usec);
+ return 0;
+}
diff --git a/misc-utils/flash_erase.c b/misc-utils/flash_erase.c
new file mode 100644
index 0000000..933373a
--- /dev/null
+++ b/misc-utils/flash_erase.c
@@ -0,0 +1,295 @@
+/* flash_erase.c -- erase MTD devices
+
+ Copyright (C) 2000 Arcom Control System Ltd
+ Copyright (C) 2010 Mike Frysinger <vapier@gentoo.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#define PROGRAM_NAME "flash_erase"
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <common.h>
+#include <crc32.h>
+#include <libmtd.h>
+
+#include <mtd/mtd-user.h>
+#include <mtd/jffs2-user.h>
+
+static const char *mtd_device;
+
+static int quiet; /* true -- don't output progress */
+static int jffs2; /* format for jffs2 usage */
+static int noskipbad; /* do not skip bad blocks */
+static int unlock; /* unlock sectors before erasing */
+
+static struct jffs2_unknown_node cleanmarker;
+int target_endian = __BYTE_ORDER;
+
+static void show_progress(struct mtd_dev_info *mtd, off_t start, int eb,
+ int eb_start, int eb_cnt)
+{
+ bareverbose(!quiet, "\rErasing %d Kibyte @ %"PRIxoff_t" -- %2i %% complete ",
+ mtd->eb_size / 1024, start, ((eb - eb_start) * 100) / eb_cnt);
+ fflush(stdout);
+}
+
+static void display_help (void)
+{
+ printf("Usage: %s [options] MTD_DEVICE <start offset> <block count>\n"
+ "Erase blocks of the specified MTD device.\n"
+ "Specify a count of 0 to erase to end of device.\n"
+ "\n"
+ " -j, --jffs2 format the device for jffs2\n"
+ " -N, --noskipbad don't skip bad blocks\n"
+ " -u, --unlock unlock sectors before erasing\n"
+ " -q, --quiet do not display progress messages\n"
+ " --silent same as --quiet\n"
+ " --help display this help and exit\n"
+ " --version output version information and exit\n",
+ PROGRAM_NAME);
+}
+
+static void display_version (void)
+{
+ printf("%1$s version " VERSION "\n"
+ "\n"
+ "Copyright (C) 2000 Arcom Control Systems Ltd\n"
+ "\n"
+ "%1$s comes with NO WARRANTY\n"
+ "to the extent permitted by law.\n"
+ "\n"
+ "You may redistribute copies of %1$s\n"
+ "under the terms of the GNU General Public Licence.\n"
+ "See the file `COPYING' for more information.\n",
+ PROGRAM_NAME);
+}
+
+int main(int argc, char *argv[])
+{
+ libmtd_t mtd_desc;
+ struct mtd_dev_info mtd;
+ int fd, clmpos = 0, clmlen = 8;
+ unsigned long long start;
+ unsigned int eb, eb_start, eb_cnt;
+ bool isNAND;
+ int error = 0;
+ off_t offset = 0;
+
+ /*
+ * Process user arguments
+ */
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "jNqu";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"jffs2", no_argument, 0, 'j'},
+ {"noskipbad", no_argument, 0, 'N'},
+ {"quiet", no_argument, 0, 'q'},
+ {"silent", no_argument, 0, 'q'},
+ {"unlock", no_argument, 0, 'u'},
+
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c) {
+ case 0:
+ switch (option_index) {
+ case 0:
+ display_help();
+ return 0;
+ case 1:
+ display_version();
+ return 0;
+ }
+ break;
+ case 'j':
+ jffs2 = 1;
+ break;
+ case 'N':
+ noskipbad = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'u':
+ unlock = 1;
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+ switch (argc - optind) {
+ case 3:
+ mtd_device = argv[optind];
+ start = simple_strtoull(argv[optind + 1], &error);
+ eb_cnt = simple_strtoul(argv[optind + 2], &error);
+ break;
+ default:
+ case 0:
+ errmsg("no MTD device specified");
+ case 1:
+ errmsg("no start erase block specified");
+ case 2:
+ errmsg("no erase block count specified");
+ error = 1;
+ break;
+ }
+ if (error)
+ return errmsg("Try `--help' for more information");
+
+ /*
+ * Locate MTD and prepare for erasure
+ */
+ mtd_desc = libmtd_open();
+ if (mtd_desc == NULL)
+ return errmsg("can't initialize libmtd");
+
+ if ((fd = open(mtd_device, O_RDWR)) < 0)
+ return sys_errmsg("%s", mtd_device);
+
+ if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0)
+ return errmsg("mtd_get_dev_info failed");
+
+ if (jffs2 && mtd.type == MTD_MLCNANDFLASH)
+ return errmsg("JFFS2 cannot support MLC NAND.");
+
+ eb_start = start / mtd.eb_size;
+
+ isNAND = mtd.type == MTD_NANDFLASH || mtd.type == MTD_MLCNANDFLASH;
+
+ if (jffs2) {
+ cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+ if (!isNAND)
+ cleanmarker.totlen = cpu_to_je32(sizeof(cleanmarker));
+ else {
+ struct nand_oobinfo oobinfo;
+
+ if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0)
+ return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device);
+
+ /* Check for autoplacement */
+ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
+ /* Get the position of the free bytes */
+ if (!oobinfo.oobfree[0][1])
+ return errmsg(" Eeep. Autoplacement selected and no empty space in oob");
+ clmpos = oobinfo.oobfree[0][0];
+ clmlen = oobinfo.oobfree[0][1];
+ if (clmlen > 8)
+ clmlen = 8;
+ } else {
+ /* Legacy mode */
+ switch (mtd.oob_size) {
+ case 8:
+ clmpos = 6;
+ clmlen = 2;
+ break;
+ case 16:
+ clmpos = 8;
+ clmlen = 8;
+ break;
+ case 64:
+ clmpos = 16;
+ clmlen = 8;
+ break;
+ }
+ }
+ cleanmarker.totlen = cpu_to_je32(8);
+ }
+ cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4));
+ }
+
+ /*
+ * Now do the actual erasing of the MTD device
+ */
+ if (eb_cnt == 0)
+ eb_cnt = (mtd.size / mtd.eb_size) - eb_start;
+
+ for (eb = eb_start; eb < eb_start + eb_cnt; eb++) {
+ offset = (off_t)eb * mtd.eb_size;
+
+ if (!noskipbad) {
+ int ret = mtd_is_bad(&mtd, fd, eb);
+ if (ret > 0) {
+ verbose(!quiet, "Skipping bad block at %08"PRIxoff_t, offset);
+ continue;
+ } else if (ret < 0) {
+ if (errno == EOPNOTSUPP) {
+ noskipbad = 1;
+ if (isNAND)
+ return errmsg("%s: Bad block check not available", mtd_device);
+ } else
+ return sys_errmsg("%s: MTD get bad block failed", mtd_device);
+ }
+ }
+
+ show_progress(&mtd, offset, eb, eb_start, eb_cnt);
+
+ if (unlock) {
+ if (mtd_unlock(&mtd, fd, eb) != 0) {
+ sys_errmsg("%s: MTD unlock failure", mtd_device);
+ continue;
+ }
+ }
+
+ if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) {
+ sys_errmsg("%s: MTD Erase failure", mtd_device);
+ continue;
+ }
+
+ /* format for JFFS2 ? */
+ if (!jffs2)
+ continue;
+
+ /* write cleanmarker */
+ if (isNAND) {
+ if (mtd_write_oob(mtd_desc, &mtd, fd, (uint64_t)offset + clmpos, clmlen, &cleanmarker) != 0) {
+ sys_errmsg("%s: MTD writeoob failure", mtd_device);
+ continue;
+ }
+ } else {
+ if (pwrite(fd, &cleanmarker, sizeof(cleanmarker), (loff_t)offset) != sizeof(cleanmarker)) {
+ sys_errmsg("%s: MTD write failure", mtd_device);
+ continue;
+ }
+ }
+ verbose(!quiet, " Cleanmarker written at %"PRIxoff_t, offset);
+ }
+ show_progress(&mtd, offset, eb, eb_start, eb_cnt);
+ bareverbose(!quiet, "\n");
+
+ return 0;
+}
diff --git a/misc-utils/flash_eraseall b/misc-utils/flash_eraseall
new file mode 100755
index 0000000..c5539b3
--- /dev/null
+++ b/misc-utils/flash_eraseall
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "${0##*/} has been replaced by \`flash_erase <mtddev> 0 0\`; please use it" 1>&2
+[ $# -ne 0 ] && set -- "$@" 0 0
+exec flash_erase "$@"
diff --git a/misc-utils/flash_lock.c b/misc-utils/flash_lock.c
new file mode 100644
index 0000000..33f76c7
--- /dev/null
+++ b/misc-utils/flash_lock.c
@@ -0,0 +1,8 @@
+/*
+ * flash_{lock,unlock}
+ *
+ * utilities for locking/unlocking sectors of flash devices
+ */
+
+#define PROGRAM_NAME "flash_lock"
+#include "flash_unlock.c"
diff --git a/misc-utils/flash_otp_dump.c b/misc-utils/flash_otp_dump.c
new file mode 100644
index 0000000..f0c0fb9
--- /dev/null
+++ b/misc-utils/flash_otp_dump.c
@@ -0,0 +1,56 @@
+/*
+ * flash_otp_dump.c -- display One-Time-Programm data
+ */
+
+#define PROGRAM_NAME "flash_otp_dump"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, i, offset, ret;
+ unsigned char buf[16];
+
+ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) {
+ fprintf(stderr,"Usage: %s [ -f | -u ] <device>\n", PROGRAM_NAME);
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ printf("OTP %s data for %s\n",
+ argv[1][1] == 'f' ? "factory" : "user", argv[2]);
+ offset = 0;
+ while ((ret = read(fd, buf, sizeof(buf)))) {
+ if (ret < 0) {
+ perror("read()");
+ return errno;
+ }
+ printf("0x%04x:", offset);
+ for (i = 0; i < ret; i++)
+ printf(" %02x", buf[i]);
+ printf("\n");
+ offset += ret;
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/misc-utils/flash_otp_info.c b/misc-utils/flash_otp_info.c
new file mode 100644
index 0000000..2061797
--- /dev/null
+++ b/misc-utils/flash_otp_info.c
@@ -0,0 +1,65 @@
+/*
+ * flash_otp_info.c -- print info about One-Time-Programm data
+ */
+
+#define PROGRAM_NAME "flash_otp_info"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, i, ret;
+
+ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) {
+ fprintf(stderr,"Usage: %s [ -f | -u ] <device>\n", PROGRAM_NAME);
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ ret = ioctl(fd, OTPGETREGIONCOUNT, &val);
+ if (ret < 0) {
+ perror("OTPGETREGIONCOUNT");
+ return errno;
+ }
+
+ printf("Number of OTP %s blocks on %s: %d\n",
+ argv[1][1] == 'f' ? "factory" : "user", argv[2], val);
+
+ if (val > 0) {
+ struct otp_info info[val];
+
+ ret = ioctl(fd, OTPGETREGIONINFO, &info);
+ if (ret < 0) {
+ perror("OTPGETREGIONCOUNT");
+ return errno;
+ }
+
+ for (i = 0; i < val; i++)
+ printf("block %2d: offset = 0x%04x "
+ "size = %2d bytes %s\n",
+ i, info[i].start, info[i].length,
+ info[i].locked ? "[locked]" : "[unlocked]");
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/misc-utils/flash_otp_lock.c b/misc-utils/flash_otp_lock.c
new file mode 100644
index 0000000..3c39a2d
--- /dev/null
+++ b/misc-utils/flash_otp_lock.c
@@ -0,0 +1,72 @@
+/*
+ * flash_otp_lock.c -- lock area of One-Time-Program data
+ */
+
+#define PROGRAM_NAME "flash_otp_lock"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+#include "common.h"
+
+int main(int argc,char *argv[])
+{
+ int fd, val, ret, offset, size;
+ char *p;
+
+ if (argc != 5 || strcmp(argv[1], "-u")) {
+ fprintf(stderr, "Usage: %s -u <device> <offset> <size>\n", PROGRAM_NAME);
+ fprintf(stderr, "offset and size must match on OTP region boundaries\n");
+ fprintf(stderr, "CAUTION! ONCE LOCKED, OTP REGIONS CAN'T BE UNLOCKED!\n");
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_WRONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ offset = strtoul(argv[3], &p, 0);
+ if (argv[3][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME);
+ return ERANGE;
+ }
+
+ size = strtoul(argv[4], &p, 0);
+ if (argv[4][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad size value\n", PROGRAM_NAME);
+ return ERANGE;
+ }
+
+ printf("About to lock OTP user data on %s from 0x%x to 0x%x\n",
+ argv[2], offset, offset + size);
+ if (prompt("Are you sure?", false)) {
+ struct otp_info info;
+ info.start = offset;
+ info.length = size;
+ ret = ioctl(fd, OTPLOCK, &info);
+ if (ret < 0) {
+ perror("OTPLOCK");
+ return errno;
+ }
+ printf("Done.\n");
+ } else {
+ printf("Aborted\n");
+ }
+
+ return 0;
+}
diff --git a/misc-utils/flash_otp_write.c b/misc-utils/flash_otp_write.c
new file mode 100644
index 0000000..111318d
--- /dev/null
+++ b/misc-utils/flash_otp_write.c
@@ -0,0 +1,122 @@
+/*
+ * flash_otp_write.c -- write One-Time-Program data
+ */
+
+#define PROGRAM_NAME "flash_otp_write"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <common.h>
+#include <mtd/mtd-user.h>
+
+ssize_t xread(int fd, void *buf, size_t count)
+{
+ ssize_t ret, done = 0;
+
+retry:
+ ret = read(fd, buf + done, count - done);
+ if (ret < 0)
+ return ret;
+
+ done += ret;
+
+ if (ret == 0 /* EOF */ || done == count)
+ return done;
+ else
+ goto retry;
+}
+
+int main(int argc,char *argv[])
+{
+ int fd, val, ret, size, wrote, len;
+ mtd_info_t mtdInfo;
+ off_t offset;
+ char *p, buf[2048];
+
+ if (argc != 4 || strcmp(argv[1], "-u")) {
+ fprintf(stderr, "Usage: %s -u <device> <offset>\n", PROGRAM_NAME);
+ fprintf(stderr, "the raw data to write should be provided on stdin\n");
+ fprintf(stderr, "CAUTION! ONCE SET TO 0, OTP DATA BITS CAN'T BE ERASED!\n");
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_WRONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &mtdInfo)) {
+ perror("MEMGETINFO");
+ return errno;
+ }
+
+ offset = (off_t)strtoull(argv[3], &p, 0);
+ if (argv[3][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME);
+ return ERANGE;
+ }
+
+ if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
+ perror("lseek()");
+ return errno;
+ }
+
+ printf("Writing OTP user data on %s at offset 0x%"PRIxoff_t"\n", argv[2], offset);
+
+ if (mtd_type_is_nand_user(&mtdInfo))
+ len = mtdInfo.writesize;
+ else
+ len = 256;
+
+ if (len > sizeof(buf)) {
+ printf("huh, writesize (%d) bigger than buffer (%zu)\n",
+ len, sizeof(buf));
+ return ENOMEM;
+ }
+
+ wrote = 0;
+ while ((size = xread(0, buf, len))) {
+ if (size < 0) {
+ perror("read()");
+ return errno;
+ }
+ p = buf;
+ while (size > 0) {
+ if (mtd_type_is_nand_user(&mtdInfo)) {
+ /* Fill remain buffers with 0xff */
+ memset(buf + size, 0xff, mtdInfo.writesize - size);
+ size = mtdInfo.writesize;
+ }
+ ret = write(fd, p, size);
+ if (ret < 0) {
+ perror("write()");
+ return errno;
+ }
+ if (ret == 0) {
+ printf("write() returned 0 after writing %d bytes\n", wrote);
+ return 0;
+ }
+ p += ret;
+ wrote += ret;
+ size -= ret;
+ }
+ }
+
+ printf("Wrote %d bytes of OTP user data\n", wrote);
+ return 0;
+}
diff --git a/misc-utils/flash_unlock.c b/misc-utils/flash_unlock.c
new file mode 100644
index 0000000..f51870a
--- /dev/null
+++ b/misc-utils/flash_unlock.c
@@ -0,0 +1,220 @@
+/*
+ * flash_{lock,unlock}
+ *
+ * utilities for locking/unlocking sectors of flash devices
+ */
+
+enum flash_lock_request {
+ REQUEST_LOCK,
+ REQUEST_UNLOCK,
+ REQUEST_ISLOCKED,
+};
+
+#ifndef PROGRAM_NAME
+#define PROGRAM_NAME "flash_unlock"
+#define DEFAULT_REQUEST REQUEST_UNLOCK
+#else
+#define DEFAULT_REQUEST REQUEST_LOCK
+#endif
+
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include "common.h"
+#include <mtd/mtd-user.h>
+
+static const char *flash_msg[] = {
+ [ REQUEST_LOCK ] = "lock",
+ [ REQUEST_UNLOCK ] = "unlock",
+ [ REQUEST_ISLOCKED ] = "check lock status",
+};
+
+static void usage(int status)
+{
+ fprintf(status ? stderr : stdout,
+ "Utility to lock, unlock, or check the lock status of the flash.\n"
+ "Default action: %s\n"
+ "\n"
+ "Usage: %s [options] [--] <mtd device> [offset [block count]]\n"
+ "\n"
+ "Options:\n"
+ " -h --help Display this help and exit\n"
+ " --version Display version information and exit\n"
+ " -i --islocked Check if flash region is locked\n"
+ " -l --lock Lock a region of flash\n"
+ " -u --unlock Unlock a region of flash\n"
+ "\n"
+ "If offset is not specified, it defaults to 0.\n"
+ "If block count is not specified, it defaults to all blocks.\n"
+ "A block count of -1 means all blocks.\n",
+ flash_msg[DEFAULT_REQUEST],
+ PROGRAM_NAME);
+ exit(status);
+}
+
+static const char short_opts[] = "hilu";
+static const struct option long_opts[] = {
+ { "help", no_argument, 0, 'h' },
+ { "islocked", no_argument, 0, 'i' },
+ { "lock", no_argument, 0, 'l' },
+ { "unlock", no_argument, 0, 'u' },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 },
+};
+
+/* Program arguments */
+static const char *dev, *offs_s, *count_s;
+static enum flash_lock_request req = DEFAULT_REQUEST;
+
+static void process_args(int argc, char *argv[])
+{
+ int arg_idx;
+ int req_set = 0;
+
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, short_opts, long_opts, NULL);
+ if (c == EOF)
+ break;
+
+ switch (c) {
+ case 'h':
+ usage(0);
+ break;
+ case 'i':
+ req = REQUEST_ISLOCKED;
+ req_set++;
+ break;
+ case 'l':
+ req = REQUEST_LOCK;
+ req_set++;
+ break;
+ case 'u':
+ req = REQUEST_UNLOCK;
+ req_set++;
+ break;
+ case 'v':
+ common_print_version();
+ exit(0);
+ default:
+ usage(1);
+ break;
+ }
+ }
+
+ if (req_set > 1) {
+ errmsg("cannot specify more than one lock/unlock/islocked option");
+ usage(1);
+ }
+
+ arg_idx = optind;
+
+ /* Sanity checks */
+ if (argc - arg_idx < 1) {
+ errmsg("too few arguments");
+ usage(1);
+ } else if (argc - arg_idx > 3) {
+ errmsg("too many arguments");
+ usage(1);
+ }
+
+ /* First non-option argument */
+ dev = argv[arg_idx++];
+
+ /* Second non-option argument */
+ if (arg_idx < argc)
+ offs_s = argv[arg_idx++];
+ else
+ offs_s = NULL;
+
+ /* Third non-option argument */
+ if (arg_idx < argc)
+ count_s = argv[arg_idx++];
+ else
+ count_s = NULL;
+
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, request;
+ struct mtd_info_user mtdInfo;
+ struct erase_info_user mtdLockInfo;
+ long count;
+ int ret = 0;
+
+ process_args(argc, argv);
+
+ /* Get the device info to compare to command line sizes */
+ fd = open(dev, O_RDWR);
+ if (fd < 0)
+ sys_errmsg_die("could not open: %s", dev);
+
+ if (ioctl(fd, MEMGETINFO, &mtdInfo))
+ sys_errmsg_die("could not get mtd info: %s", dev);
+
+ /* Make sure user options are valid */
+ if (offs_s) {
+ mtdLockInfo.start = simple_strtol(offs_s, &ret);
+ if (ret)
+ errmsg_die("bad offset");
+ } else {
+ mtdLockInfo.start = 0;
+ }
+ if (mtdLockInfo.start >= mtdInfo.size)
+ errmsg_die("%#x is beyond device size %#x",
+ mtdLockInfo.start, mtdInfo.size);
+
+ if (count_s) {
+ count = simple_strtol(count_s, &ret);
+ if (ret)
+ errmsg_die("bad count");
+ if (count == -1)
+ mtdLockInfo.length = mtdInfo.size;
+ else
+ mtdLockInfo.length = mtdInfo.erasesize * count;
+ } else {
+ mtdLockInfo.length = mtdInfo.size;
+ }
+ if (mtdLockInfo.start + mtdLockInfo.length > mtdInfo.size)
+ errmsg_die("range is more than device supports: %#x + %#x > %#x",
+ mtdLockInfo.start, mtdLockInfo.length, mtdInfo.size);
+
+ /* Finally do the operation */
+ switch (req) {
+ case REQUEST_LOCK:
+ request = MEMLOCK;
+ break;
+ case REQUEST_UNLOCK:
+ request = MEMUNLOCK;
+ break;
+ case REQUEST_ISLOCKED:
+ request = MEMISLOCKED;
+ break;
+ default:
+ errmsg_die("unknown request type: %d", req);
+ break;
+ }
+ ret = ioctl(fd, request, &mtdLockInfo);
+ if (ret < 0)
+ sys_errmsg_die("could not %s device: %s\n",
+ flash_msg[req], dev);
+
+ if (req == REQUEST_ISLOCKED) {
+ printf("Device: %s\n", dev);
+ printf("Start: %#0x\n", mtdLockInfo.start);
+ printf("Len: %#0x\n", mtdLockInfo.length);
+ printf("Lock status: %s\n", ret ? "locked" : "unlocked");
+ printf("Return code: %d\n", ret);
+ }
+
+ return 0;
+}
diff --git a/misc-utils/flashcp.c b/misc-utils/flashcp.c
new file mode 100644
index 0000000..86334ac
--- /dev/null
+++ b/misc-utils/flashcp.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2d3D, Inc.
+ * Written by Abraham vd Merwe <abraham@2d3d.co.za>
+ * All rights reserved.
+ *
+ * Renamed to flashcp.c to avoid conflicts with fcp from fsh package
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of other contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define PROGRAM_NAME "flashcp"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <mtd/mtd-user.h>
+#include <getopt.h>
+
+typedef int bool;
+#define true 1
+#define false 0
+
+#define EXIT_FAILURE 1
+#define EXIT_SUCCESS 0
+
+/* for debugging purposes only */
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); }
+#else
+#undef DEBUG
+#define DEBUG(fmt,args...)
+#endif
+
+#define KB(x) ((x) / 1024)
+#define PERCENTAGE(x,total) (((x) * 100) / (total))
+
+/* size of read/write buffer */
+#define BUFSIZE (10 * 1024)
+
+/* cmd-line flags */
+#define FLAG_NONE 0x00
+#define FLAG_VERBOSE 0x01
+#define FLAG_HELP 0x02
+#define FLAG_FILENAME 0x04
+#define FLAG_DEVICE 0x08
+
+/* error levels */
+#define LOG_NORMAL 1
+#define LOG_ERROR 2
+
+static void log_printf (int level,const char *fmt, ...)
+{
+ FILE *fp = level == LOG_NORMAL ? stdout : stderr;
+ va_list ap;
+ va_start (ap,fmt);
+ vfprintf (fp,fmt,ap);
+ va_end (ap);
+ fflush (fp);
+}
+
+static void showusage(bool error)
+{
+ int level = error ? LOG_ERROR : LOG_NORMAL;
+
+ log_printf (level,
+ "\n"
+ "Flash Copy - Written by Abraham van der Merwe <abraham@2d3d.co.za>\n"
+ "\n"
+ "usage: %1$s [ -v | --verbose ] <filename> <device>\n"
+ " %1$s -h | --help\n"
+ "\n"
+ " -h | --help Show this help message\n"
+ " -v | --verbose Show progress reports\n"
+ " <filename> File which you want to copy to flash\n"
+ " <device> Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n"
+ "\n",
+ PROGRAM_NAME);
+
+ exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int safe_open (const char *pathname,int flags)
+{
+ int fd;
+
+ fd = open (pathname,flags);
+ if (fd < 0)
+ {
+ log_printf (LOG_ERROR,"While trying to open %s",pathname);
+ if (flags & O_RDWR)
+ log_printf (LOG_ERROR," for read/write access");
+ else if (flags & O_RDONLY)
+ log_printf (LOG_ERROR," for read access");
+ else if (flags & O_WRONLY)
+ log_printf (LOG_ERROR," for write access");
+ log_printf (LOG_ERROR,": %m\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return (fd);
+}
+
+static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose)
+{
+ ssize_t result;
+
+ result = read (fd,buf,count);
+ if (count != result)
+ {
+ if (verbose) log_printf (LOG_NORMAL,"\n");
+ if (result < 0)
+ {
+ log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+ log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename);
+ exit (EXIT_FAILURE);
+ }
+}
+
+static void safe_rewind (int fd,const char *filename)
+{
+ if (lseek (fd,0L,SEEK_SET) < 0)
+ {
+ log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+}
+
+/******************************************************************************/
+
+static int dev_fd = -1,fil_fd = -1;
+
+static void cleanup (void)
+{
+ if (dev_fd > 0) close (dev_fd);
+ if (fil_fd > 0) close (fil_fd);
+}
+
+int main (int argc,char *argv[])
+{
+ const char *filename = NULL,*device = NULL;
+ int i,flags = FLAG_NONE;
+ ssize_t result;
+ size_t size,written;
+ struct mtd_info_user mtd;
+ struct erase_info_user erase;
+ struct stat filestat;
+ unsigned char src[BUFSIZE],dest[BUFSIZE];
+
+ /*********************
+ * parse cmd-line
+ *****************/
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hv";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"verbose", no_argument, 0, 'v'},
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ flags |= FLAG_HELP;
+ DEBUG("Got FLAG_HELP\n");
+ break;
+ case 'v':
+ flags |= FLAG_VERBOSE;
+ DEBUG("Got FLAG_VERBOSE\n");
+ break;
+ default:
+ DEBUG("Unknown parameter: %s\n",argv[option_index]);
+ showusage(true);
+ }
+ }
+ if (optind+2 == argc) {
+ flags |= FLAG_FILENAME;
+ filename = argv[optind];
+ DEBUG("Got filename: %s\n",filename);
+
+ flags |= FLAG_DEVICE;
+ device = argv[optind+1];
+ DEBUG("Got device: %s\n",device);
+ }
+
+ if (flags & FLAG_HELP || device == NULL)
+ showusage(flags != FLAG_HELP);
+
+ atexit (cleanup);
+
+ /* get some info about the flash device */
+ dev_fd = safe_open (device,O_SYNC | O_RDWR);
+ if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0)
+ {
+ DEBUG("ioctl(): %m\n");
+ log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* get some info about the file we want to copy */
+ fil_fd = safe_open (filename,O_RDONLY);
+ if (fstat (fil_fd,&filestat) < 0)
+ {
+ log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+
+ /* does it fit into the device/partition? */
+ if (filestat.st_size > mtd.size)
+ {
+ log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device);
+ exit (EXIT_FAILURE);
+ }
+
+ /*****************************************************
+ * erase enough blocks so that we can write the file *
+ *****************************************************/
+
+#warning "Check for smaller erase regions"
+
+ erase.start = 0;
+ erase.length = (filestat.st_size + mtd.erasesize - 1) / mtd.erasesize;
+ erase.length *= mtd.erasesize;
+
+ if (flags & FLAG_VERBOSE)
+ {
+ /* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */
+ int blocks = erase.length / mtd.erasesize;
+ erase.length = mtd.erasesize;
+ log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks);
+ for (i = 1; i <= blocks; i++)
+ {
+ log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks));
+ if (ioctl (dev_fd,MEMERASE,&erase) < 0)
+ {
+ log_printf (LOG_NORMAL,"\n");
+ log_printf (LOG_ERROR,
+ "While erasing blocks 0x%.8x-0x%.8x on %s: %m\n",
+ (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
+ exit (EXIT_FAILURE);
+ }
+ erase.start += mtd.erasesize;
+ }
+ log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks);
+ }
+ else
+ {
+ /* if not, erase the whole chunk in one shot */
+ if (ioctl (dev_fd,MEMERASE,&erase) < 0)
+ {
+ log_printf (LOG_ERROR,
+ "While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n",
+ (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
+ exit (EXIT_FAILURE);
+ }
+ }
+ DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size);
+
+ /**********************************
+ * write the entire file to flash *
+ **********************************/
+
+ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size));
+ size = filestat.st_size;
+ i = BUFSIZE;
+ written = 0;
+ while (size)
+ {
+ if (size < BUFSIZE) i = size;
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)",
+ KB (written + i),
+ KB (filestat.st_size),
+ PERCENTAGE (written + i,filestat.st_size));
+
+ /* read from filename */
+ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
+
+ /* write to device */
+ result = write (dev_fd,src,i);
+ if (i != result)
+ {
+ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
+ if (result < 0)
+ {
+ log_printf (LOG_ERROR,
+ "While writing data to 0x%.8x-0x%.8x on %s: %m\n",
+ written,written + i,device);
+ exit (EXIT_FAILURE);
+ }
+ log_printf (LOG_ERROR,
+ "Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n",
+ written,written + i,device,written + result,filestat.st_size);
+ exit (EXIT_FAILURE);
+ }
+
+ written += i;
+ size -= i;
+ }
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,
+ "\rWriting data: %luk/%luk (100%%)\n",
+ KB (filestat.st_size),
+ KB (filestat.st_size));
+ DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size);
+
+ /**********************************
+ * verify that flash == file data *
+ **********************************/
+
+ safe_rewind (fil_fd,filename);
+ safe_rewind (dev_fd,device);
+ size = filestat.st_size;
+ i = BUFSIZE;
+ written = 0;
+ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size));
+ while (size)
+ {
+ if (size < BUFSIZE) i = size;
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,
+ "\rVerifying data: %dk/%luk (%lu%%)",
+ KB (written + i),
+ KB (filestat.st_size),
+ PERCENTAGE (written + i,filestat.st_size));
+
+ /* read from filename */
+ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
+
+ /* read from device */
+ safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE);
+
+ /* compare buffers */
+ if (memcmp (src,dest,i))
+ {
+ log_printf (LOG_ERROR,
+ "File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n",
+ written,written + i);
+ exit (EXIT_FAILURE);
+ }
+
+ written += i;
+ size -= i;
+ }
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,
+ "\rVerifying data: %luk/%luk (100%%)\n",
+ KB (filestat.st_size),
+ KB (filestat.st_size));
+ DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/misc-utils/ftl_check.c b/misc-utils/ftl_check.c
new file mode 100644
index 0000000..0eada8f
--- /dev/null
+++ b/misc-utils/ftl_check.c
@@ -0,0 +1,217 @@
+/* Ported to MTD system.
+ * Based on:
+ */
+/*======================================================================
+
+ Utility to create an FTL partition in a memory region
+
+ ftl_check.c 1.10 1999/10/25 20:01:35
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+ ======================================================================*/
+
+#define PROGRAM_NAME "ftl_check"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <mtd/mtd-user.h>
+#include <mtd/ftl-user.h>
+#include <mtd_swab.h>
+
+#include "common.h"
+
+/*====================================================================*/
+
+static void print_size(u_int s)
+{
+ if ((s > 0x100000) && ((s % 0x100000) == 0))
+ printf("%d mb", s / 0x100000);
+ else if ((s > 0x400) && ((s % 0x400) == 0))
+ printf("%d kb", s / 0x400);
+ else
+ printf("%d bytes", s);
+}
+
+/*====================================================================*/
+
+static void check_partition(int fd)
+{
+ mtd_info_t mtd;
+ erase_unit_header_t hdr, hdr2;
+ off_t i;
+ u_int j, nbam, *bam;
+ int control, data, free, deleted;
+
+ /* Get partition size, block size */
+ if (ioctl(fd, MEMGETINFO, &mtd) != 0) {
+ perror("get info failed");
+ return;
+ }
+
+ printf("Memory region info:\n");
+ printf(" Region size = ");
+ print_size(mtd.size);
+ printf(" Erase block size = ");
+ print_size(mtd.erasesize);
+ printf("\n\n");
+
+ for (i = 0; i < mtd.size/mtd.erasesize; i++) {
+ if (lseek(fd, (i * mtd.erasesize), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ read(fd, &hdr, sizeof(hdr));
+ if ((le32_to_cpu(hdr.FormattedSize) > 0) &&
+ (le32_to_cpu(hdr.FormattedSize) <= mtd.size) &&
+ (le16_to_cpu(hdr.NumEraseUnits) > 0) &&
+ (le16_to_cpu(hdr.NumEraseUnits) <= mtd.size/mtd.erasesize))
+ break;
+ }
+ if (i == mtd.size/mtd.erasesize) {
+ fprintf(stderr, "No valid erase unit headers!\n");
+ return;
+ }
+
+ printf("Partition header:\n");
+ printf(" Formatted size = ");
+ print_size(le32_to_cpu(hdr.FormattedSize));
+ printf(", erase units = %d, transfer units = %d\n",
+ le16_to_cpu(hdr.NumEraseUnits), hdr.NumTransferUnits);
+ printf(" Erase unit size = ");
+ print_size(1 << hdr.EraseUnitSize);
+ printf(", virtual block size = ");
+ print_size(1 << hdr.BlockSize);
+ printf("\n");
+
+ /* Create basic block allocation table for control blocks */
+ nbam = (mtd.erasesize >> hdr.BlockSize);
+ bam = malloc(nbam * sizeof(u_int));
+
+ for (i = 0; i < le16_to_cpu(hdr.NumEraseUnits); i++) {
+ if (lseek(fd, (i << hdr.EraseUnitSize), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (read(fd, &hdr2, sizeof(hdr2)) == -1) {
+ perror("read failed");
+ break;
+ }
+ printf("\nErase unit %"PRIdoff_t":\n", i);
+ if ((hdr2.FormattedSize != hdr.FormattedSize) ||
+ (hdr2.NumEraseUnits != hdr.NumEraseUnits) ||
+ (hdr2.SerialNumber != hdr.SerialNumber))
+ printf(" Erase unit header is corrupt.\n");
+ else if (le16_to_cpu(hdr2.LogicalEUN) == 0xffff)
+ printf(" Transfer unit, erase count = %d\n", le32_to_cpu(hdr2.EraseCount));
+ else {
+ printf(" Logical unit %d, erase count = %d\n",
+ le16_to_cpu(hdr2.LogicalEUN), le32_to_cpu(hdr2.EraseCount));
+ if (lseek(fd, (i << hdr.EraseUnitSize)+le32_to_cpu(hdr.BAMOffset),
+ SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (read(fd, bam, nbam * sizeof(u_int)) == -1) {
+ perror("read failed");
+ break;
+ }
+ free = deleted = control = data = 0;
+ for (j = 0; j < nbam; j++) {
+ if (BLOCK_FREE(le32_to_cpu(bam[j])))
+ free++;
+ else if (BLOCK_DELETED(le32_to_cpu(bam[j])))
+ deleted++;
+ else switch (BLOCK_TYPE(le32_to_cpu(bam[j]))) {
+ case BLOCK_CONTROL: control++; break;
+ case BLOCK_DATA: data++; break;
+ default: break;
+ }
+ }
+ printf(" Block allocation: %d control, %d data, %d free,"
+ " %d deleted\n", control, data, free, deleted);
+ }
+ }
+} /* format_partition */
+
+/* Show usage information */
+void showusage(void)
+{
+ fprintf(stderr, "usage: %s device\n", PROGRAM_NAME);
+}
+
+/*====================================================================*/
+
+int main(int argc, char *argv[])
+{
+ int optch, errflg, fd;
+ struct stat buf;
+
+ errflg = 0;
+ while ((optch = getopt(argc, argv, "h")) != -1) {
+ switch (optch) {
+ case 'h':
+ errflg = 1; break;
+ default:
+ errflg = -1; break;
+ }
+ }
+ if (errflg || (optind != argc-1)) {
+ showusage();
+ exit(errflg > 0 ? 0 : EXIT_FAILURE);
+ }
+
+ if (stat(argv[optind], &buf) != 0) {
+ perror("status check failed");
+ exit(EXIT_FAILURE);
+ }
+ if (!(buf.st_mode & S_IFCHR)) {
+ fprintf(stderr, "%s is not a character special device\n",
+ argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ fd = open(argv[optind], O_RDONLY);
+ if (fd == -1) {
+ perror("open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ check_partition(fd);
+ close(fd);
+
+ exit(EXIT_SUCCESS);
+ return 0;
+}
diff --git a/misc-utils/ftl_format.c b/misc-utils/ftl_format.c
new file mode 100644
index 0000000..b58677f
--- /dev/null
+++ b/misc-utils/ftl_format.c
@@ -0,0 +1,324 @@
+/* Ported to MTD system.
+ * Based on:
+ */
+/*======================================================================
+
+ Utility to create an FTL partition in a memory region
+
+ ftl_format.c 1.13 1999/10/25 20:01:35
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+ ======================================================================*/
+
+#define PROGRAM_NAME "ftl_format"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <mtd/mtd-user.h>
+#include <mtd/ftl-user.h>
+#include <mtd_swab.h>
+#include "common.h"
+
+/*====================================================================*/
+
+static void print_size(u_int s)
+{
+ if ((s > 0x100000) && ((s % 0x100000) == 0))
+ printf("%d mb", s / 0x100000);
+ else if ((s > 0x400) && ((s % 0x400) == 0))
+ printf("%d kb", s / 0x400);
+ else
+ printf("%d bytes", s);
+}
+
+/*====================================================================*/
+
+static const char LinkTarget[] = {
+ 0x13, 0x03, 'C', 'I', 'S'
+};
+static const char DataOrg[] = {
+ 0x46, 0x39, 0x00, 'F', 'T', 'L', '1', '0', '0', 0x00
+};
+
+static void build_header(erase_unit_header_t *hdr, u_int RegionSize,
+ u_int BlockSize, u_int Spare, int Reserve,
+ u_int BootSize)
+{
+ u_int i, BootUnits, nbam, __FormattedSize;
+
+ /* Default everything to the erased state */
+ memset(hdr, 0xff, sizeof(*hdr));
+ memcpy(hdr->LinkTargetTuple, LinkTarget, 5);
+ memcpy(hdr->DataOrgTuple, DataOrg, 10);
+ hdr->EndTuple[0] = hdr->EndTuple[1] = 0xff;
+ BootSize = (BootSize + (BlockSize-1)) & ~(BlockSize-1);
+ BootUnits = BootSize / BlockSize;
+
+ /* We only support 512-byte blocks */
+ hdr->BlockSize = 9;
+ hdr->EraseUnitSize = 0;
+ for (i = BlockSize; i > 1; i >>= 1)
+ hdr->EraseUnitSize++;
+ hdr->EraseCount = cpu_to_le32(0);
+ hdr->FirstPhysicalEUN = cpu_to_le16(BootUnits);
+ hdr->NumEraseUnits = cpu_to_le16((RegionSize - BootSize) >> hdr->EraseUnitSize);
+ hdr->NumTransferUnits = Spare;
+ __FormattedSize = RegionSize - ((Spare + BootUnits) << hdr->EraseUnitSize);
+ /* Leave a little bit of space between the CIS and BAM */
+ hdr->BAMOffset = cpu_to_le32(0x80);
+ /* Adjust size to account for BAM space */
+ nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int)
+ + le32_to_cpu(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize;
+
+ __FormattedSize -=
+ (le16_to_cpu(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize);
+ __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff);
+
+ hdr->FormattedSize = cpu_to_le32(__FormattedSize);
+
+ /* hdr->FirstVMAddress defaults to erased state */
+ hdr->NumVMPages = cpu_to_le16(0);
+ hdr->Flags = 0;
+ /* hdr->Code defaults to erased state */
+ hdr->SerialNumber = cpu_to_le32(time(NULL));
+ /* hdr->AltEUHOffset defaults to erased state */
+
+} /* build_header */
+
+/*====================================================================*/
+
+static int format_partition(int fd, int quiet, int interrogate,
+ u_int spare, int reserve, u_int bootsize)
+{
+ mtd_info_t mtd;
+ erase_info_t erase;
+ erase_unit_header_t hdr;
+ u_int step, lun, i, nbam, *bam;
+
+ /* Get partition size, block size */
+ if (ioctl(fd, MEMGETINFO, &mtd) != 0) {
+ perror("get info failed");
+ return -1;
+ }
+
+#if 0
+ /* Intel Series 100 Flash: skip first block */
+ if ((region.JedecMfr == 0x89) && (region.JedecInfo == 0xaa) &&
+ (bootsize == 0)) {
+ if (!quiet)
+ printf("Skipping first block to protect CIS info...\n");
+ bootsize = 1;
+ }
+#endif
+
+ /* Create header */
+ build_header(&hdr, mtd.size, mtd.erasesize,
+ spare, reserve, bootsize);
+
+ if (!quiet) {
+ printf("Partition size = ");
+ print_size(mtd.size);
+ printf(", erase unit size = ");
+ print_size(mtd.erasesize);
+ printf(", %d transfer units\n", spare);
+ if (bootsize != 0) {
+ print_size(le16_to_cpu(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize);
+ printf(" allocated for boot image\n");
+ }
+ printf("Reserved %d%%, formatted size = ", reserve);
+ print_size(le32_to_cpu(hdr.FormattedSize));
+ printf("\n");
+ fflush(stdout);
+ }
+
+ if (interrogate)
+ if (!prompt("This will destroy all data on the target device. Confirm?", false))
+ return -1;
+
+ /* Create basic block allocation table for control blocks */
+ nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int)
+ + le32_to_cpu(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize;
+ bam = malloc(nbam * sizeof(u_int));
+ for (i = 0; i < nbam; i++)
+ bam[i] = cpu_to_le32(BLOCK_CONTROL);
+
+ /* Erase partition */
+ if (!quiet) {
+ printf("Erasing all blocks...\n");
+ fflush(stdout);
+ }
+ erase.length = mtd.erasesize;
+ erase.start = mtd.erasesize * le16_to_cpu(hdr.FirstPhysicalEUN);
+ for (i = 0; i < le16_to_cpu(hdr.NumEraseUnits); i++) {
+ if (ioctl(fd, MEMERASE, &erase) < 0) {
+ if (!quiet) {
+ putchar('\n');
+ fflush(stdout);
+ }
+ perror("block erase failed");
+ return -1;
+ }
+ erase.start += erase.length;
+ if (!quiet) {
+ if (mtd.size <= 0x800000) {
+ if (erase.start % 0x100000) {
+ if (!(erase.start % 0x20000)) putchar('-');
+ }
+ else putchar('+');
+ }
+ else {
+ if (erase.start % 0x800000) {
+ if (!(erase.start % 0x100000)) putchar('+');
+ }
+ else putchar('*');
+ }
+ fflush(stdout);
+ }
+ }
+ if (!quiet) putchar('\n');
+
+ /* Prepare erase units */
+ if (!quiet) {
+ printf("Writing erase unit headers...\n");
+ fflush(stdout);
+ }
+ lun = 0;
+ /* Distribute transfer units over the entire region */
+ step = spare ? (le16_to_cpu(hdr.NumEraseUnits) / spare) : (le16_to_cpu(hdr.NumEraseUnits) + 1);
+ for (i = 0; i < le16_to_cpu(hdr.NumEraseUnits); i++) {
+ off_t ofs = (off_t) (i + le16_to_cpu(hdr.FirstPhysicalEUN)) << hdr.EraseUnitSize;
+ if (lseek(fd, ofs, SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ /* Is this a transfer unit? */
+ if (((i+1) % step) == 0)
+ hdr.LogicalEUN = cpu_to_le16(0xffff);
+ else {
+ hdr.LogicalEUN = cpu_to_le16(lun);
+ lun++;
+ }
+ if (write(fd, &hdr, sizeof(hdr)) == -1) {
+ perror("write failed");
+ break;
+ }
+ if (lseek(fd, ofs + le32_to_cpu(hdr.BAMOffset), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (write(fd, bam, nbam * sizeof(u_int)) == -1) {
+ perror("write failed");
+ break;
+ }
+ }
+ if (i < le16_to_cpu(hdr.NumEraseUnits))
+ return -1;
+ else
+ return 0;
+} /* format_partition */
+
+/*====================================================================*/
+
+int main(int argc, char *argv[])
+{
+ int quiet, interrogate, reserve;
+ int optch, errflg, fd, ret;
+ u_int spare, bootsize;
+ char *s;
+ extern char *optarg;
+ struct stat buf;
+
+ quiet = 0;
+ interrogate = 0;
+ spare = 1;
+ reserve = 5;
+ errflg = 0;
+ bootsize = 0;
+
+ while ((optch = getopt(argc, argv, "qir:s:b:")) != -1) {
+ switch (optch) {
+ case 'q':
+ quiet = 1; break;
+ case 'i':
+ interrogate = 1; break;
+ case 's':
+ spare = strtoul(optarg, NULL, 0); break;
+ case 'r':
+ reserve = strtoul(optarg, NULL, 0); break;
+ case 'b':
+ bootsize = strtoul(optarg, &s, 0);
+ if ((*s == 'k') || (*s == 'K'))
+ bootsize *= 1024;
+ break;
+ default:
+ errflg = 1; break;
+ }
+ }
+ if (errflg || (optind != argc-1)) {
+ fprintf(stderr, "usage: %s [-q] [-i] [-s spare-blocks]"
+ " [-r reserve-percent] [-b bootsize] device\n", PROGRAM_NAME);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(argv[optind], &buf) != 0) {
+ perror("status check failed");
+ exit(EXIT_FAILURE);
+ }
+ if (!(buf.st_mode & S_IFCHR)) {
+ fprintf(stderr, "%s is not a character special device\n",
+ argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ fd = open(argv[optind], O_RDWR);
+ if (fd == -1) {
+ perror("open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = format_partition(fd, quiet, interrogate, spare, reserve,
+ bootsize);
+ if (!quiet) {
+ if (ret)
+ printf("format failed.\n");
+ else
+ printf("format successful.\n");
+ }
+ close(fd);
+
+ exit((ret) ? EXIT_FAILURE : EXIT_SUCCESS);
+ return 0;
+}
diff --git a/misc-utils/mcast_image.h b/misc-utils/mcast_image.h
new file mode 100644
index 0000000..8e94ffa
--- /dev/null
+++ b/misc-utils/mcast_image.h
@@ -0,0 +1,54 @@
+#include <stdint.h>
+
+#define PKT_SIZE 2820
+
+struct image_pkt_hdr {
+ uint32_t resend;
+ uint32_t totcrc;
+ uint32_t nr_blocks;
+ uint32_t blocksize;
+ uint32_t block_crc;
+ uint32_t block_nr;
+ uint32_t pkt_sequence;
+ uint16_t pkt_nr;
+ uint16_t nr_pkts;
+ uint32_t thislen;
+ uint32_t thiscrc;
+};
+
+struct image_pkt {
+ struct image_pkt_hdr hdr;
+ unsigned char data[PKT_SIZE];
+};
+
+struct fec_parms;
+
+/* k - number of actual data packets
+ * n - total number of packets including data and redundant packets
+ * (actual packet size isn't relevant here) */
+struct fec_parms *fec_new(int k, int n);
+void fec_free(struct fec_parms *p);
+
+/* src - array of (n) pointers to data packets
+ * fec - buffer for packet to be generated
+ * index - index of packet to be generated (0 <= index < n)
+ * sz - data packet size
+ *
+ * _linear version just takes a pointer to the raw data; no
+ * mucking about with packet pointers.
+ */
+void fec_encode(struct fec_parms *code, unsigned char *src[],
+ unsigned char *fec, int index, int sz);
+void fec_encode_linear(struct fec_parms *code, unsigned char *src,
+ unsigned char *fec, int index, int sz);
+
+/* data - array of (k) pointers to data packets, in arbitrary order (see i)
+ * i - indices of (data) packets
+ * sz - data packet size
+ *
+ * Will never fail as long as you give it (k) individual data packets.
+ * Will re-order the (data) pointers but not the indices -- data packets
+ * are ordered on return.
+ */
+int fec_decode(struct fec_parms *code, unsigned char *data[],
+ int i[], int sz);
diff --git a/misc-utils/mtd_debug.c b/misc-utils/mtd_debug.c
new file mode 100644
index 0000000..d6993ce
--- /dev/null
+++ b/misc-utils/mtd_debug.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2d3D, Inc.
+ * Written by Abraham vd Merwe <abraham@2d3d.co.za>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of other contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define PROGRAM_NAME "mtd_debug"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <mtd/mtd-user.h>
+#include "common.h"
+
+/*
+ * MEMGETINFO
+ */
+static int getmeminfo(int fd, struct mtd_info_user *mtd)
+{
+ return ioctl(fd, MEMGETINFO, mtd);
+}
+
+/*
+ * MEMERASE
+ */
+static int memerase(int fd, struct erase_info_user *erase)
+{
+ return ioctl(fd, MEMERASE, erase);
+}
+
+/*
+ * MEMGETREGIONCOUNT
+ * MEMGETREGIONINFO
+ */
+static int getregions(int fd, struct region_info_user *regions, int *n)
+{
+ int i, err;
+ err = ioctl(fd, MEMGETREGIONCOUNT, n);
+ if (err)
+ return err;
+ for (i = 0; i < *n; i++) {
+ regions[i].regionindex = i;
+ err = ioctl(fd, MEMGETREGIONINFO, &regions[i]);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+int erase_flash(int fd, u_int32_t offset, u_int32_t bytes)
+{
+ int err;
+ struct erase_info_user erase;
+ erase.start = offset;
+ erase.length = bytes;
+ err = memerase(fd, &erase);
+ if (err < 0) {
+ perror("MEMERASE");
+ return 1;
+ }
+ fprintf(stderr, "Erased %d bytes from address 0x%.8x in flash\n", bytes, offset);
+ return 0;
+}
+
+void printsize(u_int32_t x)
+{
+ int i;
+ static const char *flags = "KMGT";
+ printf("%u ", x);
+ for (i = 0; x >= 1024 && flags[i] != '\0'; i++)
+ x /= 1024;
+ i--;
+ if (i >= 0)
+ printf("(%u%c)", x, flags[i]);
+}
+
+int flash_to_file(int fd, off_t offset, size_t len, const char *filename)
+{
+ u_int8_t *buf = NULL;
+ int outfd, err;
+ int size = len * sizeof(u_int8_t);
+ int n = len;
+
+ if (offset != lseek(fd, offset, SEEK_SET)) {
+ perror("lseek()");
+ goto err0;
+ }
+ outfd = creat(filename, 0666);
+ if (outfd < 0) {
+ perror("creat()");
+ goto err1;
+ }
+
+retry:
+ if ((buf = (u_int8_t *) malloc(size)) == NULL) {
+#define BUF_SIZE (64 * 1024 * sizeof(u_int8_t))
+ fprintf(stderr, "%s: malloc(%#x)\n", __func__, size);
+ if (size != BUF_SIZE) {
+ size = BUF_SIZE;
+ fprintf(stderr, "%s: trying buffer size %#x\n", __func__, size);
+ goto retry;
+ }
+ perror("malloc()");
+ goto err0;
+ }
+ do {
+ if (n <= size)
+ size = n;
+ err = read(fd, buf, size);
+ if (err < 0) {
+ fprintf(stderr, "%s: read, size %#x, n %#x\n", __func__, size, n);
+ perror("read()");
+ goto err2;
+ }
+ err = write(outfd, buf, size);
+ if (err < 0) {
+ fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, size, n);
+ perror("write()");
+ goto err2;
+ }
+ if (err != size) {
+ fprintf(stderr, "Couldn't copy entire buffer to %s. (%d/%d bytes copied)\n", filename, err, size);
+ goto err2;
+ }
+ n -= size;
+ } while (n > 0);
+
+ if (buf != NULL)
+ free(buf);
+ close(outfd);
+ printf("Copied %zu bytes from address 0x%.8"PRIxoff_t" in flash to %s\n", len, offset, filename);
+ return 0;
+
+err2:
+ close(outfd);
+err1:
+ if (buf != NULL)
+ free(buf);
+err0:
+ return 1;
+}
+
+int file_to_flash(int fd, off_t offset, u_int32_t len, const char *filename)
+{
+ u_int8_t *buf = NULL;
+ FILE *fp;
+ int err;
+ int size = len * sizeof(u_int8_t);
+ int n = len;
+
+ if (offset != lseek(fd, offset, SEEK_SET)) {
+ perror("lseek()");
+ return 1;
+ }
+ if ((fp = fopen(filename, "r")) == NULL) {
+ perror("fopen()");
+ return 1;
+ }
+retry:
+ if ((buf = (u_int8_t *) malloc(size)) == NULL) {
+ fprintf(stderr, "%s: malloc(%#x) failed\n", __func__, size);
+ if (size != BUF_SIZE) {
+ size = BUF_SIZE;
+ fprintf(stderr, "%s: trying buffer size %#x\n", __func__, size);
+ goto retry;
+ }
+ perror("malloc()");
+ fclose(fp);
+ return 1;
+ }
+ do {
+ if (n <= size)
+ size = n;
+ if (fread(buf, size, 1, fp) != 1 || ferror(fp)) {
+ fprintf(stderr, "%s: fread, size %#x, n %#x\n", __func__, size, n);
+ perror("fread()");
+ free(buf);
+ fclose(fp);
+ return 1;
+ }
+ err = write(fd, buf, size);
+ if (err < 0) {
+ fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, size, n);
+ perror("write()");
+ free(buf);
+ fclose(fp);
+ return 1;
+ }
+ n -= size;
+ } while (n > 0);
+
+ if (buf != NULL)
+ free(buf);
+ fclose(fp);
+ printf("Copied %d bytes from %s to address 0x%.8"PRIxoff_t" in flash\n", len, filename, offset);
+ return 0;
+}
+
+int showinfo(int fd)
+{
+ int i, err, n;
+ struct mtd_info_user mtd;
+ static struct region_info_user region[1024];
+
+ err = getmeminfo(fd, &mtd);
+ if (err < 0) {
+ perror("MEMGETINFO");
+ return 1;
+ }
+
+ err = getregions(fd, region, &n);
+ if (err < 0) {
+ perror("MEMGETREGIONCOUNT");
+ return 1;
+ }
+
+ printf("mtd.type = ");
+ switch (mtd.type) {
+ case MTD_ABSENT:
+ printf("MTD_ABSENT");
+ break;
+ case MTD_RAM:
+ printf("MTD_RAM");
+ break;
+ case MTD_ROM:
+ printf("MTD_ROM");
+ break;
+ case MTD_NORFLASH:
+ printf("MTD_NORFLASH");
+ break;
+ case MTD_NANDFLASH:
+ printf("MTD_NANDFLASH");
+ break;
+ case MTD_MLCNANDFLASH:
+ printf("MTD_MLCNANDFLASH");
+ break;
+ case MTD_DATAFLASH:
+ printf("MTD_DATAFLASH");
+ break;
+ case MTD_UBIVOLUME:
+ printf("MTD_UBIVOLUME");
+ default:
+ printf("(unknown type - new MTD API maybe?)");
+ }
+
+ printf("\nmtd.flags = ");
+ if (mtd.flags == MTD_CAP_ROM)
+ printf("MTD_CAP_ROM");
+ else if (mtd.flags == MTD_CAP_RAM)
+ printf("MTD_CAP_RAM");
+ else if (mtd.flags == MTD_CAP_NORFLASH)
+ printf("MTD_CAP_NORFLASH");
+ else if (mtd.flags == MTD_CAP_NANDFLASH)
+ printf("MTD_CAP_NANDFLASH");
+ else if (mtd.flags == MTD_WRITEABLE)
+ printf("MTD_WRITEABLE");
+ else {
+ int first = 1;
+ static struct {
+ const char *name;
+ int value;
+ } flags[] =
+ {
+ { "MTD_WRITEABLE", MTD_WRITEABLE },
+ { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE },
+ { "MTD_NO_ERASE", MTD_NO_ERASE },
+ { "MTD_POWERUP_LOCK", MTD_POWERUP_LOCK },
+ { NULL, -1 }
+ };
+ for (i = 0; flags[i].name != NULL; i++) {
+ if (mtd.flags & flags[i].value) {
+ if (first) {
+ printf("%s", flags[i].name);
+ first = 0;
+ } else {
+ printf(" | %s", flags[i].name);
+ }
+ }
+ }
+ }
+
+ printf("\nmtd.size = ");
+ printsize(mtd.size);
+
+ printf("\nmtd.erasesize = ");
+ printsize(mtd.erasesize);
+
+ printf("\nmtd.writesize = ");
+ printsize(mtd.writesize);
+
+ printf("\nmtd.oobsize = ");
+ printsize(mtd.oobsize);
+
+ printf("\nregions = %d\n\n", n);
+
+ for (i = 0; i < n; i++) {
+ printf("region[%d].offset = 0x%.8x\n"
+ "region[%d].erasesize = ",
+ i, region[i].offset, i);
+ printsize(region[i].erasesize);
+ printf("\nregion[%d].numblocks = %d\n"
+ "region[%d].regionindex = %d\n",
+ i, region[i].numblocks,
+ i, region[i].regionindex);
+ }
+ return 0;
+}
+
+void showusage(void)
+{
+ fprintf(stderr, "usage: %1$s info <device>\n"
+ " %1$s read <device> <offset> <len> <dest-filename>\n"
+ " %1$s write <device> <offset> <len> <source-filename>\n"
+ " %1$s erase <device> <offset> <len>\n",
+ PROGRAM_NAME);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ int err = 0, fd;
+ int open_flag;
+
+ enum {
+ OPT_INFO,
+ OPT_READ,
+ OPT_WRITE,
+ OPT_ERASE
+ } option = OPT_INFO;
+
+ /* parse command-line options */
+ if (argc == 3 && !strcmp(argv[1], "info"))
+ option = OPT_INFO;
+ else if (argc == 6 && !strcmp(argv[1], "read"))
+ option = OPT_READ;
+ else if (argc == 6 && !strcmp(argv[1], "write"))
+ option = OPT_WRITE;
+ else if (argc == 5 && !strcmp(argv[1], "erase"))
+ option = OPT_ERASE;
+ else
+ showusage();
+
+ /* open device */
+ open_flag = (option == OPT_INFO || option == OPT_READ) ? O_RDONLY : O_RDWR;
+ if ((fd = open(argv[2], O_SYNC | open_flag)) < 0)
+ errmsg_die("open()");
+
+ switch (option) {
+ case OPT_INFO:
+ showinfo(fd);
+ break;
+ case OPT_READ:
+ err = flash_to_file(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]);
+ break;
+ case OPT_WRITE:
+ err = file_to_flash(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]);
+ break;
+ case OPT_ERASE:
+ err = erase_flash(fd, strtoul(argv[3], NULL, 0), strtoul(argv[4], NULL, 0));
+ break;
+ }
+
+ /* close device */
+ if (close(fd) < 0)
+ errmsg_die("close()");
+
+ return err;
+}
diff --git a/misc-utils/mtdpart.c b/misc-utils/mtdpart.c
new file mode 100644
index 0000000..0016e34
--- /dev/null
+++ b/misc-utils/mtdpart.c
@@ -0,0 +1,194 @@
+/*
+ * mtdpart.c
+ *
+ * Copyright 2015 The Chromium OS Authors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ * This utility adds or removes a partition from an MTD device.
+ */
+
+#define PROGRAM_NAME "mtdpart"
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/blkpg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+
+static void display_help(int status)
+{
+ fprintf(status == EXIT_SUCCESS ? stdout : stderr,
+"Usage: %1$s add [OPTION] <MTD_DEVICE> <PART_NAME> <START> <SIZE>\n"
+" %1$s del [OPTION] <MTD_DEVICE> <PART_NUMBER>\n"
+"Adds a partition to an MTD device, or remove an existing partition from it.\n"
+"\n"
+" -h, --help Display this help and exit\n"
+" --version Output version information and exit\n"
+"\n"
+"START location and SIZE of the partition are in bytes. They should align on\n"
+"eraseblock size.\n",
+ PROGRAM_NAME
+ );
+ exit(status);
+}
+
+static void display_version(void)
+{
+ printf("%1$s " VERSION "\n"
+ "\n"
+ "%1$s comes with NO WARRANTY\n"
+ "to the extent permitted by law.\n"
+ "\n"
+ "You may redistribute copies of %1$s\n"
+ "under the terms of the GNU General Public Licence.\n"
+ "See the file `COPYING' for more information.\n",
+ PROGRAM_NAME);
+ exit(EXIT_SUCCESS);
+}
+
+/* Command arguments */
+
+typedef enum {
+ COMMAND_ADD,
+ COMMAND_DEL
+} command_type;
+
+static command_type command; /* add or del */
+static const char *mtddev; /* mtd device name */
+static const char *part_name; /* partition name */
+static int part_no; /* partition number */
+static long long start_addr; /* start address */
+static long long length; /* partition size */
+
+static void process_options(int argc, char * const argv[])
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char short_options[] = "h";
+ static const struct option long_options[] = {
+ {"version", no_argument, 0, 0},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 0:
+ display_version();
+ break;
+ case 'h':
+ display_help(EXIT_SUCCESS);
+ break;
+ case '?':
+ error++;
+ break;
+ }
+ }
+
+ if ((argc - optind) < 3 || error)
+ display_help(EXIT_FAILURE);
+
+ const char *s_command = argv[optind++];
+ mtddev = argv[optind++];
+
+ if (strcmp(s_command, "del") == 0 && (argc - optind) == 1) {
+ const char *s_part_no = argv[optind++];
+
+ long tmp = simple_strtol(s_part_no, &error);
+ if (tmp < 0)
+ errmsg_die("Can't specify negative partition number: %ld",
+ tmp);
+ if (tmp > INT_MAX)
+ errmsg_die("Partition number exceeds INT_MAX: %ld",
+ tmp);
+
+ part_no = tmp;
+ command = COMMAND_DEL;
+ } else if (strcmp(s_command, "add") == 0 && (argc - optind) == 3) {
+ const char *s_start;
+ const char *s_length;
+
+ part_name = argv[optind++];
+ s_start = argv[optind++];
+ s_length = argv[optind++];
+
+ if (strlen(part_name) >= BLKPG_DEVNAMELTH)
+ errmsg_die("Partition name (%s) should be less than %d characters",
+ part_name, BLKPG_DEVNAMELTH);
+
+ start_addr = simple_strtoll(s_start, &error);
+ if (start_addr < 0)
+ errmsg_die("Can't specify negative start offset: %lld",
+ start_addr);
+
+ length = simple_strtoll(s_length, &error);
+ if (length < 0)
+ errmsg_die("Can't specify negative length: %lld",
+ length);
+
+ command = COMMAND_ADD;
+ } else
+ display_help(EXIT_FAILURE);
+
+ if (error)
+ display_help(EXIT_FAILURE);
+}
+
+
+int main(int argc, char * const argv[])
+{
+ int fd;
+ struct blkpg_partition part;
+ struct blkpg_ioctl_arg arg;
+
+ process_options(argc, argv);
+
+ fd = open(mtddev, O_RDWR | O_CLOEXEC);
+ if (fd == -1)
+ sys_errmsg_die("Cannot open %s", mtddev);
+
+ memset(&part, 0, sizeof(part));
+
+ memset(&arg, 0, sizeof(arg));
+ arg.datalen = sizeof(part);
+ arg.data = &part;
+
+ switch (command) {
+ case COMMAND_ADD:
+ part.start = start_addr;
+ part.length = length;
+ strncpy(part.devname, part_name, sizeof(part.devname));
+ arg.op = BLKPG_ADD_PARTITION;
+ break;
+ case COMMAND_DEL:
+ part.pno = part_no;
+ arg.op = BLKPG_DEL_PARTITION;
+ break;
+ }
+
+ if (ioctl(fd, BLKPG, &arg))
+ sys_errmsg_die("Failed to issue BLKPG ioctl");
+
+ close(fd);
+
+ /* Exit happy */
+ return EXIT_SUCCESS;
+}
diff --git a/misc-utils/recv_image.c b/misc-utils/recv_image.c
new file mode 100644
index 0000000..0093831
--- /dev/null
+++ b/misc-utils/recv_image.c
@@ -0,0 +1,484 @@
+
+#define PROGRAM_NAME "recv_image"
+#define _XOPEN_SOURCE 500
+#define _BSD_SOURCE /* struct ip_mreq */
+
+#include <errno.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <crc32.h>
+#include "mtd/mtd-user.h"
+#include "mcast_image.h"
+
+#include "common.h"
+
+#define WBUF_SIZE 4096
+struct eraseblock {
+ uint32_t flash_offset;
+ unsigned char wbuf[WBUF_SIZE];
+ int wbuf_ofs;
+ int nr_pkts;
+ int *pkt_indices;
+ uint32_t crc;
+};
+
+int main(int argc, char **argv)
+{
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ struct addrinfo *runp;
+ int ret;
+ int sock;
+ ssize_t len;
+ int flfd;
+ struct mtd_info_user meminfo;
+ unsigned char *eb_buf, *decode_buf, **src_pkts;
+ int nr_blocks = 0;
+ int pkts_per_block;
+ int block_nr = -1;
+ uint32_t image_crc = 0;
+ int total_pkts = 0;
+ int ignored_pkts = 0;
+ loff_t mtdoffset = 0;
+ int badcrcs = 0;
+ int duplicates = 0;
+ int file_mode = 0;
+ struct fec_parms *fec = NULL;
+ int i;
+ struct eraseblock *eraseblocks = NULL;
+ uint32_t start_seq = 0;
+ struct timeval start, now;
+ unsigned long fec_time = 0, flash_time = 0, crc_time = 0,
+ rflash_time = 0, erase_time = 0, net_time = 0;
+
+ if (argc != 4) {
+ fprintf(stderr, "usage: %s <host> <port> <mtddev>\n",
+ PROGRAM_NAME);
+ exit(1);
+ }
+ /* Open the device */
+ flfd = open(argv[3], O_RDWR);
+
+ if (flfd >= 0) {
+ /* Fill in MTD device capability structure */
+ if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) {
+ perror("MEMGETINFO");
+ close(flfd);
+ flfd = -1;
+ } else {
+ printf("Receive to MTD device %s with erasesize %d\n",
+ argv[3], meminfo.erasesize);
+ }
+ }
+ if (flfd == -1) {
+ /* Try again, as if it's a file */
+ flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644);
+ if (flfd < 0) {
+ perror("open");
+ exit(1);
+ }
+ meminfo.erasesize = 131072;
+ file_mode = 1;
+ printf("Receive to file %s with (assumed) erasesize %d\n",
+ argv[3], meminfo.erasesize);
+ }
+
+ pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE;
+
+ eb_buf = malloc(pkts_per_block * PKT_SIZE);
+ decode_buf = malloc(pkts_per_block * PKT_SIZE);
+ if (!eb_buf && !decode_buf) {
+ fprintf(stderr, "No memory for eraseblock buffer\n");
+ exit(1);
+ }
+ src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block);
+ if (!src_pkts) {
+ fprintf(stderr, "No memory for decode packet pointers\n");
+ exit(1);
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ ret = getaddrinfo(argv[1], argv[2], &hints, &ai);
+ if (ret) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
+ exit(1);
+ }
+ runp = ai;
+ for (runp = ai; runp; runp = runp->ai_next) {
+ sock = socket(runp->ai_family, runp->ai_socktype,
+ runp->ai_protocol);
+ if (sock == -1) {
+ perror("socket");
+ continue;
+ }
+ if (runp->ai_family == AF_INET &&
+ IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) {
+ struct ip_mreq rq;
+ rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr;
+ rq.imr_interface.s_addr = INADDR_ANY;
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) {
+ perror("IP_ADD_MEMBERSHIP");
+ close(sock);
+ continue;
+ }
+
+ } else if (runp->ai_family == AF_INET6 &&
+ ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) {
+ struct ipv6_mreq rq;
+ rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr;
+ rq.ipv6mr_interface = 0;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) {
+ perror("IPV6_ADD_MEMBERSHIP");
+ close(sock);
+ continue;
+ }
+ }
+ if (bind(sock, runp->ai_addr, runp->ai_addrlen)) {
+ perror("bind");
+ close(sock);
+ continue;
+ }
+ break;
+ }
+ if (!runp)
+ exit(1);
+
+ while (1) {
+ struct image_pkt thispkt;
+
+ len = read(sock, &thispkt, sizeof(thispkt));
+
+ if (len < 0) {
+ perror("read socket");
+ break;
+ }
+ if (len < sizeof(thispkt)) {
+ fprintf(stderr, "Wrong length %zd bytes (expected %zu)\n",
+ len, sizeof(thispkt));
+ continue;
+ }
+ if (!eraseblocks) {
+ image_crc = thispkt.hdr.totcrc;
+ start_seq = ntohl(thispkt.hdr.pkt_sequence);
+
+ if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) {
+ fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n",
+ ntohl(thispkt.hdr.blocksize), meminfo.erasesize);
+ exit(1);
+ }
+ nr_blocks = ntohl(thispkt.hdr.nr_blocks);
+
+ fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts));
+
+ eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks));
+ if (!eraseblocks) {
+ fprintf(stderr, "No memory for block map\n");
+ exit(1);
+ }
+ for (i = 0; i < nr_blocks; i++) {
+ eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block);
+ if (!eraseblocks[i].pkt_indices) {
+ fprintf(stderr, "Failed to allocate packet indices\n");
+ exit(1);
+ }
+ eraseblocks[i].nr_pkts = 0;
+ if (!file_mode) {
+ if (mtdoffset >= meminfo.size) {
+ fprintf(stderr, "Run out of space on flash\n");
+ exit(1);
+ }
+#if 1 /* Deliberately use bad blocks... test write failures */
+ while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) {
+ printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset);
+ mtdoffset += meminfo.erasesize;
+ }
+#endif
+ }
+ eraseblocks[i].flash_offset = mtdoffset;
+ mtdoffset += meminfo.erasesize;
+ eraseblocks[i].wbuf_ofs = 0;
+ }
+ gettimeofday(&start, NULL);
+ }
+ if (image_crc != thispkt.hdr.totcrc) {
+ fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n",
+ ntohl(image_crc), ntohl(thispkt.hdr.totcrc));
+ exit(1);
+ }
+
+ block_nr = ntohl(thispkt.hdr.block_nr);
+ if (block_nr >= nr_blocks) {
+ fprintf(stderr, "\nErroneous block_nr %d (> %d)\n",
+ block_nr, nr_blocks);
+ exit(1);
+ }
+ for (i=0; i<eraseblocks[block_nr].nr_pkts; i++) {
+ if (eraseblocks[block_nr].pkt_indices[i] == ntohs(thispkt.hdr.pkt_nr)) {
+// printf("Discarding duplicate packet at %08x pkt %d\n",
+// block_nr * meminfo.erasesize, eraseblocks[block_nr].pkt_indices[i]);
+ duplicates++;
+ break;
+ }
+ }
+ if (i < eraseblocks[block_nr].nr_pkts) {
+ continue;
+ }
+
+ if (eraseblocks[block_nr].nr_pkts >= pkts_per_block) {
+ /* We have a block which we didn't really need */
+ eraseblocks[block_nr].nr_pkts++;
+ ignored_pkts++;
+ continue;
+ }
+
+ if (mtd_crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) {
+ printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n",
+ block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr),
+ mtd_crc32(-1, thispkt.data, PKT_SIZE),
+ ntohl(thispkt.hdr.thiscrc));
+ badcrcs++;
+ continue;
+ }
+ pkt_again:
+ eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] =
+ ntohs(thispkt.hdr.pkt_nr);
+ total_pkts++;
+ if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) {
+ uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1;
+ long time_msec;
+ gettimeofday(&now, NULL);
+
+ time_msec = ((now.tv_usec - start.tv_usec) / 1000) +
+ (now.tv_sec - start.tv_sec) * 1000;
+
+ printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ",
+ total_pkts, nr_blocks * pkts_per_block,
+ total_pkts * 100 / nr_blocks / pkts_per_block,
+ time_msec / 1000,
+ total_pkts * PKT_SIZE / 1024 * 1000 / time_msec,
+ pkts_sent - total_pkts - duplicates - ignored_pkts,
+ (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent,
+ duplicates + ignored_pkts);
+ fflush(stdout);
+ }
+
+ if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) {
+ /* New packet doesn't full the wbuf */
+ memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs,
+ thispkt.data, PKT_SIZE);
+ eraseblocks[block_nr].wbuf_ofs += PKT_SIZE;
+ } else {
+ int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs;
+ ssize_t wrotelen;
+ static int faked = 1;
+
+ memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs,
+ thispkt.data, fits);
+ wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE,
+ eraseblocks[block_nr].flash_offset);
+
+ if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) {
+ faked = 1;
+ if (wrotelen < 0)
+ perror("\npacket write");
+ else
+ fprintf(stderr, "\nshort write of packet wbuf\n");
+
+ if (!file_mode) {
+ struct erase_info_user erase;
+ /* FIXME: Perhaps we should store pkt crcs and try
+ to recover data from the offending eraseblock */
+
+ /* We have increased nr_pkts but not yet flash_offset */
+ erase.start = eraseblocks[block_nr].flash_offset &
+ ~(meminfo.erasesize - 1);
+ erase.length = meminfo.erasesize;
+
+ printf("Will erase at %08x len %08x (bad write was at %08x)\n",
+ erase.start, erase.length, eraseblocks[block_nr].flash_offset);
+ if (ioctl(flfd, MEMERASE, &erase)) {
+ perror("MEMERASE");
+ exit(1);
+ }
+ if (mtdoffset >= meminfo.size) {
+ fprintf(stderr, "Run out of space on flash\n");
+ exit(1);
+ }
+ while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) {
+ printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset);
+ mtdoffset += meminfo.erasesize;
+ if (mtdoffset >= meminfo.size) {
+ fprintf(stderr, "Run out of space on flash\n");
+ exit(1);
+ }
+ }
+ eraseblocks[block_nr].flash_offset = mtdoffset;
+ printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset);
+ total_pkts -= eraseblocks[block_nr].nr_pkts;
+ eraseblocks[block_nr].nr_pkts = 0;
+ eraseblocks[block_nr].wbuf_ofs = 0;
+ mtdoffset += meminfo.erasesize;
+ goto pkt_again;
+ }
+ else /* Usually nothing we can do in file mode */
+ exit(1);
+ }
+ eraseblocks[block_nr].flash_offset += WBUF_SIZE;
+ /* Copy the remainder into the wbuf */
+ memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits);
+ eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits;
+ }
+
+ if (eraseblocks[block_nr].nr_pkts == pkts_per_block) {
+ eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc);
+
+ if (total_pkts == nr_blocks * pkts_per_block)
+ break;
+ }
+ }
+ printf("\n");
+ gettimeofday(&now, NULL);
+ net_time = (now.tv_usec - start.tv_usec) / 1000;
+ net_time += (now.tv_sec - start.tv_sec) * 1000;
+ close(sock);
+ for (block_nr = 0; block_nr < nr_blocks; block_nr++) {
+ ssize_t rwlen;
+ gettimeofday(&start, NULL);
+ eraseblocks[block_nr].flash_offset -= meminfo.erasesize;
+ rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset);
+
+ gettimeofday(&now, NULL);
+ rflash_time += (now.tv_usec - start.tv_usec) / 1000;
+ rflash_time += (now.tv_sec - start.tv_sec) * 1000;
+ if (rwlen < 0) {
+ perror("read");
+ /* Argh. Perhaps we could go back and try again, but if the flash is
+ going to fail to read back what we write to it, and the whole point
+ in this program is to write to it, what's the point? */
+ fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n");
+ exit(1);
+ }
+
+ memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf,
+ eraseblocks[block_nr].wbuf_ofs);
+
+ for (i=0; i < pkts_per_block; i++)
+ src_pkts[i] = &eb_buf[i * PKT_SIZE];
+
+ gettimeofday(&start, NULL);
+ if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) {
+ /* Eep. This cannot happen */
+ printf("The world is broken. fec_decode() returned error\n");
+ exit(1);
+ }
+ gettimeofday(&now, NULL);
+ fec_time += (now.tv_usec - start.tv_usec) / 1000;
+ fec_time += (now.tv_sec - start.tv_sec) * 1000;
+
+ for (i=0; i < pkts_per_block; i++)
+ memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE);
+
+ /* Paranoia */
+ gettimeofday(&start, NULL);
+ if (mtd_crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) {
+ printf("\nCRC mismatch for block #%d: want %08x got %08x\n",
+ block_nr, eraseblocks[block_nr].crc,
+ mtd_crc32(-1, decode_buf, meminfo.erasesize));
+ exit(1);
+ }
+ gettimeofday(&now, NULL);
+ crc_time += (now.tv_usec - start.tv_usec) / 1000;
+ crc_time += (now.tv_sec - start.tv_sec) * 1000;
+ start = now;
+
+ if (!file_mode) {
+ struct erase_info_user erase;
+
+ erase.start = eraseblocks[block_nr].flash_offset;
+ erase.length = meminfo.erasesize;
+
+ printf("\rErasing block at %08x...", erase.start);
+
+ if (ioctl(flfd, MEMERASE, &erase)) {
+ perror("MEMERASE");
+ /* This block has dirty data on it. If the erase failed, we're screwed */
+ fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n");
+ exit(1);
+ }
+ gettimeofday(&now, NULL);
+ erase_time += (now.tv_usec - start.tv_usec) / 1000;
+ erase_time += (now.tv_sec - start.tv_sec) * 1000;
+ start = now;
+ }
+ else printf("\r");
+ write_again:
+ rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset);
+ if (rwlen < meminfo.erasesize) {
+ if (rwlen < 0) {
+ perror("\ndecoded data write");
+ } else
+ fprintf(stderr, "\nshort write of decoded data\n");
+
+ if (!file_mode) {
+ struct erase_info_user erase;
+ erase.start = eraseblocks[block_nr].flash_offset;
+ erase.length = meminfo.erasesize;
+
+ printf("Erasing failed block at %08x\n",
+ eraseblocks[block_nr].flash_offset);
+
+ if (ioctl(flfd, MEMERASE, &erase)) {
+ perror("MEMERASE");
+ exit(1);
+ }
+ if (mtdoffset >= meminfo.size) {
+ fprintf(stderr, "Run out of space on flash\n");
+ exit(1);
+ }
+ while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) {
+ printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset);
+ mtdoffset += meminfo.erasesize;
+ if (mtdoffset >= meminfo.size) {
+ fprintf(stderr, "Run out of space on flash\n");
+ exit(1);
+ }
+ }
+ printf("Will try again at %08lx...", (long)mtdoffset);
+ eraseblocks[block_nr].flash_offset = mtdoffset;
+
+ goto write_again;
+ }
+ else /* Usually nothing we can do in file mode */
+ exit(1);
+ }
+ gettimeofday(&now, NULL);
+ flash_time += (now.tv_usec - start.tv_usec) / 1000;
+ flash_time += (now.tv_sec - start.tv_sec) * 1000;
+
+ printf("wrote image block %08x (%d pkts) ",
+ block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts);
+ fflush(stdout);
+ }
+ close(flfd);
+ printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000);
+ printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000);
+ printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000);
+ printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000);
+ printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000);
+ printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000);
+
+ return 0;
+}
diff --git a/misc-utils/serve_image.c b/misc-utils/serve_image.c
new file mode 100644
index 0000000..cbcf3b7
--- /dev/null
+++ b/misc-utils/serve_image.c
@@ -0,0 +1,300 @@
+#define PROGRAM_NAME "serve_image"
+#define _POSIX_C_SOURCE 200112L
+
+#include <time.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <crc32.h>
+#include <inttypes.h>
+
+#include "mcast_image.h"
+
+int tx_rate = 80000;
+int pkt_delay;
+
+#undef RANDOMDROP
+
+int main(int argc, char **argv)
+{
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ struct addrinfo *runp;
+ int ret;
+ int sock;
+ struct image_pkt pktbuf;
+ int rfd;
+ struct stat st;
+ int writeerrors = 0;
+ uint32_t erasesize;
+ unsigned char *image, *blockptr = NULL;
+ uint32_t block_nr, pkt_nr;
+ int nr_blocks;
+ struct timeval then, now, nextpkt;
+ long time_msecs;
+ int pkts_per_block;
+ int total_pkts_per_block;
+ struct fec_parms *fec;
+ unsigned char *last_block;
+ uint32_t *block_crcs;
+ long tosleep;
+ uint32_t sequence = 0;
+
+ if (argc == 6) {
+ tx_rate = atol(argv[5]) * 1024;
+ if (tx_rate < PKT_SIZE || tx_rate > 20000000) {
+ fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate);
+ exit(1);
+ }
+ argc = 5;
+ }
+ if (argc != 5) {
+ fprintf(stderr, "usage: %s <host> <port> <image> <erasesize> [<tx_rate>]\n",
+ PROGRAM_NAME);
+ exit(1);
+ }
+ pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate;
+ printf("Inter-packet delay (avg): %dµs\n", pkt_delay);
+ printf("Transmit rate: %d KiB/s\n", tx_rate / 1024);
+
+ erasesize = atol(argv[4]);
+ if (!erasesize) {
+ fprintf(stderr, "erasesize cannot be zero\n");
+ exit(1);
+ }
+
+ pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE;
+ total_pkts_per_block = pkts_per_block * 3 / 2;
+
+ /* We have to pad it with zeroes, so can't use it in-place */
+ last_block = malloc(pkts_per_block * PKT_SIZE);
+ if (!last_block) {
+ fprintf(stderr, "Failed to allocate last-block buffer\n");
+ exit(1);
+ }
+
+ fec = fec_new(pkts_per_block, total_pkts_per_block);
+ if (!fec) {
+ fprintf(stderr, "Error initialising FEC\n");
+ exit(1);
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ ret = getaddrinfo(argv[1], argv[2], &hints, &ai);
+ if (ret) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
+ exit(1);
+ }
+ runp = ai;
+ for (runp = ai; runp; runp = runp->ai_next) {
+ sock = socket(runp->ai_family, runp->ai_socktype,
+ runp->ai_protocol);
+ if (sock == -1) {
+ perror("socket");
+ continue;
+ }
+ if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0)
+ break;
+ perror("connect");
+ close(sock);
+ }
+ if (!runp)
+ exit(1);
+
+ rfd = open(argv[3], O_RDONLY);
+ if (rfd < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ if (fstat(rfd, &st)) {
+ perror("fstat");
+ exit(1);
+ }
+
+ if (st.st_size % erasesize) {
+ fprintf(stderr, "Image size %" PRIu64 " bytes is not a multiple of erasesize %d bytes\n",
+ st.st_size, erasesize);
+ exit(1);
+ }
+ image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0);
+ if (image == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ nr_blocks = st.st_size / erasesize;
+
+ block_crcs = malloc(nr_blocks * sizeof(uint32_t));
+ if (!block_crcs) {
+ fprintf(stderr, "Failed to allocate memory for CRCs\n");
+ exit(1);
+ }
+
+ memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize);
+ memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize);
+
+ printf("Checking CRC....");
+ fflush(stdout);
+
+ pktbuf.hdr.resend = 0;
+ pktbuf.hdr.totcrc = htonl(mtd_crc32(-1, image, st.st_size));
+ pktbuf.hdr.nr_blocks = htonl(nr_blocks);
+ pktbuf.hdr.blocksize = htonl(erasesize);
+ pktbuf.hdr.thislen = htonl(PKT_SIZE);
+ pktbuf.hdr.nr_pkts = htons(total_pkts_per_block);
+
+ printf("%08x\n", ntohl(pktbuf.hdr.totcrc));
+ printf("Checking block CRCs....");
+ fflush(stdout);
+ for (block_nr=0; block_nr < nr_blocks; block_nr++) {
+ printf("\rChecking block CRCS.... %d/%d",
+ block_nr + 1, nr_blocks);
+ fflush(stdout);
+ block_crcs[block_nr] = mtd_crc32(-1, image + (block_nr * erasesize), erasesize);
+ }
+
+ printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n"
+ "Estimated transmit time per cycle: %ds\n",
+ (long)st.st_size / 1024, (long) st.st_size,
+ nr_blocks, pkts_per_block,
+ nr_blocks * pkts_per_block * pkt_delay / 1000000);
+ gettimeofday(&then, NULL);
+ nextpkt = then;
+
+#ifdef RANDOMDROP
+ srand((unsigned)then.tv_usec);
+ printf("Random seed %u\n", (unsigned)then.tv_usec);
+#endif
+ while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) {
+
+ if (blockptr && pkt_nr == 0) {
+ unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf);
+ gettimeofday(&now, NULL);
+
+ time_msecs = (now.tv_sec - then.tv_sec) * 1000;
+ time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000;
+ printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n",
+ amt_sent / 1024, time_msecs,
+ amt_sent / 1024 * 1000 / time_msecs);
+ then = now;
+ }
+
+ for (block_nr = 0; block_nr < nr_blocks; block_nr++) {
+
+ int actualpkt;
+
+ /* Calculating the redundant FEC blocks is expensive;
+ the first $pkts_per_block are cheap enough though
+ because they're just copies. So alternate between
+ simple and complex stuff, so that we don't start
+ to choke and fail to keep up with the expected
+ bitrate in the second half of the sequence */
+ if (block_nr & 1)
+ actualpkt = pkt_nr;
+ else
+ actualpkt = total_pkts_per_block - 1 - pkt_nr;
+
+ blockptr = image + (erasesize * block_nr);
+ if (block_nr == nr_blocks - 1)
+ blockptr = last_block;
+
+ fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE);
+
+ pktbuf.hdr.thiscrc = htonl(mtd_crc32(-1, pktbuf.data, PKT_SIZE));
+ pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]);
+ pktbuf.hdr.block_nr = htonl(block_nr);
+ pktbuf.hdr.pkt_nr = htons(actualpkt);
+ pktbuf.hdr.pkt_sequence = htonl(sequence++);
+
+ printf("\rSending data block %08x packet %3d/%d",
+ block_nr * erasesize,
+ pkt_nr, total_pkts_per_block);
+
+ if (pkt_nr && !block_nr) {
+ unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf);
+
+ gettimeofday(&now, NULL);
+
+ time_msecs = (now.tv_sec - then.tv_sec) * 1000;
+ time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000;
+ printf(" (%ld KiB/s) ",
+ amt_sent / 1024 * 1000 / time_msecs);
+ }
+
+ fflush(stdout);
+
+#ifdef RANDOMDROP
+ if ((rand() % 1000) < 20) {
+ printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize);
+ continue;
+ }
+#endif
+ gettimeofday(&now, NULL);
+#if 1
+ tosleep = nextpkt.tv_usec - now.tv_usec +
+ (1000000 * (nextpkt.tv_sec - now.tv_sec));
+
+ /* We need hrtimers for this to actually work */
+ if (tosleep > 0) {
+ struct timespec req;
+
+ req.tv_nsec = (tosleep % 1000000) * 1000;
+ req.tv_sec = tosleep / 1000000;
+
+ nanosleep(&req, NULL);
+ }
+#else
+ while (now.tv_sec < nextpkt.tv_sec ||
+ (now.tv_sec == nextpkt.tv_sec &&
+ now.tv_usec < nextpkt.tv_usec)) {
+ gettimeofday(&now, NULL);
+ }
+#endif
+ nextpkt.tv_usec += pkt_delay;
+ if (nextpkt.tv_usec >= 1000000) {
+ nextpkt.tv_sec += nextpkt.tv_usec / 1000000;
+ nextpkt.tv_usec %= 1000000;
+ }
+
+ /* If the time for the next packet has already
+ passed (by some margin), then we've lost time
+ Adjust our expected timings accordingly. If
+ we're only a little way behind, don't slip yet */
+ if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) +
+ 1000000 * (nextpkt.tv_sec - now.tv_sec))) {
+ nextpkt = now;
+ }
+
+ if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) {
+ perror("write");
+ writeerrors++;
+ if (writeerrors > 10) {
+ fprintf(stderr, "Too many consecutive write errors\n");
+ exit(1);
+ }
+ } else
+ writeerrors = 0;
+
+
+
+ }
+ }
+ munmap(image, st.st_size);
+ close(rfd);
+ close(sock);
+ return 0;
+}