root/drivers/mtd/tests/mtd_test.c
// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) "mtd_test: " fmt

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/printk.h>

#include "mtd_test.h"

int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
{
        int err;
        struct erase_info ei;
        loff_t addr = (loff_t)ebnum * mtd->erasesize;

        memset(&ei, 0, sizeof(struct erase_info));
        ei.addr = addr;
        ei.len  = mtd->erasesize;

        err = mtd_erase(mtd, &ei);
        if (err) {
                pr_info("error %d while erasing EB %d\n", err, ebnum);
                return err;
        }

        return 0;
}
EXPORT_SYMBOL_GPL(mtdtest_erase_eraseblock);

static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum)
{
        int ret;
        loff_t addr = (loff_t)ebnum * mtd->erasesize;

        ret = mtd_block_isbad(mtd, addr);
        if (ret)
                pr_info("block %d is bad\n", ebnum);

        return ret;
}

int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
                                        unsigned int eb, int ebcnt)
{
        int i, bad = 0;

        if (!mtd_can_have_bb(mtd))
                return 0;

        pr_info("scanning for bad eraseblocks\n");
        for (i = 0; i < ebcnt; ++i) {
                bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0;
                if (bbt[i])
                        bad += 1;
                cond_resched();
        }
        pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);

        return 0;
}
EXPORT_SYMBOL_GPL(mtdtest_scan_for_bad_eraseblocks);

int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
                                unsigned int eb, int ebcnt)
{
        int err;
        unsigned int i;

        for (i = 0; i < ebcnt; ++i) {
                if (bbt[i])
                        continue;
                err = mtdtest_erase_eraseblock(mtd, eb + i);
                if (err)
                        return err;
                cond_resched();
        }

        return 0;
}
EXPORT_SYMBOL_GPL(mtdtest_erase_good_eraseblocks);

int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf)
{
        size_t read;
        int err;

        err = mtd_read(mtd, addr, size, &read, buf);
        /* Ignore corrected ECC errors */
        if (mtd_is_bitflip(err))
                err = 0;
        if (!err && read != size)
                err = -EIO;
        if (err)
                pr_err("error: read failed at %#llx\n", addr);

        return err;
}
EXPORT_SYMBOL_GPL(mtdtest_read);

int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size,
                const void *buf)
{
        size_t written;
        int err;

        err = mtd_write(mtd, addr, size, &written, buf);
        if (!err && written != size)
                err = -EIO;
        if (err)
                pr_err("error: write failed at %#llx\n", addr);

        return err;
}
EXPORT_SYMBOL_GPL(mtdtest_write);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MTD function test helpers");
MODULE_AUTHOR("Akinobu Mita");