Re: [Yaffs] Patch to allow mounting of MTD devices by partit…

Top Page
Attachments:
Message as email
+ (text/plain)
+ bse_vde_nand.c (text/x-csrc)
+ bse_devfs.c (text/x-csrc)
+ bse_label.c (text/x-csrc)
Delete this message
Reply to this message
Author: ian@brightstareng.com
Date:  
To: yaffs
Subject: Re: [Yaffs] Patch to allow mounting of MTD devices by partition name
On Friday 25 August 2006 00:03, Andre Renaud wrote:
> I've fiddled that code into yaffs, with one minor change -
> JFFS2 uses ':' to delimit the partition name, that is:
> mount -t jffs2 mtd:PartitionName /mnt
> However busybox assumes that any mount device containing a ':'
> is an NFS mount, and changes the type to nfs even if you
> explicitly set it. To avoid this I've used '.' instead, ie:
> mount -t yaffs mtd.PartitionName /mnt
>
> I'm not sure if anyone else would find this useful or not, but
> I've attached the patch if so.


We implemented similar functionality at the MTD layer: we
register an mtd_notifier callback before we call
add_mtd_partitions in our NAND hook-up module (aka MTD map
driver), and the callback creates symlinks for each
partition/device. This is done for both the char and block
interfaces.

So /dev/mtd/myfs -> 4, for example, where 'myfs' comes from the
partition name. Using the 'named' partitions keeps all the
user-space scripts and configuration more sane. [An earlier
implementation was done using a shell script to grab the
partition names from 'dmesg' and create the links].

The use of 'logical' names for the devices is generally useful,
and not just limited to the 'mount' case. Perhaps MTD will
support this one day.

Attached is some code to illustrate this approach, the 'MTD map
driver' code is from one of our platforms. We have a proprietary
flash label format, and it's included just so you can follow the
flow through bse_mtd_label() to add_mtd_partitions(), which
invokes the notifier. Also included are some helper functions we
use to fix-up code that relied on DEVFS which has become extinct
in the 2.6 codebase; this creates the symlinks. Use these files
as an example only - don't expect to build/use them verbatim.
The (missing) include files mainly contain function prototypes.

-imcd
/*
* NAND I/O for BSE LH7952X
* Copyright (c) 2006 Bright Star Engineering.
* Released under GPLv2.
*/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/syscalls.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include "bse_mtdlabel.h"
#include "bse_priv.h"
#include "bse_devlib.h"

static int allow_oob_erase;
module_param(allow_oob_erase,int,0644);
static int label_address;
module_param(label_address,int,0444);

// for Samsung parts chip-enable-DO-care parts
#define CE_DO_CARE 1

#if CE_DO_CARE
static volatile u_int32_t *lh_muxctl_14 = (void *)0xfffe5000+0x68;
static volatile u_int32_t *lh_port_m = (void *)0xfffd9000;
#endif

/* The NAND address of the LABEL is recorded in two u_int32_t
* integers at the end (512-8) of the BSE asset data struct.
* The asset data is at the start of the 2nd NAND block, or
* next good block if that block is marked bad.
*/
#define LABEL_INDEX_ADDR(blocksize) ((blocksize)+512-8)

static struct info {
    struct mtd_info nand_mtd[1];
    struct nand_chip nand_chip[1];
    int gpio_chip_enable;
} myinfo[1];


static void
nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
    struct nand_chip* chip = (struct nand_chip*) mtd->priv;


    switch (cmd) {
    case NAND_CTL_SETCLE:
        chip->IO_ADDR_W = (void *)(NAND_VIRT + (1<<4));
        break;


    case NAND_CTL_SETALE:
        chip->IO_ADDR_W = (void *)(NAND_VIRT + (1<<3));
        break;


#if CE_DO_CARE
    case NAND_CTL_SETNCE:
        if (myinfo->gpio_chip_enable) {
            lh_port_m[0] &= ~1;    // PM0=0
        }
        break;


    case NAND_CTL_CLRNCE:
        if (myinfo->gpio_chip_enable) {
            lh_port_m[0] |= 1;    // PM0=1
        }
        break;
#endif
    default:
        chip->IO_ADDR_W = (void *)NAND_VIRT;
        break;
    }
}


static int __init
null_scan_bbt(struct mtd_info *mtd)
{
    return 0;
}


/* Create entries in /dev/mtd... for device partitions
 */
static void
bse_add_dev_entries(struct mtd_info *mtd)
{
    int index = mtd->index;
    int minor = index;
    char linkname[64];


    bse_mkdev(MKDEV(MTD_BLOCK_MAJOR,minor), S_IFBLK|0600,
        "/dev/mtdblock/%d", index);


    bse_mkdev(MKDEV(MTD_CHAR_MAJOR,minor*2), S_IFCHR|0600,
        "/dev/mtd/%d", index);


    snprintf(linkname, sizeof(linkname), "/dev/mtdblock/%s", mtd->name);
    bse_mksymlink(linkname, "%d", index);


    snprintf(linkname, sizeof(linkname), "/dev/mtd/%s", mtd->name);
    bse_mksymlink(linkname, "%d", index);
}


