mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-14 15:23:07 +00:00
17cb4b8f32
These functions are part of the Linux 4.6 sync. They are being added before the main sync patch in order to make it easier to address the issue across all NAND drivers (many/most of which do not closely track their Linux counterparts) separately from other merge issues. Signed-off-by: Scott Wood <oss@buserror.net>
461 lines
8.9 KiB
C
461 lines
8.9 KiB
C
/*
|
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
|
*
|
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
|
* for Toby Churchill Ltd and Brightstar Engineering
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* yaffscfg.c The configuration for the "direct" use of yaffs.
|
|
*
|
|
* This is set up for u-boot.
|
|
*
|
|
* This version now uses the ydevconfig mechanism to set up partitions.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <div64.h>
|
|
|
|
#include <config.h>
|
|
#include "nand.h"
|
|
#include "yaffscfg.h"
|
|
#include "yaffsfs.h"
|
|
#include "yaffs_packedtags2.h"
|
|
#include "yaffs_mtdif.h"
|
|
#include "yaffs_mtdif2.h"
|
|
#if 0
|
|
#include <errno.h>
|
|
#else
|
|
#include "malloc.h"
|
|
#endif
|
|
|
|
unsigned yaffs_trace_mask = 0x0; /* Disable logging */
|
|
static int yaffs_errno;
|
|
|
|
|
|
void yaffs_bug_fn(const char *fn, int n)
|
|
{
|
|
printf("yaffs bug at %s:%d\n", fn, n);
|
|
}
|
|
|
|
void *yaffsfs_malloc(size_t x)
|
|
{
|
|
return malloc(x);
|
|
}
|
|
|
|
void yaffsfs_free(void *x)
|
|
{
|
|
free(x);
|
|
}
|
|
|
|
void yaffsfs_SetError(int err)
|
|
{
|
|
yaffs_errno = err;
|
|
}
|
|
|
|
int yaffsfs_GetLastError(void)
|
|
{
|
|
return yaffs_errno;
|
|
}
|
|
|
|
|
|
int yaffsfs_GetError(void)
|
|
{
|
|
return yaffs_errno;
|
|
}
|
|
|
|
void yaffsfs_Lock(void)
|
|
{
|
|
}
|
|
|
|
void yaffsfs_Unlock(void)
|
|
{
|
|
}
|
|
|
|
__u32 yaffsfs_CurrentTime(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void *yaffs_malloc(size_t size)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
void yaffs_free(void *ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
void yaffsfs_LocalInitialisation(void)
|
|
{
|
|
/* No locking used */
|
|
}
|
|
|
|
|
|
static const char *yaffs_file_type_str(struct yaffs_stat *stat)
|
|
{
|
|
switch (stat->st_mode & S_IFMT) {
|
|
case S_IFREG: return "regular file";
|
|
case S_IFDIR: return "directory";
|
|
case S_IFLNK: return "symlink";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
static const char *yaffs_error_str(void)
|
|
{
|
|
int error = yaffsfs_GetLastError();
|
|
|
|
if (error < 0)
|
|
error = -error;
|
|
|
|
switch (error) {
|
|
case EBUSY: return "Busy";
|
|
case ENODEV: return "No such device";
|
|
case EINVAL: return "Invalid parameter";
|
|
case ENFILE: return "Too many open files";
|
|
case EBADF: return "Bad handle";
|
|
case EACCES: return "Wrong permissions";
|
|
case EXDEV: return "Not on same device";
|
|
case ENOENT: return "No such entry";
|
|
case ENOSPC: return "Device full";
|
|
case EROFS: return "Read only file system";
|
|
case ERANGE: return "Range error";
|
|
case ENOTEMPTY: return "Not empty";
|
|
case ENAMETOOLONG: return "Name too long";
|
|
case ENOMEM: return "Out of memory";
|
|
case EFAULT: return "Fault";
|
|
case EEXIST: return "Name exists";
|
|
case ENOTDIR: return "Not a directory";
|
|
case EISDIR: return "Not permitted on a directory";
|
|
case ELOOP: return "Symlink loop";
|
|
case 0: return "No error";
|
|
default: return "Unknown error";
|
|
}
|
|
}
|
|
|
|
void cmd_yaffs_tracemask(unsigned set, unsigned mask)
|
|
{
|
|
if (set)
|
|
yaffs_trace_mask = mask;
|
|
|
|
printf("yaffs trace mask: %08x\n", yaffs_trace_mask);
|
|
}
|
|
|
|
static int yaffs_regions_overlap(int a, int b, int x, int y)
|
|
{
|
|
return (a <= x && x <= b) ||
|
|
(a <= y && y <= b) ||
|
|
(x <= a && a <= y) ||
|
|
(x <= b && b <= y);
|
|
}
|
|
|
|
void cmd_yaffs_devconfig(char *_mp, int flash_dev,
|
|
int start_block, int end_block)
|
|
{
|
|
struct mtd_info *mtd = NULL;
|
|
struct yaffs_dev *dev = NULL;
|
|
struct yaffs_dev *chk;
|
|
char *mp = NULL;
|
|
struct nand_chip *chip;
|
|
|
|
dev = calloc(1, sizeof(*dev));
|
|
mp = strdup(_mp);
|
|
|
|
mtd = nand_info[flash_dev];
|
|
|
|
if (!dev || !mp) {
|
|
/* Alloc error */
|
|
printf("Failed to allocate memory\n");
|
|
goto err;
|
|
}
|
|
|
|
if (flash_dev >= CONFIG_SYS_MAX_NAND_DEVICE) {
|
|
printf("Flash device invalid\n");
|
|
goto err;
|
|
}
|
|
|
|
if (end_block == 0)
|
|
end_block = lldiv(mtd->size, mtd->erasesize - 1);
|
|
|
|
if (end_block < start_block) {
|
|
printf("Bad start/end\n");
|
|
goto err;
|
|
}
|
|
|
|
chip = mtd_to_nand(mtd);
|
|
|
|
/* Check for any conflicts */
|
|
yaffs_dev_rewind();
|
|
while (1) {
|
|
chk = yaffs_next_dev();
|
|
if (!chk)
|
|
break;
|
|
if (strcmp(chk->param.name, mp) == 0) {
|
|
printf("Mount point name already used\n");
|
|
goto err;
|
|
}
|
|
if (chk->driver_context == mtd &&
|
|
yaffs_regions_overlap(
|
|
chk->param.start_block, chk->param.end_block,
|
|
start_block, end_block)) {
|
|
printf("Region overlaps with partition %s\n",
|
|
chk->param.name);
|
|
goto err;
|
|
}
|
|
|
|
}
|
|
|
|
/* Seems sane, so configure */
|
|
memset(dev, 0, sizeof(*dev));
|
|
dev->param.name = mp;
|
|
dev->driver_context = mtd;
|
|
dev->param.start_block = start_block;
|
|
dev->param.end_block = end_block;
|
|
dev->param.chunks_per_block = mtd->erasesize / mtd->writesize;
|
|
dev->param.total_bytes_per_chunk = mtd->writesize;
|
|
dev->param.is_yaffs2 = 1;
|
|
dev->param.use_nand_ecc = 1;
|
|
dev->param.n_reserved_blocks = 5;
|
|
if (chip->ecc.layout->oobavail < sizeof(struct yaffs_packed_tags2))
|
|
dev->param.inband_tags = 1;
|
|
dev->param.n_caches = 10;
|
|
dev->param.write_chunk_tags_fn = nandmtd2_write_chunk_tags;
|
|
dev->param.read_chunk_tags_fn = nandmtd2_read_chunk_tags;
|
|
dev->param.erase_fn = nandmtd_EraseBlockInNAND;
|
|
dev->param.initialise_flash_fn = nandmtd_InitialiseNAND;
|
|
dev->param.bad_block_fn = nandmtd2_MarkNANDBlockBad;
|
|
dev->param.query_block_fn = nandmtd2_QueryNANDBlock;
|
|
|
|
yaffs_add_device(dev);
|
|
|
|
printf("Configures yaffs mount %s: dev %d start block %d, end block %d %s\n",
|
|
mp, flash_dev, start_block, end_block,
|
|
dev->param.inband_tags ? "using inband tags" : "");
|
|
return;
|
|
|
|
err:
|
|
free(dev);
|
|
free(mp);
|
|
}
|
|
|
|
void cmd_yaffs_dev_ls(void)
|
|
{
|
|
struct yaffs_dev *dev;
|
|
int flash_dev;
|
|
int free_space;
|
|
|
|
yaffs_dev_rewind();
|
|
|
|
while (1) {
|
|
dev = yaffs_next_dev();
|
|
if (!dev)
|
|
return;
|
|
flash_dev = nand_mtd_to_devnum(dev->driver_context);
|
|
printf("%-10s %5d 0x%05x 0x%05x %s",
|
|
dev->param.name, flash_dev,
|
|
dev->param.start_block, dev->param.end_block,
|
|
dev->param.inband_tags ? "using inband tags, " : "");
|
|
|
|
free_space = yaffs_freespace(dev->param.name);
|
|
if (free_space < 0)
|
|
printf("not mounted\n");
|
|
else
|
|
printf("free 0x%x\n", free_space);
|
|
|
|
}
|
|
}
|
|
|
|
void make_a_file(char *yaffsName, char bval, int sizeOfFile)
|
|
{
|
|
int outh;
|
|
int i;
|
|
unsigned char buffer[100];
|
|
|
|
outh = yaffs_open(yaffsName,
|
|
O_CREAT | O_RDWR | O_TRUNC,
|
|
S_IREAD | S_IWRITE);
|
|
if (outh < 0) {
|
|
printf("Error opening file: %d. %s\n", outh, yaffs_error_str());
|
|
return;
|
|
}
|
|
|
|
memset(buffer, bval, 100);
|
|
|
|
do {
|
|
i = sizeOfFile;
|
|
if (i > 100)
|
|
i = 100;
|
|
sizeOfFile -= i;
|
|
|
|
yaffs_write(outh, buffer, i);
|
|
|
|
} while (sizeOfFile > 0);
|
|
|
|
|
|
yaffs_close(outh);
|
|
}
|
|
|
|
void read_a_file(char *fn)
|
|
{
|
|
int h;
|
|
int i = 0;
|
|
unsigned char b;
|
|
|
|
h = yaffs_open(fn, O_RDWR, 0);
|
|
if (h < 0) {
|
|
printf("File not found\n");
|
|
return;
|
|
}
|
|
|
|
while (yaffs_read(h, &b, 1) > 0) {
|
|
printf("%02x ", b);
|
|
i++;
|
|
if (i > 32) {
|
|
printf("\n");
|
|
i = 0;;
|
|
}
|
|
}
|
|
printf("\n");
|
|
yaffs_close(h);
|
|
}
|
|
|
|
void cmd_yaffs_mount(char *mp)
|
|
{
|
|
int retval = yaffs_mount(mp);
|
|
if (retval < 0)
|
|
printf("Error mounting %s, return value: %d, %s\n", mp,
|
|
yaffsfs_GetError(), yaffs_error_str());
|
|
}
|
|
|
|
|
|
void cmd_yaffs_umount(char *mp)
|
|
{
|
|
if (yaffs_unmount(mp) == -1)
|
|
printf("Error umounting %s, return value: %d, %s\n", mp,
|
|
yaffsfs_GetError(), yaffs_error_str());
|
|
}
|
|
|
|
void cmd_yaffs_write_file(char *yaffsName, char bval, int sizeOfFile)
|
|
{
|
|
make_a_file(yaffsName, bval, sizeOfFile);
|
|
}
|
|
|
|
|
|
void cmd_yaffs_read_file(char *fn)
|
|
{
|
|
read_a_file(fn);
|
|
}
|
|
|
|
|
|
void cmd_yaffs_mread_file(char *fn, char *addr)
|
|
{
|
|
int h;
|
|
struct yaffs_stat s;
|
|
|
|
yaffs_stat(fn, &s);
|
|
|
|
printf("Copy %s to 0x%p... ", fn, addr);
|
|
h = yaffs_open(fn, O_RDWR, 0);
|
|
if (h < 0) {
|
|
printf("File not found\n");
|
|
return;
|
|
}
|
|
|
|
yaffs_read(h, addr, (int)s.st_size);
|
|
printf("\t[DONE]\n");
|
|
|
|
yaffs_close(h);
|
|
}
|
|
|
|
|
|
void cmd_yaffs_mwrite_file(char *fn, char *addr, int size)
|
|
{
|
|
int outh;
|
|
|
|
outh = yaffs_open(fn, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
|
|
if (outh < 0)
|
|
printf("Error opening file: %d, %s\n", outh, yaffs_error_str());
|
|
|
|
yaffs_write(outh, addr, size);
|
|
|
|
yaffs_close(outh);
|
|
}
|
|
|
|
|
|
void cmd_yaffs_ls(const char *mountpt, int longlist)
|
|
{
|
|
int i;
|
|
yaffs_DIR *d;
|
|
struct yaffs_dirent *de;
|
|
struct yaffs_stat stat;
|
|
char tempstr[255];
|
|
|
|
d = yaffs_opendir(mountpt);
|
|
|
|
if (!d) {
|
|
printf("opendir failed, %s\n", yaffs_error_str());
|
|
return;
|
|
}
|
|
|
|
for (i = 0; (de = yaffs_readdir(d)) != NULL; i++) {
|
|
if (longlist) {
|
|
sprintf(tempstr, "%s/%s", mountpt, de->d_name);
|
|
yaffs_lstat(tempstr, &stat);
|
|
printf("%-25s\t%7ld",
|
|
de->d_name,
|
|
(long)stat.st_size);
|
|
printf(" %5d %s\n",
|
|
stat.st_ino,
|
|
yaffs_file_type_str(&stat));
|
|
} else {
|
|
printf("%s\n", de->d_name);
|
|
}
|
|
}
|
|
|
|
yaffs_closedir(d);
|
|
}
|
|
|
|
|
|
void cmd_yaffs_mkdir(const char *dir)
|
|
{
|
|
int retval = yaffs_mkdir(dir, 0);
|
|
|
|
if (retval < 0)
|
|
printf("yaffs_mkdir returning error: %d, %s\n",
|
|
retval, yaffs_error_str());
|
|
}
|
|
|
|
void cmd_yaffs_rmdir(const char *dir)
|
|
{
|
|
int retval = yaffs_rmdir(dir);
|
|
|
|
if (retval < 0)
|
|
printf("yaffs_rmdir returning error: %d, %s\n",
|
|
retval, yaffs_error_str());
|
|
}
|
|
|
|
void cmd_yaffs_rm(const char *path)
|
|
{
|
|
int retval = yaffs_unlink(path);
|
|
|
|
if (retval < 0)
|
|
printf("yaffs_unlink returning error: %d, %s\n",
|
|
retval, yaffs_error_str());
|
|
}
|
|
|
|
void cmd_yaffs_mv(const char *oldPath, const char *newPath)
|
|
{
|
|
int retval = yaffs_rename(newPath, oldPath);
|
|
|
|
if (retval < 0)
|
|
printf("yaffs_unlink returning error: %d, %s\n",
|
|
retval, yaffs_error_str());
|
|
}
|