summaryrefslogtreecommitdiff
path: root/ftl_check.c
diff options
context:
space:
mode:
Diffstat (limited to 'ftl_check.c')
-rw-r--r--ftl_check.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/ftl_check.c b/ftl_check.c
new file mode 100644
index 0000000..1a64f6b
--- /dev/null
+++ b/ftl_check.c
@@ -0,0 +1,233 @@
+/* Ported to MTD system.
+ * $Id: ftl_check.c,v 1.6 2005/11/07 11:15:11 gleixner Exp $
+ * 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.
+
+======================================================================*/
+
+#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 <linux/mtd/ftl.h>
+
+#include <byteswap.h>
+#include <endian.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define TO_LE32(x) (x)
+# define TO_LE16(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define TO_LE32(x) (bswap_32(x))
+# define TO_LE16(x) (bswap_16(x))
+#else
+# error cannot detect endianess
+#endif
+
+#define FROM_LE32(x) TO_LE32(x)
+#define FROM_LE16(x) TO_LE16(x)
+
+/*====================================================================*/
+
+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, int verbose)
+{
+ mtd_info_t mtd;
+ erase_unit_header_t hdr, hdr2;
+ u_int i, 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 ((FROM_LE32(hdr.FormattedSize) > 0) &&
+ (FROM_LE32(hdr.FormattedSize) <= mtd.size) &&
+ (FROM_LE16(hdr.NumEraseUnits) > 0) &&
+ (FROM_LE16(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(FROM_LE32(hdr.FormattedSize));
+ printf(", erase units = %d, transfer units = %d\n",
+ FROM_LE16(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 < FROM_LE16(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 %d:\n", i);
+ if ((hdr2.FormattedSize != hdr.FormattedSize) ||
+ (hdr2.NumEraseUnits != hdr.NumEraseUnits) ||
+ (hdr2.SerialNumber != hdr.SerialNumber))
+ printf(" Erase unit header is corrupt.\n");
+ else if (FROM_LE16(hdr2.LogicalEUN) == 0xffff)
+ printf(" Transfer unit, erase count = %d\n", FROM_LE32(hdr2.EraseCount));
+ else {
+ printf(" Logical unit %d, erase count = %d\n",
+ FROM_LE16(hdr2.LogicalEUN), FROM_LE32(hdr2.EraseCount));
+ if (lseek(fd, (i << hdr.EraseUnitSize)+FROM_LE32(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(FROM_LE32(bam[j])))
+ free++;
+ else if (BLOCK_DELETED(FROM_LE32(bam[j])))
+ deleted++;
+ else switch (BLOCK_TYPE(FROM_LE32(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(char *pname)
+{
+ fprintf(stderr, "usage: %s [-v] device\n", pname);
+ fprintf(stderr, "-v verbose messages\n");
+}
+
+/*====================================================================*/
+
+int main(int argc, char *argv[])
+{
+ int verbose;
+ int optch, errflg, fd;
+ struct stat buf;
+
+ errflg = 0;
+ verbose = 0;
+ while ((optch = getopt(argc, argv, "vh")) != -1) {
+ switch (optch) {
+ case 'h':
+ errflg = 1; break;
+ case 'v':
+ verbose = 1; break;
+ default:
+ errflg = -1; break;
+ }
+ }
+ if (errflg || (optind != argc-1)) {
+ showusage(argv[0]);
+ 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, verbose);
+ close(fd);
+
+ exit(EXIT_SUCCESS);
+ return 0;
+}