static void
bse_remove_dev_entries(struct mtd_info *mtd)
{
    bse_rmdev("/dev/mtdblock/%d", mtd->index);
    bse_rmdev("/dev/mtdblock/%s", mtd->name);
    bse_rmdev("/dev/mtd/%d", mtd->index);
    bse_rmdev("/dev/mtd/%s", mtd->name);


    /* release memory from bse_strdup() */
    kfree(mtd->name);
}


static void
bse_mtd_add_handler(struct mtd_info *mtd)
{
    //printk("bse_mtd_add_handler: %s\n", mtd->name);
    bse_add_dev_entries(mtd);
}


static void
bse_mtd_remove_handler(struct mtd_info *mtd)
{
    //printk("bse_mtd_removed_handler: %s\n", mtd->name);
    bse_remove_dev_entries(mtd);
}


static struct mtd_notifier bse_mtd_notifier = {
    add: bse_mtd_add_handler,
    remove: bse_mtd_remove_handler,
};


char *
bse_strdup(const char *s)
{
        char *rv = kmalloc(strlen(s)+1, GFP_KERNEL);
        if (rv)
                strcpy(rv, s);
        return rv;
}


static __init int
bse_read_label(struct mtd_info *mtd)
{
    int pgsize, blksize;
    u_int32_t label_addrs[2];
    int len = 0;
    int try;


    if (label_address) {
        /* use given address to locate label */
        return bse_mtd_label(mtd, label_address, FL_magic1) ? 0 : -1;
    }


    pgsize = mtd->oobblock;
    blksize = mtd->erasesize;


    /* Look for LABEL indexs at LABEL_INDEX_ADDR and the next few blocks.
     * We do this in case the normal location is a bad block.
     */
    for (try = 0; try < 3; try++) {
        mtd->read(mtd, LABEL_INDEX_ADDR(blksize)+try*blksize, 8, &len,
            (u_char *)label_addrs);
        if (len != 8) {
            continue;
        }
        /* check for page alignment */
        if (label_addrs[1] & (pgsize-1)) {
            continue;
        }
        /* look for a NAND label at two locations */
        if (bse_mtd_label(mtd, label_addrs[1], FL_magic1) ||
            bse_mtd_label(mtd, label_addrs[0], FL_magic1)) {
            return 0;
        }
    }
    return -1;
}


/* We use our own oobinfo data for the STM 128Mbyte large page device.
 * It has factory bad block indicators in the 1st and 6th bytes.
 * MTD's oobinfo data for these type of parts is different.
 *
 * Large page (2k) devices have 64 bytes of 'spare' (oob) data.
 * We provide 34 bytes of 'oobfree' data for user/filesystem use;
 * bytes 6-40 inclusive.  YAFFS2 needs 28 bytes for its tags; this
 * is stored in the 'oobfree' area.  24 bytes of ECC for the 2K page
 * is stored in 'eccpos', see below; this is managed by MTD.
 */
static struct nand_oobinfo nand_oob_64 = {
    .useecc = MTD_NANDECC_AUTOPLACE,
    .eccbytes = 24,
    .eccpos = {
        40, 41, 42, 43, 44, 45, 46, 47,
        48, 49, 50, 51, 52, 53, 54, 55,
        56, 57, 58, 59, 60, 61, 62, 63
    },
    .oobfree = { {6, 34} }
};


/* Examine the 6th byte of the oob area in the 1st and 2nd pages of
 * the specified block.
 */
static int
nand_small_block_bad(struct mtd_info *mtd, loff_t ofs, int dummy)
{
    const struct nand_chip *chip = mtd->priv;
    int addr = (int)ofs & ~(mtd->erasesize-1); /* 1st page in block */
    int page = addr >> chip->page_shift;


    /* Hack to allow us to wipe clean ALL BB markers! */
    if (allow_oob_erase)
        return 0;


    chip->cmdfunc(mtd, NAND_CMD_READOOB, 5, page);
    if (chip->read_byte(mtd) != 0xff) {
        return 1;
    }
    chip->cmdfunc(mtd, NAND_CMD_READOOB, 5, page+1);
    return (chip->read_byte(mtd) != 0xff);
}


/* Examine the 1st and 6th byte of the oob area in the 1st page of
 * the specified block.  Ref. STMicroelectronics datasheet page 36.
 */
