Hi folks
One piece of the yaffs2 puzzle that has been missing forever is some means of
extracting/loading images from/to flash and being able to manipiulate these
on the host.
This is generally thwarted by different flash drivers having different ecc
layouts.
What I have in development is something called ymemtool which is intended to
do all the nandread/nandwrite functions with a yaffs slant.
In the fullness of time, ymemtool will be able to do all of:
1) Tell us info about the flash including the oob layout.
2) Read the contents of flash, apply the oob layout and so write an image file
in a consistent format.
3) Read a consistently formatted image file, apply the oob layout and write to
nand.
At this stage, (1) works and (2,3) are work in progress.
Then a host-side program will be able to read/write the consistently formatted
image file.
-- Charles
/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
* Created by Charles Manning <
charles@aleph1.co.uk>
*
* 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.
*/
/*
* This is a tool to perform various yaffs2-friendly operations on
* Linux mtd NAND.
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <getopt.h>
#include <asm/types.h>
#include <mtd/mtd-user.h>
#include "yaffs_packedtags2.h"
struct mtd_view {
int fd;
char * name;
struct mtd_info_user info;
struct nand_ecclayout ecclayout;
int nblocks;
int pages_per_block;
};
struct options {
int quiet;
int do_read;
int do_write;
int inband_tags;
int skip_bad;
char *dev_name;
char *image_name;
};
static int mtd_view_init(struct mtd_view *view, const char *dname)
{
int res;
view->name = strdup(dname);
view->fd = open(view->name, O_RDWR);
if (view->fd < 0) {
perror(view->name);
return -1;
}
res = ioctl(view->fd, MEMGETINFO, &view->info);
if(res < 0) {
perror("MEMGETINFO");
return -1;
}
res = ioctl(view->fd, ECCGETLAYOUT, &view->ecclayout);
if(res < 0) {
perror("ECCGETLAYOUT");
return -1;
}
view->nblocks = view->info.size / view->info.erasesize;
view->pages_per_block = view->info.erasesize / view->info.writesize;
return res;
}
static int mtd_is_bad_block(const struct mtd_view *view, int block)
{
loff_t pos;
int ret;
pos = (loff_t)block * view->info.erasesize;
ret = ioctl(view->fd, MEMGETBADBLOCK, &pos);
if (ret < 0)
perror("MEMGETBADBLOCK");
return ret;
}
static int mtd_read_oob(const struct mtd_view *view,
unsigned char *buffer,
loff_t pos)
{
struct mtd_oob_buf64 oob64;
int ret;
oob64.start = pos;
oob64.length = view->info.oobsize;
oob64.usr_ptr = (uintptr_t)buffer;
ret = ioctl(view->fd, MEMREADOOB64, &oob64);
if(ret < 0)
perror("MEMREADOOB64");
return ret;
}
static void mtd_oob_to_avail(const struct mtd_view *view,
unsigned char *oob,
unsigned char *avail)
{
int i;
int entry;
int offs;
const struct nand_oobfree *oobfree;
offs = 0;
for(entry = 0; entry < MTD_MAX_OOBFREE_ENTRIES; entry++) {
oobfree = &view->ecclayout.oobfree[entry];
if(oobfree->length == 0)
break;
for( i = 0; i < oobfree->length; i++) {
avail[offs] = oob[oobfree->offset + i];
offs++;
}
}
}
static void fprintf_bad_blocks(FILE *f, struct mtd_view *view)
{
int i;
int count = 0;
fprintf(f, "Scanning bad blocks\n");
for(i = 0; i < view->nblocks; i++) {
if(mtd_is_bad_block(view, i)) {
if(count == 0)
fprintf(f, "Bad blocks at:");
fprintf(f, " %d", i);
count++;
}
}
fprintf(f, "%sBad block count %d\n", count ? "\n" : "", count);
}
static void fprintf_mtd_view(FILE *f, struct mtd_view *view)
{
int i;
fprintf(f, "name %s\n", view->name);
fprintf(f, "type %u\n", view->info.type);
fprintf(f, "flags %u\n", view->info.flags);
fprintf(f, "size %u\n", view->info.size);
fprintf(f, "erasesize %u\n", view->info.erasesize);
fprintf(f, "writesize %u\n", view->info.writesize);
fprintf(f, "oobsize %u\n", view->info.oobsize);
fprintf(f, "nblocks %u\n", view->nblocks);
fprintf(f, "pagespb %u\n", view->pages_per_block);
fprintf(f, "eccbytes %d\n", view->ecclayout.eccbytes);
fprintf(f, "eccpos");
for(i = 0; i < view->ecclayout.eccbytes; i++)
fprintf(f, " %d",view->ecclayout.eccpos[i]);
fprintf(f, "\n");
fprintf(f, "oobavail %d\n", view->ecclayout.oobavail);
fprintf(f, "oobfree");
for(i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
view->ecclayout.oobfree[i].length > 0; i++)
fprintf(f, " [%d,%d]",
view->ecclayout.oobfree[i].offset,
view->ecclayout.oobfree[i].length);
fprintf(f, "\n");
}
static void fprintf_buffer( FILE *f, unsigned char *buff, int len)
{
while(len > 0) {
fprintf(f, " %02x",*buff);
len--;
buff++;
}
fprintf(f, "\n");
}
static int read_image(const struct mtd_view *view, const struct options *opt)
{
int i;
int b;
loff_t pos;
unsigned char *oob;
unsigned char *data;
unsigned char *avail;
struct yaffs_packed_tags2 pt2;
data = malloc(view->info.writesize);
oob = malloc(view->info.oobsize);
avail = malloc(view->ecclayout.oobavail);
for(b = 0; b < view->nblocks; b++) {
if(opt->skip_bad && mtd_is_bad_block(view, b)) {
fprintf(stdout, "Skipping bad block %d\n", b);
} else {
for(i = b * view->pages_per_block;
i < (b+1) * view->pages_per_block;
i++) {
pos = (loff_t) view->info.writesize * i;
printf("reading page %d at %llx\n", i, pos);
lseek(view->fd, pos, SEEK_SET);
read(view->fd, data, view->info.writesize);
mtd_read_oob(view, oob, pos);
mtd_oob_to_avail(view, oob, avail);
printf("data ");
fprintf_buffer(stdout, data, view->info.writesize);
printf("oob ");
fprintf_buffer(stdout, oob, view->info.oobsize);
printf("avail ");
fprintf_buffer(stdout, avail, view->ecclayout.oobavail);
printf("tags ");
fprintf_buffer(stdout, avail, sizeof(struct yaffs_packed_tags2));
memcpy(&pt2, avail, sizeof(pt2));
printf("seq %d obj %d ch %d n %d\n",
pt2.t.seq_number, pt2.t.obj_id, pt2.t.chunk_id, pt2.t.n_bytes);
}
}
}
return 0;
}
static void help(const char *prob)
{
printf(
"yaffs mem tool\n"
" -d dev-name mtd char device name\n"
" -f file_name Image file name\n"
" -i Inband tags\n"
" -q Quiet\n"
" -r Read image\n"
" -w Write image\n"
"\n"
"%s\n", prob? prob : "");
exit(2);
}
static void read_options(int argc, char *argv[], struct options *opt)
{
int c;
memset(opt, 0, sizeof(*opt));
while ((c = getopt (argc, argv, "d:f:iqrsw")) != -1) {
switch (c) {
case 'd':
opt->dev_name = strdup(optarg);
break;
case 'f':
opt->image_name = strdup(optarg);
break;
case 'q':
opt->quiet = 1;
break;
case 'r':
opt->do_read = 1;
break;
case 'w':
opt->do_write= 1;
break;
case 'i': opt->inband_tags = 1;
break;
case 's': opt->skip_bad = 1;
break;
case '?':
help("Bad option");
}
}
}
int main(int argc, char *argv[])
{
struct mtd_view view;
struct options opt;
read_options(argc, argv, &opt);
if (!opt.dev_name)
help("No device specified");
if(mtd_view_init(&view, opt.dev_name) < 0) {
printf("Error initialising mtd view for %s\n",opt.dev_name);
return 1;
}
if(!opt.quiet) {
fprintf_mtd_view(stdout, &view);
fprintf_bad_blocks(stdout, &view);
}
if(opt.do_read)
read_image(&view, &opt);
return 0;
}