218 lines
6.0 KiB
C
218 lines
6.0 KiB
C
|
/* 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;
|
||
|
}
|