static int
nand_large_block_bad(struct mtd_info *mtd, loff_t ofs, int dummy)
{
    const struct nand_chip *chip = mtd->priv;
    int addr = (int)ofs & ~(mtd->erasesize-1); /* 1st page in block */
    int page = addr >> chip->page_shift;
    unsigned char buf[6];


    /* Hack to allow us to wipe clean ALL BB markers! */
    if (allow_oob_erase)
        return 0;


    chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
    chip->read_buf(mtd, buf, 6);
    return (buf[0] != 0xff || buf[5] != 0xff);
}


static int __init
nand_init(void)
{
    struct mtd_info * const mtd = myinfo->nand_mtd;
    struct nand_chip * const chip = myinfo->nand_chip;
    struct mtd_partition mp[1];
    char extname[32];


    printk(KERN_INFO "NAND: BSE LH7952X NAND I/O module\n");


    mtd->priv = chip;
    mtd->name = "nand";


    chip->IO_ADDR_R = (void __iomem*) NAND_VIRT;
    chip->IO_ADDR_W = (void __iomem*) NAND_VIRT;
    chip->hwcontrol = nand_hwcontrol;
    chip->eccmode = NAND_ECC_SOFT;


    /* use 30uS command delay time until we know the type */
    chip->chip_delay = 30;
    chip->scan_bbt = null_scan_bbt;
    /* default to small block method until we know the type */
    chip->block_bad = nand_small_block_bad;


    /* Scan to find existence of the device */
    if (nand_scan(mtd, 1)) {
        return -ENXIO;
    }


    /* Change the owner to us, not the 'mtd' module (as set by nand_scan)
     * so we stay locked in core while devices are in use.
     */
    mtd->owner = THIS_MODULE;


    /* Which chip do we have?  */
    switch (chip->chipsize>>20) {
    case 32:
        /* 32MB devices can run faster... */
        chip->chip_delay = 15;
#if CE_DO_CARE
        /* Samsung part is not "chip-enable-don't-care" so
         * configure and drive PM0 (flash #CE) using GPIO.
         */
        lh_port_m[0] |= 1;        // bit=1
        lh_port_m[2] |= 1;        // dir=output
        lh_muxctl_14[0] &= ~(3<<8);    // PM0=nCS0
        lh_muxctl_14[0] |= (1<<8);    // PM0=GPIO


        myinfo->gpio_chip_enable = 1;
        printk("nand: using GPIO for NAND #CE\n");
#endif
        break;


    case 64:
        /* 64MB devices can run a little faster... */
        chip->chip_delay = 20;
        break;


    case 128:
        /* large page device */
        chip->block_bad = nand_large_block_bad;


        /* Fix up OOB info for large page ST NAND */
        chip->autooob = &nand_oob_64;


        /* use the length of the first (only) oobfree region */
        mtd->oobavail = nand_oob_64.oobfree[0][1];
        break;


    default:
        printk(KERN_ERR "nand: unsupported chip size\n");
        return -ENXIO;
    }


    /* add MTD notifier for our mkdev mechanism */
    register_mtd_user(&bse_mtd_notifier);


#ifdef CONFIG_MTD_PARTITIONS
    /* Always create a read-only partition that spans the full extent.
     * This can be switched to rw and used to update a label.
     */
    memset(mp, 0, sizeof(*mp));
    snprintf(extname, sizeof(extname), "%s-ext", mtd->name);
    mp->name = bse_strdup(extname);
    mp->size = mtd->size;
//    mp->mask_flags = MTD_WRITEABLE;
    add_mtd_partitions(mtd, mp, 1);


    /* Find label */
    if (bse_read_label(mtd) == 0) {
        return 0;
    }
#else
    /* just add device if we're not doing partitions */
    add_mtd_device(mtd);
#endif
    return 0;
}


/*
 * Clean up routine
 */
static void __exit
nand_cleanup(void)
{
#ifdef CONFIG_MTD_PARTITIONS
    del_mtd_partitions(myinfo->nand_mtd);
#else
    /* remove MTD device */
    del_mtd_device(myinfo->nand_mtd);
#endif
    /* unlink from MTD notifier */
    unregister_mtd_user(&bse_mtd_notifier);


#if CE_DO_CARE
    if (myinfo->gpio_chip_enable) {
        /* restore nCS0 functionality */
        lh_muxctl_14[0] &= ~(3<<8);    // PM0=nCS0
    }
#endif
}


#ifdef STANDALONE_MODULE
module_init(nand_init);
module_exit(nand_cleanup);

MODULE_LICENSE("BSE");
MODULE_AUTHOR("BSE");
MODULE_DESCRIPTION("BSE LH7952X NAND I/O");
MODULE_VERSION("1.1");
#endif
/*
* BSE DEVFS substitute for 2.6.
* Copyright (c) 2006 Bright Star Engineering.
* Released under GPLv2.
*/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>    // for get_fs etc


