[Yaffs] yaffs2 flash image handling tools

Top Page
Attachments:
Message as email
+ (text/plain)
+ ymemtool.c (text/x-csrc)
Delete this message
Reply to this message
Author: Charles Manning
Date:  
To: yaffs
Subject: [Yaffs] yaffs2 flash image handling tools
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 <>
*
* 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;
}