#include "bse_devlib.h"

int
bse_mkdev(dev_t dev, mode_t mode, const char *fmt, ...)
{
    va_list va;
    mm_segment_t oldfs;
    char namebuf[64];
    char *s, *t;
    int err;


    va_start(va, fmt);
    vsnprintf(namebuf, sizeof(namebuf), fmt, va);
    va_end(va);


    oldfs = get_fs();
    set_fs(get_ds());


    s = strchr(namebuf, '/')+1;
    for ( ;(t = strchr(s,'/')); s = t) {
        *t = 0;
        (void) sys_mkdir(namebuf, 0755);
        *t++ = '/';
    }
    err = sys_mknod(namebuf, mode, old_encode_dev(dev));
    set_fs(oldfs);
    //printk("bse_mkdev: %s, err %d\n", namebuf, err);


    return err;
}


int
bse_rmdev(const char *fmt, ...)
{
    va_list va;
    mm_segment_t oldfs;
    char namebuf[64];
    char *s;
    int err;


    va_start(va, fmt);
    vsnprintf(namebuf, sizeof(namebuf), fmt, va);
    va_end(va);


    oldfs = get_fs();
    set_fs(get_ds());


    err = sys_unlink(namebuf);
    for (s = strrchr(namebuf, '/'); !err && s; s = strrchr(namebuf, '/')) {
        *s = 0;
        if (sys_rmdir(namebuf)) {
            break;
        }
    }
    set_fs(oldfs);
    //printk("bse_rmdev: %s, err %d\n", namebuf, err);


    return err;
}


int
bse_mksymlink(const char *newname, const char *fmt, ...)
{
    va_list va;
    mm_segment_t oldfs;
    char namebuf[64];
    int err;


    va_start(va, fmt);
    vsnprintf(namebuf, sizeof(namebuf), fmt, va);
    va_end(va);


    oldfs = get_fs();
    set_fs(get_ds());
    err = sys_symlink(namebuf, newname);
    set_fs(oldfs);
    //printk("bse_mksymlink: %s->%s, err %d\n", newname, namebuf, err);


    return err;
}


EXPORT_SYMBOL(bse_mkdev);
EXPORT_SYMBOL(bse_rmdev);
EXPORT_SYMBOL(bse_mksymlink);

MODULE_AUTHOR("Bright Star Engineering");
MODULE_LICENSE("BSE");
MODULE_VERSION("1.2");
/*
* BSE NAND Label support.
* Copyright (c) 2006 Bright Star Engineering.
* Released under GPLv2.
*/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>

#include "bse_mtdlabel.h"
#include "bse_priv.h"

/* From rfc1950, Mark Adler */
static unsigned long
update_adler32(unsigned long adler, void *abuf, size_t len)
{
    const int BASE = 65521; /* largest prime smaller than 65536 */
    unsigned char *buf = abuf;
    unsigned long s1 = adler & 0xffff;
    unsigned long s2 = (adler >> 16) & 0xffff;
    int n;


    for (n = 0; n < len; n++) {
        s1 = (s1 + buf[n]) % BASE;
        s2 = (s2 + s1)     % BASE;
    }
    return (s2 << 16) + s1;
}


static unsigned long
label_adler32(struct fl_label_t *label)
{
    return update_adler32(1, (char *)label+8, sizeof(*label)-8);
}


/* Generate MTD partitions for each BSE flash label partition */
static int
bse_make_partitions(struct mtd_info *mtd, struct fl_label_t *label)
{
    struct mtd_partition mp[1];
    int count = 0;
    int n;


    for (n = 0; n < FL_nparts; n++) {
        if (!label->part[n].end)
            continue;


        memset(mp, 0, sizeof(*mp));
        mp->name = bse_strdup(label->part[n].name);
        mp->size = label->part[n].end - label->part[n].start;
        mp->offset = label->part[n].start;
        if (label->part[n].flags & FL_flag_ro) {
            /* Write-protected */
            mp->mask_flags = MTD_WRITEABLE;
        }
        add_mtd_partitions(mtd, mp, 1);
        count++;
    }
    return count;
}


/* Look for a BSE flash label and create MTD partitions */
int
bse_mtd_label(struct mtd_info *mtd, size_t offset, u_int32_t magic)
{
    struct fl_label_t label[1];
    size_t n;


    (void) mtd->read(mtd, offset, FL_size, &n, (u_char *) label);


    if (n != FL_size || label->magic1 != magic ||
            label_adler32(label) != label->adler32) {
        printk(KERN_INFO "mtd: %s: no label at %#x\n",
            mtd->name, offset);
        memset(label, 0, sizeof label);
        return 0;
    }
    printk(KERN_INFO "mtd: %s: using label at %#x\n", mtd->name, offset);
    n = bse_make_partitions(mtd, label);
    return n;
}