Import work done under project/nand (@235533) into head.
The NAND Flash environment consists of several distinct components: - NAND framework (drivers harness for NAND controllers and NAND chips) - NAND simulator (NANDsim) - NAND file system (NAND FS) - Companion tools and utilities - Documentation (manual pages) This work is still experimental. Please use with caution. Obtained from: Semihalf Supported by: FreeBSD Foundation, Juniper Networks
This commit is contained in:
parent
cac3dcd5f9
commit
7f725bcd5c
@ -122,6 +122,8 @@
|
||||
mpilib
|
||||
..
|
||||
..
|
||||
nand
|
||||
..
|
||||
ofw
|
||||
..
|
||||
pbio
|
||||
@ -154,6 +156,8 @@
|
||||
..
|
||||
msdosfs
|
||||
..
|
||||
nandfs
|
||||
..
|
||||
nfs
|
||||
..
|
||||
ntfs
|
||||
|
@ -43,7 +43,7 @@ LSUBDIRS= cam/ata cam/scsi \
|
||||
dev/ic dev/iicbus ${_dev_ieee488} dev/io dev/lmc dev/mfi dev/ofw \
|
||||
dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus \
|
||||
dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \
|
||||
fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/ntfs fs/nullfs \
|
||||
fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/ntfs fs/nullfs \
|
||||
${_fs_nwfs} fs/portalfs fs/procfs fs/smbfs fs/udf fs/unionfs \
|
||||
geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \
|
||||
geom/mirror geom/mountver geom/multipath geom/nop \
|
||||
@ -157,7 +157,7 @@ copies:
|
||||
done
|
||||
.endif
|
||||
.endfor
|
||||
.for i in ${LDIRS} ${LSUBDIRS:Ndev/acpica:Ndev/bktr} ${LSUBSUBDIRS}
|
||||
.for i in ${LDIRS} ${LSUBDIRS:Ndev/acpica:Ndev/bktr:Ndev/nand} ${LSUBSUBDIRS}
|
||||
cd ${.CURDIR}/../sys; \
|
||||
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 $i/*.h \
|
||||
${DESTDIR}${INCLUDEDIR}/$i
|
||||
@ -168,6 +168,13 @@ copies:
|
||||
cd ${.CURDIR}/../sys/dev/bktr; \
|
||||
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ioctl_*.h \
|
||||
${DESTDIR}${INCLUDEDIR}/dev/bktr
|
||||
.if ${MK_NAND} != "no"
|
||||
cd ${.CURDIR}/../sys/dev/nand; \
|
||||
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 nandsim.h \
|
||||
${DESTDIR}${INCLUDEDIR}/dev/nand; \
|
||||
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 nand_dev.h \
|
||||
${DESTDIR}${INCLUDEDIR}/dev/nand
|
||||
.endif
|
||||
cd ${.CURDIR}/../sys/contrib/altq/altq; \
|
||||
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 *.h \
|
||||
${DESTDIR}${INCLUDEDIR}/altq
|
||||
@ -224,7 +231,7 @@ symlinks:
|
||||
ln -fs ../../../sys/$i/$$h ${DESTDIR}${INCLUDEDIR}/$i; \
|
||||
done
|
||||
.endfor
|
||||
.for i in ${LSUBDIRS:Ndev/acpica:Ndev/bktr}
|
||||
.for i in ${LSUBDIRS:Ndev/acpica:Ndev/bktr:Ndev/nand}
|
||||
cd ${.CURDIR}/../sys/$i; \
|
||||
for h in *.h; do \
|
||||
ln -fs ../../../../sys/$i/$$h ${DESTDIR}${INCLUDEDIR}/$i; \
|
||||
@ -240,6 +247,13 @@ symlinks:
|
||||
ln -fs ../../../../sys/dev/bktr/$$h \
|
||||
${DESTDIR}${INCLUDEDIR}/dev/bktr; \
|
||||
done
|
||||
.if ${MK_NAND} != "no"
|
||||
cd ${.CURDIR}/../sys/dev/nand; \
|
||||
for h in nandsim.h nand_dev.h; do \
|
||||
ln -fs ../../../../sys/dev/nand/$$h \
|
||||
${DESTDIR}${INCLUDEDIR}/dev/nand; \
|
||||
done
|
||||
.endif
|
||||
.for i in ${LSUBSUBDIRS}
|
||||
cd ${.CURDIR}/../sys/$i; \
|
||||
for h in *.h; do \
|
||||
|
@ -88,6 +88,7 @@ SUBDIR= ${SUBDIR_ORDERED} \
|
||||
libmemstat \
|
||||
${_libmilter} \
|
||||
${_libmp} \
|
||||
${_libnandfs} \
|
||||
${_libncp} \
|
||||
${_libngatm} \
|
||||
libopie \
|
||||
@ -175,6 +176,10 @@ _libipx= libipx
|
||||
_libthr= libthr
|
||||
.endif
|
||||
|
||||
.if ${MK_NAND} != "no"
|
||||
_libnandfs= libnandfs
|
||||
.endif
|
||||
|
||||
.if ${MK_NETGRAPH} != "no"
|
||||
_libnetgraph= libnetgraph
|
||||
.endif
|
||||
|
9
lib/libnandfs/Makefile
Normal file
9
lib/libnandfs/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# $FreeBSD$
|
||||
|
||||
LIB= nandfs
|
||||
SRCS+= nandfs.c
|
||||
INCS= libnandfs.h
|
||||
|
||||
CFLAGS += -I${.CURDIR}
|
||||
|
||||
.include <bsd.lib.mk>
|
65
lib/libnandfs/libnandfs.h
Normal file
65
lib/libnandfs/libnandfs.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _LIBNANDFS_NANDFS_H
|
||||
#define _LIBNANDFS_NANDFS_H
|
||||
|
||||
struct nandfs {
|
||||
struct nandfs_fsdata n_fsdata;
|
||||
struct nandfs_super_block n_sb;
|
||||
char n_ioc[MNAMELEN];
|
||||
char n_dev[MNAMELEN];
|
||||
int n_iocfd;
|
||||
int n_devfd;
|
||||
int n_flags;
|
||||
char n_errmsg[120];
|
||||
};
|
||||
|
||||
int nandfs_iserror(struct nandfs *);
|
||||
const char *nandfs_errmsg(struct nandfs *);
|
||||
|
||||
void nandfs_init(struct nandfs *, const char *);
|
||||
void nandfs_destroy(struct nandfs *);
|
||||
|
||||
const char *nandfs_dev(struct nandfs *);
|
||||
|
||||
int nandfs_open(struct nandfs *);
|
||||
void nandfs_close(struct nandfs *);
|
||||
|
||||
int nandfs_get_cpstat(struct nandfs *, struct nandfs_cpstat *);
|
||||
|
||||
ssize_t nandfs_get_cp(struct nandfs *, uint64_t,
|
||||
struct nandfs_cpinfo *, size_t);
|
||||
|
||||
ssize_t nandfs_get_snap(struct nandfs *, uint64_t,
|
||||
struct nandfs_cpinfo *, size_t);
|
||||
|
||||
int nandfs_make_snap(struct nandfs *, uint64_t *);
|
||||
int nandfs_delete_snap(struct nandfs *, uint64_t);
|
||||
|
||||
#endif /* _LIBNANDFS_NANDFS_H */
|
247
lib/libnandfs/nandfs.c
Normal file
247
lib/libnandfs/nandfs.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stdint.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <sys/disk.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_fs.h>
|
||||
#include <libnandfs.h>
|
||||
|
||||
#define NANDFS_IS_VALID 0x1
|
||||
#define NANDFS_IS_OPENED 0x2
|
||||
#define NANDFS_IS_OPENED_DEV 0x4
|
||||
#define NANDFS_IS_ERROR 0x8
|
||||
|
||||
#define DEBUG
|
||||
#undef DEBUG
|
||||
#ifdef DEBUG
|
||||
#define NANDFS_DEBUG(fmt, args...) do { \
|
||||
printf("libnandfs:" fmt "\n", ##args); } while (0)
|
||||
#else
|
||||
#define NANDFS_DEBUG(fmt, args...)
|
||||
#endif
|
||||
|
||||
#define NANDFS_ASSERT_VALID(fs) assert((fs)->n_flags & NANDFS_IS_VALID)
|
||||
#define NANDFS_ASSERT_VALID_DEV(fs) \
|
||||
assert(((fs)->n_flags & (NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV)) == \
|
||||
(NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV))
|
||||
|
||||
int
|
||||
nandfs_iserror(struct nandfs *fs)
|
||||
{
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
|
||||
return (fs->n_flags & NANDFS_IS_ERROR);
|
||||
}
|
||||
|
||||
const char *
|
||||
nandfs_errmsg(struct nandfs *fs)
|
||||
{
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
|
||||
assert(nandfs_iserror(fs));
|
||||
assert(fs->n_errmsg);
|
||||
return (fs->n_errmsg);
|
||||
}
|
||||
|
||||
static void
|
||||
nandfs_seterr(struct nandfs *fs, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(fs->n_errmsg, sizeof(fs->n_errmsg), fmt, ap);
|
||||
va_end(ap);
|
||||
fs->n_flags |= NANDFS_IS_ERROR;
|
||||
}
|
||||
|
||||
const char *
|
||||
nandfs_dev(struct nandfs *fs)
|
||||
{
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
return (fs->n_dev);
|
||||
}
|
||||
|
||||
void
|
||||
nandfs_init(struct nandfs *fs, const char *dir)
|
||||
{
|
||||
|
||||
snprintf(fs->n_ioc, sizeof(fs->n_ioc), "%s/%s", dir, ".");
|
||||
fs->n_iocfd = -1;
|
||||
fs->n_flags = NANDFS_IS_VALID;
|
||||
}
|
||||
|
||||
void
|
||||
nandfs_destroy(struct nandfs *fs)
|
||||
{
|
||||
|
||||
assert(fs->n_iocfd == -1);
|
||||
fs->n_flags &=
|
||||
~(NANDFS_IS_ERROR | NANDFS_IS_VALID);
|
||||
assert(fs->n_flags == 0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_open(struct nandfs *fs)
|
||||
{
|
||||
struct nandfs_fsinfo fsinfo;
|
||||
|
||||
fs->n_flags |= NANDFS_IS_OPENED;
|
||||
|
||||
fs->n_iocfd = open(fs->n_ioc, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP |
|
||||
S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if (fs->n_iocfd == -1) {
|
||||
nandfs_seterr(fs, "couldn't open %s: %s", fs->n_ioc,
|
||||
strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_FSINFO, &fsinfo) == -1) {
|
||||
nandfs_seterr(fs, "couldn't fetch fsinfo: %s",
|
||||
strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(&fs->n_fsdata, &fsinfo.fs_fsdata, sizeof(fs->n_fsdata));
|
||||
memcpy(&fs->n_sb, &fsinfo.fs_super, sizeof(fs->n_sb));
|
||||
snprintf(fs->n_dev, sizeof(fs->n_dev), "%s", fsinfo.fs_dev);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nandfs_close(struct nandfs *fs)
|
||||
{
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
assert(fs->n_flags & NANDFS_IS_OPENED);
|
||||
|
||||
close(fs->n_iocfd);
|
||||
fs->n_iocfd = -1;
|
||||
fs->n_flags &= ~NANDFS_IS_OPENED;
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_cpstat(struct nandfs *fs, struct nandfs_cpstat *cpstat)
|
||||
{
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
|
||||
if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPSTAT, cpstat) == -1) {
|
||||
nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPSTAT: %s",
|
||||
strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
nandfs_get_cpinfo(struct nandfs *fs, uint64_t cno, int mode,
|
||||
struct nandfs_cpinfo *cpinfo, size_t nci)
|
||||
{
|
||||
struct nandfs_argv args;
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
|
||||
args.nv_base = (u_long)cpinfo;
|
||||
args.nv_nmembs = nci;
|
||||
args.nv_index = cno;
|
||||
args.nv_flags = mode;
|
||||
|
||||
if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPINFO, &args) == -1) {
|
||||
nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPINFO: %s",
|
||||
strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (args.nv_nmembs);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
nandfs_get_cp(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo,
|
||||
size_t nci)
|
||||
{
|
||||
|
||||
return (nandfs_get_cpinfo(fs, cno, NANDFS_CHECKPOINT, cpinfo, nci));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
nandfs_get_snap(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo,
|
||||
size_t nci)
|
||||
{
|
||||
|
||||
return (nandfs_get_cpinfo(fs, cno, NANDFS_SNAPSHOT, cpinfo, nci));
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_make_snap(struct nandfs *fs, uint64_t *cno)
|
||||
{
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
|
||||
if (ioctl(fs->n_iocfd, NANDFS_IOCTL_MAKE_SNAP, cno) == -1) {
|
||||
nandfs_seterr(fs, "ioctl NANDFS_IOCTL_MAKE_SNAP: %s",
|
||||
strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_delete_snap(struct nandfs *fs, uint64_t cno)
|
||||
{
|
||||
|
||||
NANDFS_ASSERT_VALID(fs);
|
||||
|
||||
if (ioctl(fs->n_iocfd, NANDFS_IOCTL_DELETE_SNAP, &cno) == -1) {
|
||||
nandfs_seterr(fs, "ioctl NANDFS_IOCTL_DELETE_SNAP: %s",
|
||||
strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
@ -150,6 +150,9 @@ SRCS+= bootp.c rarp.c bootparam.c
|
||||
SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c
|
||||
SRCS+= dosfs.c ext2fs.c
|
||||
SRCS+= splitfs.c
|
||||
.if ${MK_NAND} != "no"
|
||||
SRCS+= nandfs.c
|
||||
.endif
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
||||
|
1041
lib/libstand/nandfs.c
Normal file
1041
lib/libstand/nandfs.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -118,6 +118,7 @@ extern struct fs_ops ufs_fsops;
|
||||
extern struct fs_ops tftp_fsops;
|
||||
extern struct fs_ops nfs_fsops;
|
||||
extern struct fs_ops cd9660_fsops;
|
||||
extern struct fs_ops nandfs_fsops;
|
||||
extern struct fs_ops gzipfs_fsops;
|
||||
extern struct fs_ops bzipfs_fsops;
|
||||
extern struct fs_ops dosfs_fsops;
|
||||
|
@ -92,6 +92,11 @@ SUBDIR+= ipfw
|
||||
SUBDIR+= natd
|
||||
.endif
|
||||
|
||||
.if ${MK_NAND} != "no"
|
||||
SUBDIR+= nandfs
|
||||
SUBDIR+= newfs_nandfs
|
||||
.endif
|
||||
|
||||
.if ${MK_PF} != "no"
|
||||
SUBDIR+= pfctl
|
||||
SUBDIR+= pflogd
|
||||
|
10
sbin/nandfs/Makefile
Normal file
10
sbin/nandfs/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= nandfs
|
||||
SRCS= nandfs.c lssnap.c mksnap.c rmsnap.c
|
||||
MAN= nandfs.8
|
||||
|
||||
DPADD= ${LIBNANDFS}
|
||||
LDADD= -lnandfs
|
||||
|
||||
.include <bsd.prog.mk>
|
112
sbin/nandfs/lssnap.c
Normal file
112
sbin/nandfs/lssnap.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Semihalf under sponsorship
|
||||
* from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sysexits.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_fs.h>
|
||||
#include <libnandfs.h>
|
||||
|
||||
#include "nandfs.h"
|
||||
|
||||
#define NCPINFO 512
|
||||
|
||||
static void
|
||||
lssnap_usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage:\n");
|
||||
fprintf(stderr, "\tlssnap node\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_cpinfo(struct nandfs_cpinfo *cpinfo)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
char timebuf[128];
|
||||
|
||||
t = (time_t)cpinfo->nci_create;
|
||||
localtime_r(&t, &tm);
|
||||
strftime(timebuf, sizeof(timebuf), "%F %T", &tm);
|
||||
|
||||
printf("%20llu %s\n", (unsigned long long)cpinfo->nci_cno, timebuf);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_lssnap(int argc, char **argv)
|
||||
{
|
||||
struct nandfs_cpinfo *cpinfos;
|
||||
struct nandfs fs;
|
||||
uint64_t next;
|
||||
int error, nsnap, i;
|
||||
|
||||
if (argc != 1) {
|
||||
lssnap_usage();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
cpinfos = malloc(sizeof(*cpinfos) * NCPINFO);
|
||||
if (cpinfos == NULL) {
|
||||
fprintf(stderr, "cannot allocate memory\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
nandfs_init(&fs, argv[0]);
|
||||
error = nandfs_open(&fs);
|
||||
if (error == -1) {
|
||||
fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs));
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (next = 1; next != 0; next = cpinfos[nsnap - 1].nci_next) {
|
||||
nsnap = nandfs_get_snap(&fs, next, cpinfos, NCPINFO);
|
||||
if (nsnap < 1)
|
||||
break;
|
||||
|
||||
for (i = 0; i < nsnap; i++)
|
||||
print_cpinfo(&cpinfos[i]);
|
||||
}
|
||||
|
||||
if (nsnap == -1)
|
||||
fprintf(stderr, "nandfs_get_snap: %s\n", nandfs_errmsg(&fs));
|
||||
|
||||
out:
|
||||
nandfs_close(&fs);
|
||||
nandfs_destroy(&fs);
|
||||
free(cpinfos);
|
||||
return (error);
|
||||
}
|
80
sbin/nandfs/mksnap.c
Normal file
80
sbin/nandfs/mksnap.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Semihalf under sponsorship
|
||||
* from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_fs.h>
|
||||
#include <libnandfs.h>
|
||||
|
||||
#include "nandfs.h"
|
||||
|
||||
static void
|
||||
mksnap_usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage:\n");
|
||||
fprintf(stderr, "\tmksnap node\n");
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_mksnap(int argc, char **argv)
|
||||
{
|
||||
struct nandfs fs;
|
||||
uint64_t cpno;
|
||||
int error;
|
||||
|
||||
if (argc != 1) {
|
||||
mksnap_usage();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
nandfs_init(&fs, argv[0]);
|
||||
error = nandfs_open(&fs);
|
||||
if (error == -1) {
|
||||
fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs));
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = nandfs_make_snap(&fs, &cpno);
|
||||
if (error == -1)
|
||||
fprintf(stderr, "nandfs_make_snap: %s\n", nandfs_errmsg(&fs));
|
||||
else
|
||||
printf("%jd\n", cpno);
|
||||
|
||||
out:
|
||||
nandfs_close(&fs);
|
||||
nandfs_destroy(&fs);
|
||||
return (error);
|
||||
}
|
75
sbin/nandfs/nandfs.8
Normal file
75
sbin/nandfs/nandfs.8
Normal file
@ -0,0 +1,75 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This software was developed by Semihalf under sponsorship
|
||||
.\" from the FreeBSD Foundation.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\"
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 28, 2012
|
||||
.Dt NANDFS 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm nandfs
|
||||
.Nd manage mounted NAND FS
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Cm lssnap
|
||||
.Ar node
|
||||
.Nm
|
||||
.Cm mksnap
|
||||
.Ar node
|
||||
.Nm
|
||||
.Cm rmsnap
|
||||
.Ar snapshot node
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility allows to manage snapshots of a mounted NAND FS.
|
||||
.Sh EXAMPLES
|
||||
.Pp
|
||||
Create a snapshot of filesystem mounted on
|
||||
.Em /nand .
|
||||
.Bd -literal -offset 2n
|
||||
.Li # Ic nandfs mksnap /nand
|
||||
1
|
||||
.Ed
|
||||
.Pp
|
||||
List snapshots of filesystem mounted on
|
||||
.Em /nand .
|
||||
.Bd -literal -offset 2n
|
||||
.Li # Ic nandfs lssnap /nand
|
||||
1 2012-02-28 18:49:45 ss 138 2
|
||||
.Ed
|
||||
.Pp
|
||||
Remove snapshot 1 of filesystem mounted on
|
||||
.Em /nand .
|
||||
.Bd -literal -offset 2n
|
||||
.Li # Ic nandfs rmsnap 1 /nand
|
||||
.Ed
|
||||
.Sh AUTHORS
|
||||
This utility and manual page were written by
|
||||
.An Mateusz Guzik .
|
74
sbin/nandfs/nandfs.c
Normal file
74
sbin/nandfs/nandfs.c
Normal file
@ -0,0 +1,74 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Semihalf under sponsorship
|
||||
* from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "nandfs.h"
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage: nandfs [lssnap | mksnap | rmsnap <snap>] "
|
||||
"node\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int error = 0;
|
||||
char *cmd;
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
cmd = argv[1];
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
|
||||
if (strcmp(cmd, "lssnap") == 0)
|
||||
error = nandfs_lssnap(argc, argv);
|
||||
else if (strcmp(cmd, "mksnap") == 0)
|
||||
error = nandfs_mksnap(argc, argv);
|
||||
else if (strcmp(cmd, "rmsnap") == 0)
|
||||
error = nandfs_rmsnap(argc, argv);
|
||||
else
|
||||
usage();
|
||||
|
||||
return (error);
|
||||
}
|
40
sbin/nandfs/nandfs.h
Normal file
40
sbin/nandfs/nandfs.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Semihalf under sponsorship
|
||||
* from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef NANDFS_H
|
||||
#define NANDFS_H
|
||||
|
||||
int nandfs_lssnap(int, char **);
|
||||
int nandfs_mksnap(int, char **);
|
||||
int nandfs_rmsnap(int, char **);
|
||||
|
||||
#endif /* !NANDFS_H */
|
87
sbin/nandfs/rmsnap.c
Normal file
87
sbin/nandfs/rmsnap.c
Normal file
@ -0,0 +1,87 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Semihalf under sponsorship
|
||||
* from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_fs.h>
|
||||
#include <libnandfs.h>
|
||||
|
||||
#include "nandfs.h"
|
||||
|
||||
static void
|
||||
rmsnap_usage(void)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage:\n");
|
||||
fprintf(stderr, "\trmsnap snap node\n");
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_rmsnap(int argc, char **argv)
|
||||
{
|
||||
struct nandfs fs;
|
||||
uint64_t cpno;
|
||||
int error;
|
||||
|
||||
if (argc != 2) {
|
||||
rmsnap_usage();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
cpno = strtoll(argv[0], (char **)NULL, 10);
|
||||
if (cpno == 0) {
|
||||
fprintf(stderr, "%s must be a number greater than 0\n",
|
||||
argv[0]);
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
nandfs_init(&fs, argv[1]);
|
||||
error = nandfs_open(&fs);
|
||||
if (error == -1) {
|
||||
fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs));
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = nandfs_delete_snap(&fs, cpno);
|
||||
if (error == -1)
|
||||
fprintf(stderr, "nandfs_delete_snap: %s\n", nandfs_errmsg(&fs));
|
||||
|
||||
out:
|
||||
nandfs_close(&fs);
|
||||
nandfs_destroy(&fs);
|
||||
return (error);
|
||||
}
|
9
sbin/newfs_nandfs/Makefile
Normal file
9
sbin/newfs_nandfs/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= newfs_nandfs
|
||||
MAN= newfs_nandfs.8
|
||||
|
||||
LDADD+= -lgeom
|
||||
DPADD+= ${LIBGEOM}
|
||||
|
||||
.include <bsd.prog.mk>
|
77
sbin/newfs_nandfs/newfs_nandfs.8
Normal file
77
sbin/newfs_nandfs/newfs_nandfs.8
Normal file
@ -0,0 +1,77 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2010 Semihalf
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
|
||||
.Dd April 11, 2009
|
||||
.Dt NEWFS_NANDFS 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm newfs_nandfs
|
||||
.Nd construct a new NAND FS file system
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl b Ar blocsize
|
||||
.Op Fl B Ar blocks-per-segment
|
||||
.Op Fl L Ar label
|
||||
.Op Fl m Ar reserved-segment-percent
|
||||
.Ar device
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility creates a NAND FS file system on device.
|
||||
.Pp
|
||||
The options are as follow:
|
||||
.Bl -tag -width indent
|
||||
.It Fl b Ar blocksize
|
||||
Size of block (1024 if not specified).
|
||||
.It Fl B Ar blocks_per_segment
|
||||
Number of blocks per segment (2048 if not specified).
|
||||
.It Fl L Ar label
|
||||
Volume label (up to 16 characters).
|
||||
.It Fl m Ar reserved_block_percent
|
||||
Percentage of reserved blocks (5 if not specified).
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
Exit status is 0 on success and 1 on error.
|
||||
.Sh EXAMPLES
|
||||
Create a file system, using default parameters, on
|
||||
.Pa /dev/ad0s1 :
|
||||
.Bd -literal -offset indent
|
||||
newfs_nandfs /dev/ad0s1
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr disktab 5 ,
|
||||
.Xr disklabel 8 ,
|
||||
.Xr fdisk 8 ,
|
||||
.Xr newfs 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
utility first appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHOR
|
||||
.An Grzegorz Bernacki
|
1176
sbin/newfs_nandfs/newfs_nandfs.c
Normal file
1176
sbin/newfs_nandfs/newfs_nandfs.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -250,6 +250,8 @@ MAN= aac.4 \
|
||||
mwlfw.4 \
|
||||
mxge.4 \
|
||||
my.4 \
|
||||
nand.4 \
|
||||
nandsim.4 \
|
||||
natm.4 \
|
||||
natmip.4 \
|
||||
ncr.4 \
|
||||
|
140
share/man/man4/nand.4
Normal file
140
share/man/man4/nand.4
Normal file
@ -0,0 +1,140 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This documentation was written by Semihalf under sponsorship from
|
||||
.\" the FreeBSD Foundation.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 8, 2012
|
||||
.Dt NAND 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm nand
|
||||
.Nd NAND Flash framework
|
||||
.Sh SYNOPSIS
|
||||
.Cd "device nand"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fx
|
||||
.Nm
|
||||
framework consists of a set of interfaces that aim to provide an extensible,
|
||||
object oriented environement for NAND controllers and NAND Flash memory chips
|
||||
from various hardware vendors, and to allow for uniform and flexible
|
||||
management of the NAND devices. It comprises of the following major
|
||||
components:
|
||||
.Bl -bullet
|
||||
.It
|
||||
NAND Flash controller (NFC) interface.
|
||||
.Pp
|
||||
Defines methods which allow to send commands as well as send/receive data
|
||||
between the controller and a NAND chip. Back-end drivers for specific NAND
|
||||
controllers plug into this interface and implement low-level routines for a
|
||||
given NAND controller.
|
||||
.Pp
|
||||
This layer implements basic functionality of a NAND Flash controller. It
|
||||
allows to send command and address to chip, drive CS (chip select line), as
|
||||
well as read/write to the selected NAND chip. This layer is independent of
|
||||
NAND chip devices actually connected to the controller.
|
||||
.It
|
||||
NAND chip interface.
|
||||
.Pp
|
||||
Provides basic operations like read page, program page, erase block. Currently
|
||||
three generic classes of drivers are available, which provide support for the
|
||||
following chips:
|
||||
.Bl -bullet
|
||||
.It
|
||||
large page
|
||||
.It
|
||||
small page
|
||||
.It
|
||||
ONFI-compliant
|
||||
.El
|
||||
.Pp
|
||||
This layer implements basic operations to be performed on a NAND chip, like
|
||||
read, program, erase, get status etc. Since these operations use specific
|
||||
commands (depending on the vendor), each chip has potentially its own
|
||||
implementation of the commands set.
|
||||
.Pp
|
||||
The framework is extensible so it is also possible to create a custom command
|
||||
set for a non standard chip support.
|
||||
.It
|
||||
NANDbus.
|
||||
.Pp
|
||||
This layer is responsible for enumerating NAND chips in the system and
|
||||
establishing the hierarchy between chips and their supervising controllers.
|
||||
.Pp
|
||||
Its main purpose is detecting type of NAND chips connected to a given chip
|
||||
select (CS line). It also allows manages locking access to the NAND
|
||||
controller. NANDbus passes requests from an active chip to the chip
|
||||
controller.
|
||||
.It
|
||||
NAND character / GEOM device.
|
||||
.Pp
|
||||
For each NAND chip found in a system a character and GEOM devices are created
|
||||
which allows to read / write directly to a device, as well as perform other
|
||||
specific operations (like via ioctl).
|
||||
.Pp
|
||||
There are two GEOM devices created for each NAND chip:
|
||||
.Bl -bullet
|
||||
.It
|
||||
raw device
|
||||
.It
|
||||
normal device
|
||||
.El
|
||||
.Pp
|
||||
Raw device allows to bypass ECC checking when reading/writing to it, while
|
||||
normal device always uses ECC algorithm to validate the read data.
|
||||
.Pp
|
||||
NAND character devices will be created for each NAND chip detected while
|
||||
probing the NAND controller.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr libnandfs 3 ,
|
||||
.Xr gnand 4 ,
|
||||
.Xr nandsim 4 ,
|
||||
.Xr nandfs 5 ,
|
||||
.Xr makefs 8 ,
|
||||
.Xr mount_nandfs 8 ,
|
||||
.Xr nandfs 8 ,
|
||||
.Xr nandsim 8 ,
|
||||
.Xr nandtool 8 ,
|
||||
.Xr newfs_nandfs 8 ,
|
||||
.Xr umount_nandfs 8
|
||||
.Sh STANDARDS
|
||||
Open NAND Flash Interface Working Group
|
||||
.Pq Vt ONFI .
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
framework support first appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHOR
|
||||
The
|
||||
.Nm
|
||||
framework was designed and developed by
|
||||
.An Grzegorz Bernacki .
|
||||
This manual page was written by
|
||||
.An Rafal Jaworowski .
|
184
share/man/man4/nandsim.4
Normal file
184
share/man/man4/nandsim.4
Normal file
@ -0,0 +1,184 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This documentation was written by Semihalf under sponsorship from
|
||||
.\" the FreeBSD Foundation.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 8, 2012
|
||||
.Dt NANDSIM 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm nandsim
|
||||
.Nd NAND Flash simulator driver
|
||||
.Sh SYNOPSIS
|
||||
.Cd "device nand"
|
||||
.Cd "device nandsim"
|
||||
.Cd "options ALQ"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
is part of the
|
||||
.Fx
|
||||
NAND framework
|
||||
.Xr nand 4
|
||||
and can be characterized with the following highlights:
|
||||
.Bl -bullet
|
||||
.It
|
||||
plugs into the
|
||||
.Xr nand 4
|
||||
framework APIs as if it were a hardware controller (hanging on the nexus bus)
|
||||
with real NAND chips connected to it
|
||||
.It
|
||||
physically part of the kernel code (either statically linked into the kernel
|
||||
image or built as a module)
|
||||
.It
|
||||
controlled with a user space program
|
||||
.Xr nandsim 8
|
||||
.El
|
||||
.Pp
|
||||
From the user perspective, the
|
||||
.Nm
|
||||
allows for imitating ONFI-compliant NAND Flash devices as if they were
|
||||
attached to the system via a virtual controller.
|
||||
.Pp
|
||||
Some
|
||||
.Nm
|
||||
features rely on the ability to log contents to a file, which is achieved
|
||||
through the
|
||||
.Xr alq 9
|
||||
facility.
|
||||
.Sh SEE ALSO
|
||||
.Xr nand 4 ,
|
||||
.Xr nandsim.conf 5 ,
|
||||
.Xr nandsim 8
|
||||
.Sh STANDARDS
|
||||
Open NAND Flash Interface Working Group
|
||||
.Pq Vt ONFI .
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
support first appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHOR
|
||||
The
|
||||
.Nm
|
||||
kernel driver was developed by
|
||||
.An Grzegorz Bernacki .
|
||||
This manual page was written by
|
||||
.An Rafal Jaworowski .
|
||||
.\"
|
||||
.\" Copyright (c) 2012 The FreeBSD Foundation
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This documentation was written by Semihalf under sponsorship from
|
||||
.\" the FreeBSD Foundation.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 8, 2012
|
||||
.Dt NANDSIM 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm nandsim
|
||||
.Nd NAND Flash simulator driver
|
||||
.Sh SYNOPSIS
|
||||
.Cd "device nand"
|
||||
.Cd "device nandsim"
|
||||
.Cd "options ALQ"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
is part of the
|
||||
.Fx
|
||||
NAND framework
|
||||
.Xr nand 4
|
||||
and can be characterized with the following highlights:
|
||||
.Bl -bullet
|
||||
.It
|
||||
plugs into the
|
||||
.Xr nand 4
|
||||
framework APIs as if it were a hardware controller (hanging on the nexus bus)
|
||||
with real NAND chips connected to it
|
||||
.It
|
||||
physically part of the kernel code (either statically linked into the kernel
|
||||
image or built as a module)
|
||||
.It
|
||||
controlled with a user space program
|
||||
.Xr nandsim 8
|
||||
.El
|
||||
.Pp
|
||||
From the user perspective, the
|
||||
.Nm
|
||||
allows for imitating ONFI-compliant NAND Flash devices as if they were
|
||||
attached to the system via a virtual controller.
|
||||
.Pp
|
||||
Some
|
||||
.Nm
|
||||
features rely on the ability to log contents to a file, which is achieved
|
||||
through the
|
||||
.Xr alq 9
|
||||
facility.
|
||||
.Sh SEE ALSO
|
||||
.Xr nand 4 ,
|
||||
.Xr nandsim.conf 5 ,
|
||||
.Xr nandsim 8
|
||||
.Sh STANDARDS
|
||||
Open NAND Flash Interface Working Group
|
||||
.Pq Vt ONFI .
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
support first appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHOR
|
||||
The
|
||||
.Nm
|
||||
kernel driver was developed by
|
||||
.An Grzegorz Bernacki .
|
||||
This manual page was written by
|
||||
.An Rafal Jaworowski .
|
@ -85,6 +85,10 @@ MLINKS+=resolver.5 resolv.conf.5
|
||||
MAN+= hesiod.conf.5
|
||||
.endif
|
||||
|
||||
.if ${MK_NAND} != "no"
|
||||
MAN+= nandfs.5
|
||||
.endif
|
||||
|
||||
.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386"
|
||||
_boot.config.5= boot.config.5
|
||||
.endif
|
||||
|
129
share/man/man5/nandfs.5
Normal file
129
share/man/man5/nandfs.5
Normal file
@ -0,0 +1,129 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2010 Semihalf
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd Nov 11, 2010
|
||||
.Dt NANDFS 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm nandfs
|
||||
.Nd NAND Flash file system
|
||||
.Sh SYNOPSIS
|
||||
To compile support for the
|
||||
.Nm ,
|
||||
place the following in your kernel configuration file:
|
||||
.Bd -ragged -offset indent
|
||||
.Cd "options NANDFS"
|
||||
.Ed
|
||||
.Pp
|
||||
Even though the NAND FS can be used with any storage media, it has been
|
||||
optimized and designed towards NAND Flash devices, so typically the following
|
||||
driver is used:
|
||||
.Bd -ragged -offset indent
|
||||
.Cd "device nand"
|
||||
.Ed
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver enables
|
||||
.Fx
|
||||
with support for NAND-oriented file system.
|
||||
.Pp
|
||||
It is a log-structured style file system with the following major features and
|
||||
characteristics:
|
||||
.Bl -bullet
|
||||
.It
|
||||
Hard links, symbolic links support
|
||||
.It
|
||||
Block journaling
|
||||
.It
|
||||
Copy-On-Write
|
||||
.It
|
||||
Snapshots (continuous, taken automatically, simultaneously mountable)
|
||||
.It
|
||||
Quick crash recovery at mount time
|
||||
.It
|
||||
64-bit data structures; supports many files, large files and volumes
|
||||
.It
|
||||
POSIX file permissions
|
||||
.It
|
||||
Checksum / ECC
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
The most common usage is mounting the file system:
|
||||
.Pp
|
||||
.Dl "mount -t nandfs /dev/<gnandN> /mnt"
|
||||
.Pp
|
||||
or:
|
||||
.Dl "mount_nandfs /dev/<gnandN> /mnt"
|
||||
.Pp
|
||||
where
|
||||
.Ar gnandN
|
||||
is the GEOM device representing a Flash partition (slice) containing the
|
||||
.Nm
|
||||
structure, and
|
||||
.Pa /mnt
|
||||
is a mount point.
|
||||
.Pp
|
||||
.Pp
|
||||
It is possible to define an entry in
|
||||
.Pa /etc/fstab
|
||||
for the
|
||||
.Nm :
|
||||
.Bd -literal
|
||||
/dev/gnand0 /flash nandfs rw 0 0
|
||||
.Ed
|
||||
.Pp
|
||||
This will mount a
|
||||
.Nm
|
||||
partition at the specified mount point during system boot.
|
||||
.Sh SEE ALSO
|
||||
.Xr gnand 4 ,
|
||||
.Xr nand 4 ,
|
||||
.Xr mount_nandfs 8 ,
|
||||
.Xr nandfs 8 ,
|
||||
.Xr nandsim 8 ,
|
||||
.Xr nandtool 8 ,
|
||||
.Xr umount_nandfs 8
|
||||
.Sh HISTORY
|
||||
The NAND FS concepts are based on NILFS principles and initial implementation
|
||||
was derived from early NILFS NetBSD code (read only). Since then the NAND FS
|
||||
code diverged significantly and is by no means compatible with NILFS.
|
||||
.Pp
|
||||
The NAND Flash file system first appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHOR
|
||||
The NAND FS was written by
|
||||
.An Grzegorz Bernacki with the help of
|
||||
.An Mateusz Guzik ,
|
||||
based on the NetBSD code created by
|
||||
.An Reinoud Zandijk .
|
||||
Additional help and support by
|
||||
.An Lukasz Plachno ,
|
||||
.An Jan Sieka and
|
||||
.An Lukasz Wojcik .
|
||||
This manual page was written by
|
||||
.An Rafal Jaworowski .
|
@ -426,6 +426,7 @@ __DEFAULT_NO_OPTIONS = \
|
||||
ICONV \
|
||||
IDEA \
|
||||
LIBCPLUSPLUS \
|
||||
NAND \
|
||||
OFED \
|
||||
SHARED_TOOLCHAIN
|
||||
|
||||
|
@ -15,6 +15,11 @@ LOADER_DISK_SUPPORT?= yes
|
||||
LOADER_UFS_SUPPORT?= yes
|
||||
LOADER_CD9660_SUPPORT?= no
|
||||
LOADER_EXT2FS_SUPPORT?= no
|
||||
.if ${MK_NAND} != "no"
|
||||
LOADER_NANDFS_SUPPORT?= yes
|
||||
.else
|
||||
LOADER_NANDFS_SUPPORT?= no
|
||||
.endif
|
||||
LOADER_NET_SUPPORT?= yes
|
||||
LOADER_NFS_SUPPORT?= yes
|
||||
LOADER_TFTP_SUPPORT?= no
|
||||
@ -38,6 +43,9 @@ CFLAGS+= -DLOADER_CD9660_SUPPORT
|
||||
.if ${LOADER_EXT2FS_SUPPORT} == "yes"
|
||||
CFLAGS+= -DLOADER_EXT2FS_SUPPORT
|
||||
.endif
|
||||
.if ${LOADER_NANDFS_SUPPORT} == "yes"
|
||||
CFLAGS+= -DLOADER_NANDFS_SUPPORT
|
||||
.endif
|
||||
.if ${LOADER_GZIP_SUPPORT} == "yes"
|
||||
CFLAGS+= -DLOADER_GZIP_SUPPORT
|
||||
.endif
|
||||
|
@ -56,6 +56,9 @@ struct fs_ops *file_system[] = {
|
||||
#if defined(LOADER_EXT2FS_SUPPORT)
|
||||
&ext2fs_fsops,
|
||||
#endif
|
||||
#if defined(LOADER_NANDFS_SUPPORT)
|
||||
&nandfs_fsops,
|
||||
#endif
|
||||
#if defined(LOADER_NFS_SUPPORT)
|
||||
&nfs_fsops,
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@ $FreeBSD$
|
||||
NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
|
||||
file is important. Make sure the current version number is on line 6.
|
||||
|
||||
1.2: Extended with NAND FS support.
|
||||
1.1: Flattened Device Tree blob support.
|
||||
1.0: Added storage support. Booting from HDD, USB, etc. is now possible.
|
||||
0.5: Initial U-Boot/arm version (netbooting only).
|
||||
|
@ -53,6 +53,9 @@ CFLAGS+= -DLOADER_GZIP_SUPPORT
|
||||
.if !defined(LOADER_NO_GPT_SUPPORT)
|
||||
CFLAGS+= -DLOADER_GPT_SUPPORT
|
||||
.endif
|
||||
.if defined(LOADER_NANDFS_SUPPORT)
|
||||
CFLAGS+= -DLOADER_NANDFS_SUPPORT
|
||||
.endif
|
||||
|
||||
# Always add MI sources
|
||||
.PATH: ${.CURDIR}/../../common
|
||||
|
@ -74,6 +74,9 @@ struct fs_ops *file_system[] = {
|
||||
&ext2fs_fsops,
|
||||
&dosfs_fsops,
|
||||
&cd9660_fsops,
|
||||
#if defined(LOADER_NANDFS_SUPPORT)
|
||||
&nandfs_fsops,
|
||||
#endif
|
||||
&splitfs_fsops,
|
||||
#if defined(LOADER_ZFS_SUPPORT)
|
||||
&zfs_fsops,
|
||||
|
@ -1660,6 +1660,21 @@ dev/mxge/mxge_ethp_z8e.c optional mxge pci
|
||||
dev/mxge/mxge_rss_eth_z8e.c optional mxge pci
|
||||
dev/mxge/mxge_rss_ethp_z8e.c optional mxge pci
|
||||
dev/my/if_my.c optional my
|
||||
dev/nand/nand.c optional nand
|
||||
dev/nand/nand_bbt.c optional nand
|
||||
dev/nand/nand_cdev.c optional nand
|
||||
dev/nand/nand_generic.c optional nand
|
||||
dev/nand/nand_geom.c optional nand
|
||||
dev/nand/nand_id.c optional nand
|
||||
dev/nand/nandbus.c optional nand
|
||||
dev/nand/nandbus_if.m optional nand
|
||||
dev/nand/nand_if.m optional nand
|
||||
dev/nand/nandsim.c optional nandsim nand
|
||||
dev/nand/nandsim_chip.c optional nandsim nand
|
||||
dev/nand/nandsim_ctrl.c optional nandsim nand
|
||||
dev/nand/nandsim_log.c optional nandsim nand
|
||||
dev/nand/nandsim_swap.c optional nandsim nand
|
||||
dev/nand/nfc_if.m optional nand
|
||||
dev/ncv/ncr53c500.c optional ncv
|
||||
dev/ncv/ncr53c500_pccard.c optional ncv pccard
|
||||
dev/netmap/netmap.c optional netmap
|
||||
@ -2252,6 +2267,20 @@ fs/msdosfs/msdosfs_iconv.c optional msdosfs_iconv
|
||||
fs/msdosfs/msdosfs_lookup.c optional msdosfs
|
||||
fs/msdosfs/msdosfs_vfsops.c optional msdosfs
|
||||
fs/msdosfs/msdosfs_vnops.c optional msdosfs
|
||||
fs/nandfs/bmap.c optional nandfs
|
||||
fs/nandfs/nandfs_alloc.c optional nandfs
|
||||
fs/nandfs/nandfs_bmap.c optional nandfs
|
||||
fs/nandfs/nandfs_buffer.c optional nandfs
|
||||
fs/nandfs/nandfs_cleaner.c optional nandfs
|
||||
fs/nandfs/nandfs_cpfile.c optional nandfs
|
||||
fs/nandfs/nandfs_dat.c optional nandfs
|
||||
fs/nandfs/nandfs_dir.c optional nandfs
|
||||
fs/nandfs/nandfs_ifile.c optional nandfs
|
||||
fs/nandfs/nandfs_segment.c optional nandfs
|
||||
fs/nandfs/nandfs_subr.c optional nandfs
|
||||
fs/nandfs/nandfs_sufile.c optional nandfs
|
||||
fs/nandfs/nandfs_vfsops.c optional nandfs
|
||||
fs/nandfs/nandfs_vnops.c optional nandfs
|
||||
fs/nfs/nfs_commonkrpc.c optional nfscl | nfsd
|
||||
fs/nfs/nfs_commonsubs.c optional nfscl | nfsd
|
||||
fs/nfs/nfs_commonport.c optional nfscl | nfsd
|
||||
|
@ -213,6 +213,7 @@ FDESCFS opt_dontuse.h
|
||||
FFS opt_dontuse.h
|
||||
HPFS opt_dontuse.h
|
||||
MSDOSFS opt_dontuse.h
|
||||
NANDFS opt_dontuse.h
|
||||
NTFS opt_dontuse.h
|
||||
NULLFS opt_dontuse.h
|
||||
NWFS opt_dontuse.h
|
||||
|
832
sys/dev/nand/nand.c
Normal file
832
sys/dev/nand/nand.c
Normal file
@ -0,0 +1,832 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandbus.h>
|
||||
#include <dev/nand/nand_ecc_pos.h>
|
||||
#include "nfc_if.h"
|
||||
#include "nand_if.h"
|
||||
#include "nandbus_if.h"
|
||||
#include <machine/stdarg.h>
|
||||
|
||||
#define NAND_RESET_DELAY 1000 /* tRST */
|
||||
#define NAND_ERASE_DELAY 3000 /* tBERS */
|
||||
#define NAND_PROG_DELAY 700 /* tPROG */
|
||||
#define NAND_READ_DELAY 50 /* tR */
|
||||
|
||||
#define BIT0(x) ((x) & 0x1)
|
||||
#define BIT1(x) (BIT0(x >> 1))
|
||||
#define BIT2(x) (BIT0(x >> 2))
|
||||
#define BIT3(x) (BIT0(x >> 3))
|
||||
#define BIT4(x) (BIT0(x >> 4))
|
||||
#define BIT5(x) (BIT0(x >> 5))
|
||||
#define BIT6(x) (BIT0(x >> 6))
|
||||
#define BIT7(x) (BIT0(x >> 7))
|
||||
|
||||
#define SOFTECC_SIZE 256
|
||||
#define SOFTECC_BYTES 3
|
||||
|
||||
int nand_debug_flag = 0;
|
||||
SYSCTL_INT(_debug, OID_AUTO, nand_debug, CTLFLAG_RW, &nand_debug_flag, 0,
|
||||
"NAND subsystem debug flag");
|
||||
|
||||
static void
|
||||
nand_tunable_init(void *arg)
|
||||
{
|
||||
|
||||
TUNABLE_INT_FETCH("debug.nand", &nand_debug_flag);
|
||||
}
|
||||
|
||||
SYSINIT(nand_tunables, SI_SUB_VFS, SI_ORDER_ANY, nand_tunable_init, NULL);
|
||||
|
||||
MALLOC_DEFINE(M_NAND, "NAND", "NAND dynamic data");
|
||||
|
||||
static void calculate_ecc(const uint8_t *, uint8_t *);
|
||||
static int correct_ecc(uint8_t *, uint8_t *, uint8_t *);
|
||||
|
||||
void
|
||||
nand_debug(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!(nand_debug_flag & level))
|
||||
return;
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
|
||||
int ecc_bytes, int ecc_size, uint16_t *eccposition, char *cdev_name)
|
||||
{
|
||||
|
||||
nand->ecc.eccmode = ecc_mode;
|
||||
nand->chip_cdev_name = cdev_name;
|
||||
|
||||
if (ecc_mode == NAND_ECC_SOFT) {
|
||||
nand->ecc.eccbytes = SOFTECC_BYTES;
|
||||
nand->ecc.eccsize = SOFTECC_SIZE;
|
||||
} else if (ecc_mode != NAND_ECC_NONE) {
|
||||
nand->ecc.eccbytes = ecc_bytes;
|
||||
nand->ecc.eccsize = ecc_size;
|
||||
if (eccposition)
|
||||
nand->ecc.eccpositions = eccposition;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
|
||||
init_chip_geom(cg, params->luns, params->blocks_per_lun,
|
||||
params->pages_per_block, params->bytes_per_page,
|
||||
params->spare_bytes_per_page);
|
||||
chip->t_bers = params->t_bers;
|
||||
chip->t_prog = params->t_prog;
|
||||
chip->t_r = params->t_r;
|
||||
chip->t_ccs = params->t_ccs;
|
||||
|
||||
if (params->features & ONFI_FEAT_16BIT)
|
||||
chip->flags |= NAND_16_BIT;
|
||||
}
|
||||
|
||||
void
|
||||
nand_set_params(struct nand_chip *chip, struct nand_params *params)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
uint32_t blocks_per_chip;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
blocks_per_chip = (params->chip_size << 20) /
|
||||
(params->page_size * params->pages_per_block);
|
||||
|
||||
init_chip_geom(cg, 1, blocks_per_chip,
|
||||
params->pages_per_block, params->page_size,
|
||||
params->oob_size);
|
||||
|
||||
chip->t_bers = NAND_ERASE_DELAY;
|
||||
chip->t_prog = NAND_PROG_DELAY;
|
||||
chip->t_r = NAND_READ_DELAY;
|
||||
chip->t_ccs = 0;
|
||||
|
||||
if (params->flags & NAND_16_BIT)
|
||||
chip->flags |= NAND_16_BIT;
|
||||
}
|
||||
|
||||
int
|
||||
nand_init_stat(struct nand_chip *chip)
|
||||
{
|
||||
struct block_stat *blk_stat;
|
||||
struct page_stat *pg_stat;
|
||||
struct chip_geom *cg;
|
||||
uint32_t blks, pgs;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
blks = cg->blks_per_lun * cg->luns;
|
||||
blk_stat = malloc(sizeof(struct block_stat) * blks, M_NAND,
|
||||
M_WAITOK | M_ZERO);
|
||||
if (!blk_stat)
|
||||
return (ENOMEM);
|
||||
|
||||
pgs = blks * cg->pgs_per_blk;
|
||||
pg_stat = malloc(sizeof(struct page_stat) * pgs, M_NAND,
|
||||
M_WAITOK | M_ZERO);
|
||||
if (!pg_stat) {
|
||||
free(blk_stat, M_NAND);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
chip->blk_stat = blk_stat;
|
||||
chip->pg_stat = pg_stat;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nand_destroy_stat(struct nand_chip *chip)
|
||||
{
|
||||
|
||||
free(chip->pg_stat, M_NAND);
|
||||
free(chip->blk_stat, M_NAND);
|
||||
}
|
||||
|
||||
int
|
||||
init_chip_geom(struct chip_geom *cg, uint32_t luns, uint32_t blks_per_lun,
|
||||
uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size)
|
||||
{
|
||||
int shift;
|
||||
|
||||
if (!cg)
|
||||
return (-1);
|
||||
|
||||
cg->luns = luns;
|
||||
cg->blks_per_lun = blks_per_lun;
|
||||
cg->blks_per_chip = blks_per_lun * luns;
|
||||
cg->pgs_per_blk = pgs_per_blk;
|
||||
|
||||
cg->page_size = pg_size;
|
||||
cg->oob_size = oob_size;
|
||||
cg->block_size = cg->page_size * cg->pgs_per_blk;
|
||||
cg->chip_size = cg->block_size * cg->blks_per_chip;
|
||||
|
||||
shift = fls(cg->pgs_per_blk - 1);
|
||||
cg->pg_mask = (1 << shift) - 1;
|
||||
cg->blk_shift = shift;
|
||||
|
||||
if (cg->blks_per_lun > 0) {
|
||||
shift = fls(cg->blks_per_lun - 1);
|
||||
cg->blk_mask = ((1 << shift) - 1) << cg->blk_shift;
|
||||
} else {
|
||||
shift = 0;
|
||||
cg->blk_mask = 0;
|
||||
}
|
||||
|
||||
cg->lun_shift = shift + cg->blk_shift;
|
||||
shift = fls(cg->luns - 1);
|
||||
cg->lun_mask = ((1 << shift) - 1) << cg->lun_shift;
|
||||
|
||||
nand_debug(NDBG_NAND, "Masks: lun 0x%x blk 0x%x page 0x%x\n"
|
||||
"Shifts: lun %d blk %d",
|
||||
cg->lun_mask, cg->blk_mask, cg->pg_mask,
|
||||
cg->lun_shift, cg->blk_shift);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
|
||||
uint32_t *blk, uint32_t *pg)
|
||||
{
|
||||
|
||||
if (!cg || !lun || !blk || !pg)
|
||||
return (-1);
|
||||
|
||||
if (row & ~(cg->lun_mask | cg->blk_mask | cg->pg_mask)) {
|
||||
nand_debug(NDBG_NAND,"Address out of bounds\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*lun = (row & cg->lun_mask) >> cg->lun_shift;
|
||||
*blk = (row & cg->blk_mask) >> cg->blk_shift;
|
||||
*pg = (row & cg->pg_mask);
|
||||
|
||||
nand_debug(NDBG_NAND,"address %x-%x-%x\n", *lun, *blk, *pg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row)
|
||||
{
|
||||
uint32_t lun, block, pg_in_blk;
|
||||
|
||||
if (!cg || !row)
|
||||
return (-1);
|
||||
|
||||
block = page / cg->pgs_per_blk;
|
||||
pg_in_blk = page % cg->pgs_per_blk;
|
||||
|
||||
lun = block / cg->blks_per_lun;
|
||||
block = block % cg->blks_per_lun;
|
||||
|
||||
*row = (lun << cg->lun_shift) & cg->lun_mask;
|
||||
*row |= ((block << cg->blk_shift) & cg->blk_mask);
|
||||
*row |= (pg_in_blk & cg->pg_mask);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nand_check_page_boundary(struct nand_chip *chip, uint32_t page)
|
||||
{
|
||||
struct chip_geom* cg;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
if (page >= (cg->pgs_per_blk * cg->blks_per_lun * cg->luns)) {
|
||||
nand_debug(NDBG_GEN,"%s: page number too big %#x\n",
|
||||
__func__, page);
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
param->page_size = cg->page_size;
|
||||
param->oob_size = cg->oob_size;
|
||||
|
||||
param->blocks = cg->blks_per_lun * cg->luns;
|
||||
param->pages_per_block = cg->pgs_per_blk;
|
||||
}
|
||||
|
||||
static uint16_t *
|
||||
default_software_ecc_positions(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_ecc_data *eccd;
|
||||
|
||||
eccd = &chip->nand->ecc;
|
||||
|
||||
if (eccd->eccpositions)
|
||||
return (eccd->eccpositions);
|
||||
|
||||
switch (chip->chip_geom.oob_size) {
|
||||
case 16:
|
||||
return ((uint16_t *)&default_software_ecc_positions_16);
|
||||
case 64:
|
||||
return ((uint16_t *)&default_software_ecc_positions_64);
|
||||
case 128:
|
||||
return ((uint16_t *)&default_software_ecc_positions_128);
|
||||
default:
|
||||
return (NULL); /* No ecc bytes positions defs available */
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_ecc(const uint8_t *buf, uint8_t *ecc)
|
||||
{
|
||||
uint8_t p8, byte;
|
||||
int i;
|
||||
|
||||
memset(ecc, 0, 3);
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
byte = buf[i];
|
||||
ecc[0] ^= (BIT0(byte) ^ BIT2(byte) ^ BIT4(byte) ^
|
||||
BIT6(byte)) << 2;
|
||||
ecc[0] ^= (BIT1(byte) ^ BIT3(byte) ^ BIT5(byte) ^
|
||||
BIT7(byte)) << 3;
|
||||
ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT4(byte) ^
|
||||
BIT5(byte)) << 4;
|
||||
ecc[0] ^= (BIT2(byte) ^ BIT3(byte) ^ BIT6(byte) ^
|
||||
BIT7(byte)) << 5;
|
||||
ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
|
||||
BIT3(byte)) << 6;
|
||||
ecc[0] ^= (BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
|
||||
BIT7(byte)) << 7;
|
||||
|
||||
p8 = BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
|
||||
BIT3(byte) ^ BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
|
||||
BIT7(byte);
|
||||
|
||||
if (p8) {
|
||||
ecc[2] ^= (0x1 << BIT0(i));
|
||||
ecc[2] ^= (0x4 << BIT1(i));
|
||||
ecc[2] ^= (0x10 << BIT2(i));
|
||||
ecc[2] ^= (0x40 << BIT3(i));
|
||||
|
||||
ecc[1] ^= (0x1 << BIT4(i));
|
||||
ecc[1] ^= (0x4 << BIT5(i));
|
||||
ecc[1] ^= (0x10 << BIT6(i));
|
||||
ecc[1] ^= (0x40 << BIT7(i));
|
||||
}
|
||||
}
|
||||
ecc[0] = ~ecc[0];
|
||||
ecc[1] = ~ecc[1];
|
||||
ecc[2] = ~ecc[2];
|
||||
ecc[0] |= 3;
|
||||
}
|
||||
|
||||
static int
|
||||
correct_ecc(uint8_t *buf, uint8_t *calc_ecc, uint8_t *read_ecc)
|
||||
{
|
||||
uint8_t ecc0, ecc1, ecc2, onesnum, bit, byte;
|
||||
uint16_t addr = 0;
|
||||
|
||||
ecc0 = calc_ecc[0] ^ read_ecc[0];
|
||||
ecc1 = calc_ecc[1] ^ read_ecc[1];
|
||||
ecc2 = calc_ecc[2] ^ read_ecc[2];
|
||||
|
||||
if (!ecc0 && !ecc1 && !ecc2)
|
||||
return (ECC_OK);
|
||||
|
||||
addr = BIT3(ecc0) | (BIT5(ecc0) << 1) | (BIT7(ecc0) << 2);
|
||||
addr |= (BIT1(ecc2) << 3) | (BIT3(ecc2) << 4) |
|
||||
(BIT5(ecc2) << 5) | (BIT7(ecc2) << 6);
|
||||
addr |= (BIT1(ecc1) << 7) | (BIT3(ecc1) << 8) |
|
||||
(BIT5(ecc1) << 9) | (BIT7(ecc1) << 10);
|
||||
|
||||
onesnum = 0;
|
||||
while (ecc0 || ecc1 || ecc2) {
|
||||
if (ecc0 & 1)
|
||||
onesnum++;
|
||||
if (ecc1 & 1)
|
||||
onesnum++;
|
||||
if (ecc2 & 1)
|
||||
onesnum++;
|
||||
|
||||
ecc0 >>= 1;
|
||||
ecc1 >>= 1;
|
||||
ecc2 >>= 1;
|
||||
}
|
||||
|
||||
if (onesnum == 11) {
|
||||
/* Correctable error */
|
||||
bit = addr & 7;
|
||||
byte = addr >> 3;
|
||||
buf[byte] ^= (1 << bit);
|
||||
return (ECC_CORRECTABLE);
|
||||
} else if (onesnum == 1) {
|
||||
/* ECC error */
|
||||
return (ECC_ERROR_ECC);
|
||||
} else {
|
||||
/* Uncorrectable error */
|
||||
return (ECC_UNCORRECTABLE);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc)
|
||||
{
|
||||
int steps = pagesize / SOFTECC_SIZE;
|
||||
int i = 0, j = 0;
|
||||
|
||||
for (; i < (steps * SOFTECC_BYTES);
|
||||
i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
|
||||
calculate_ecc(&buf[j], &ecc[i]);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
|
||||
uint8_t *readecc, uint8_t *calcecc)
|
||||
{
|
||||
int steps = pagesize / SOFTECC_SIZE;
|
||||
int i = 0, j = 0, ret = 0;
|
||||
|
||||
for (i = 0; i < (steps * SOFTECC_BYTES);
|
||||
i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
|
||||
ret += correct_ecc(&buf[j], &calcecc[i], &readecc[i]);
|
||||
if (ret < 0)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
offset_to_page(struct chip_geom *cg, uint32_t offset)
|
||||
{
|
||||
|
||||
return (offset / cg->page_size);
|
||||
}
|
||||
|
||||
int
|
||||
nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
|
||||
uint32_t len)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
struct nand_ecc_data *eccd;
|
||||
struct page_stat *pg_stat;
|
||||
device_t nandbus;
|
||||
void *oob = NULL;
|
||||
uint8_t *ptr;
|
||||
uint16_t *eccpos = NULL;
|
||||
uint32_t page, num, steps = 0;
|
||||
int i, retval = 0, needwrite;
|
||||
|
||||
nand_debug(NDBG_NAND,"%p read page %x[%x]", chip, offset, len);
|
||||
cg = &chip->chip_geom;
|
||||
eccd = &chip->nand->ecc;
|
||||
page = offset_to_page(cg, offset);
|
||||
num = len / cg->page_size;
|
||||
|
||||
if (eccd->eccmode != NAND_ECC_NONE) {
|
||||
steps = cg->page_size / eccd->eccsize;
|
||||
eccpos = default_software_ecc_positions(chip);
|
||||
oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
|
||||
}
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
ptr = (uint8_t *)buf;
|
||||
while (num--) {
|
||||
pg_stat = &(chip->pg_stat[page]);
|
||||
|
||||
if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
|
||||
retval = ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (eccd->eccmode != NAND_ECC_NONE) {
|
||||
if (NAND_GET_ECC(chip->dev, ptr, eccd->ecccalculated,
|
||||
&needwrite)) {
|
||||
retval = ENXIO;
|
||||
break;
|
||||
}
|
||||
nand_debug(NDBG_ECC,"%s: ECC calculated:",
|
||||
__func__);
|
||||
if (nand_debug_flag & NDBG_ECC)
|
||||
for (i = 0; i < (eccd->eccbytes * steps); i++)
|
||||
printf("%x ", eccd->ecccalculated[i]);
|
||||
|
||||
nand_debug(NDBG_ECC,"\n");
|
||||
|
||||
if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
|
||||
0)) {
|
||||
retval = ENXIO;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < (eccd->eccbytes * steps); i++)
|
||||
eccd->eccread[i] = ((uint8_t *)oob)[eccpos[i]];
|
||||
|
||||
nand_debug(NDBG_ECC,"%s: ECC read:", __func__);
|
||||
if (nand_debug_flag & NDBG_ECC)
|
||||
for (i = 0; i < (eccd->eccbytes * steps); i++)
|
||||
printf("%x ", eccd->eccread[i]);
|
||||
nand_debug(NDBG_ECC,"\n");
|
||||
|
||||
retval = NAND_CORRECT_ECC(chip->dev, ptr, eccd->eccread,
|
||||
eccd->ecccalculated);
|
||||
|
||||
nand_debug(NDBG_ECC, "NAND_CORRECT_ECC() returned %d",
|
||||
retval);
|
||||
|
||||
if (retval == 0)
|
||||
pg_stat->ecc_stat.ecc_succeded++;
|
||||
else if (retval > 0) {
|
||||
pg_stat->ecc_stat.ecc_corrected += retval;
|
||||
retval = ECC_CORRECTABLE;
|
||||
} else {
|
||||
pg_stat->ecc_stat.ecc_failed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pg_stat->page_read++;
|
||||
page++;
|
||||
ptr += cg->page_size;
|
||||
}
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
|
||||
if (oob)
|
||||
free(oob, M_NAND);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
int
|
||||
nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
|
||||
uint32_t len)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
device_t nandbus;
|
||||
uint8_t *ptr;
|
||||
uint32_t page, num, end, begin = 0, begin_off;
|
||||
int retval = 0;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
page = offset_to_page(cg, offset);
|
||||
begin_off = offset - page * cg->page_size;
|
||||
if (begin_off) {
|
||||
begin = cg->page_size - begin_off;
|
||||
len -= begin;
|
||||
}
|
||||
num = len / cg->page_size;
|
||||
end = len % cg->page_size;
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
ptr = (uint8_t *)buf;
|
||||
if (begin_off) {
|
||||
if (NAND_READ_PAGE(chip->dev, page, ptr, begin, begin_off)) {
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
page++;
|
||||
ptr += begin;
|
||||
}
|
||||
|
||||
while (num--) {
|
||||
if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
page++;
|
||||
ptr += cg->page_size;
|
||||
}
|
||||
|
||||
if (end)
|
||||
if (NAND_READ_PAGE(chip->dev, page, ptr, end, 0)) {
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
|
||||
uint32_t len)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
struct page_stat *pg_stat;
|
||||
struct nand_ecc_data *eccd;
|
||||
device_t nandbus;
|
||||
uint32_t page, num;
|
||||
uint8_t *oob = NULL;
|
||||
uint16_t *eccpos = NULL;
|
||||
int steps = 0, i, needwrite, err = 0;
|
||||
|
||||
nand_debug(NDBG_NAND,"%p prog page %x[%x]", chip, offset, len);
|
||||
|
||||
eccd = &chip->nand->ecc;
|
||||
cg = &chip->chip_geom;
|
||||
page = offset_to_page(cg, offset);
|
||||
num = len / cg->page_size;
|
||||
|
||||
if (eccd->eccmode != NAND_ECC_NONE) {
|
||||
steps = cg->page_size / eccd->eccsize;
|
||||
oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
|
||||
eccpos = default_software_ecc_positions(chip);
|
||||
}
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
while (num--) {
|
||||
if (NAND_PROGRAM_PAGE(chip->dev, page, buf, cg->page_size, 0)) {
|
||||
err = ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (eccd->eccmode != NAND_ECC_NONE) {
|
||||
if (NAND_GET_ECC(chip->dev, buf, &eccd->ecccalculated,
|
||||
&needwrite)) {
|
||||
err = ENXIO;
|
||||
break;
|
||||
}
|
||||
nand_debug(NDBG_ECC,"ECC calculated:");
|
||||
if (nand_debug_flag & NDBG_ECC)
|
||||
for (i = 0; i < (eccd->eccbytes * steps); i++)
|
||||
printf("%x ", eccd->ecccalculated[i]);
|
||||
|
||||
nand_debug(NDBG_ECC,"\n");
|
||||
|
||||
if (needwrite) {
|
||||
if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
|
||||
0)) {
|
||||
err = ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < (eccd->eccbytes * steps); i++)
|
||||
oob[eccpos[i]] = eccd->ecccalculated[i];
|
||||
|
||||
if (NAND_PROGRAM_OOB(chip->dev, page, oob,
|
||||
cg->oob_size, 0)) {
|
||||
err = ENXIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pg_stat = &(chip->pg_stat[page]);
|
||||
pg_stat->page_written++;
|
||||
|
||||
page++;
|
||||
buf += cg->page_size;
|
||||
}
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
|
||||
if (oob)
|
||||
free(oob, M_NAND);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
|
||||
uint32_t len)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
device_t nandbus;
|
||||
uint8_t *ptr;
|
||||
uint32_t page, num, end, begin = 0, begin_off;
|
||||
int retval = 0;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
page = offset_to_page(cg, offset);
|
||||
begin_off = offset - page * cg->page_size;
|
||||
if (begin_off) {
|
||||
begin = cg->page_size - begin_off;
|
||||
len -= begin;
|
||||
}
|
||||
num = len / cg->page_size;
|
||||
end = len % cg->page_size;
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
ptr = (uint8_t *)buf;
|
||||
if (begin_off) {
|
||||
if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, begin, begin_off)) {
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
page++;
|
||||
ptr += begin;
|
||||
}
|
||||
|
||||
while (num--) {
|
||||
if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
page++;
|
||||
ptr += cg->page_size;
|
||||
}
|
||||
|
||||
if (end)
|
||||
retval = NAND_PROGRAM_PAGE(chip->dev, page, ptr, end, 0);
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
int
|
||||
nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
|
||||
uint32_t len)
|
||||
{
|
||||
device_t nandbus;
|
||||
int retval = 0;
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
retval = NAND_READ_OOB(chip->dev, page, buf, len, 0);
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
|
||||
uint32_t len)
|
||||
{
|
||||
device_t nandbus;
|
||||
int retval = 0;
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
retval = NAND_PROGRAM_OOB(chip->dev, page, buf, len, 0);
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
int
|
||||
nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len)
|
||||
{
|
||||
device_t nandbus;
|
||||
struct chip_geom *cg;
|
||||
uint32_t block, num_blocks;
|
||||
int err = 0;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
if ((offset % cg->block_size) || (len % cg->block_size))
|
||||
return (EINVAL);
|
||||
|
||||
block = offset / cg->block_size;
|
||||
num_blocks = len / cg->block_size;
|
||||
nand_debug(NDBG_NAND,"%p erase blocks %d[%d]", chip, block, num_blocks);
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
while (num_blocks--) {
|
||||
if (!nand_check_bad_block(chip, block)) {
|
||||
if (NAND_ERASE_BLOCK(chip->dev, block)) {
|
||||
nand_debug(NDBG_NAND,"%p erase blocks %d error",
|
||||
chip, block);
|
||||
nand_mark_bad_block(chip, block);
|
||||
err = ENXIO;
|
||||
}
|
||||
} else
|
||||
err = ENXIO;
|
||||
|
||||
block++;
|
||||
};
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
|
||||
if (err)
|
||||
nand_update_bbt(chip);
|
||||
|
||||
return (err);
|
||||
}
|
385
sys/dev/nand/nand.h
Normal file
385
sys/dev/nand/nand.h
Normal file
@ -0,0 +1,385 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _DEV_NAND_H_
|
||||
#define _DEV_NAND_H_
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/taskqueue.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/bio.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/malloc.h>
|
||||
|
||||
#include <dev/nand/nand_dev.h>
|
||||
|
||||
MALLOC_DECLARE(M_NAND);
|
||||
|
||||
/* Read commands */
|
||||
#define NAND_CMD_READ 0x00
|
||||
#define NAND_CMD_CHNG_READ_COL 0x05
|
||||
#define NAND_CMD_READ_END 0x30
|
||||
#define NAND_CMD_READ_CACHE 0x31
|
||||
#define NAND_CMD_READ_CPBK 0x35
|
||||
#define NAND_CMD_READ_CACHE_END 0x3F
|
||||
#define NAND_CMD_CHNG_READ_COL_END 0xE0
|
||||
|
||||
/* Erase commands */
|
||||
#define NAND_CMD_ERASE 0x60
|
||||
#define NAND_CMD_ERASE_END 0xD0
|
||||
#define NAND_CMD_ERASE_INTLV 0xD1
|
||||
|
||||
/* Program commands */
|
||||
#define NAND_CMD_PROG 0x80
|
||||
#define NAND_CMD_CHNG_WRITE_COL 0x85
|
||||
#define NAND_CMD_PROG_END 0x10
|
||||
#define NAND_CMD_PROG_INTLV 0x11
|
||||
#define NAND_CMD_PROG_CACHE 0x15
|
||||
|
||||
/* Misc commands */
|
||||
#define NAND_CMD_STATUS 0x70
|
||||
#define NAND_CMD_STATUS_ENH 0x78
|
||||
#define NAND_CMD_READ_ID 0x90
|
||||
#define NAND_CMD_READ_PARAMETER 0xec
|
||||
#define NAND_CMD_READ_UNIQUE_ID 0xed
|
||||
#define NAND_CMD_GET_FEATURE 0xee
|
||||
#define NAND_CMD_SET_FEATURE 0xef
|
||||
|
||||
/* Reset commands */
|
||||
#define NAND_CMD_SYNCH_RESET 0xfc
|
||||
#define NAND_CMD_RESET 0xff
|
||||
|
||||
/* Small page flash commands */
|
||||
#define NAND_CMD_SMALLA 0x00
|
||||
#define NAND_CMD_SMALLB 0x01
|
||||
#define NAND_CMD_SMALLOOB 0x50
|
||||
|
||||
#define NAND_STATUS_FAIL 0x1
|
||||
#define NAND_STATUS_FAILC 0x2
|
||||
#define NAND_STATUS_ARDY 0x20
|
||||
#define NAND_STATUS_RDY 0x40
|
||||
#define NAND_STATUS_WP 0x80
|
||||
|
||||
#define NAND_LP_OOB_COLUMN_START 0x800
|
||||
#define NAND_LP_OOBSZ 0x40
|
||||
#define NAND_SP_OOB_COLUMN_START 0x200
|
||||
#define NAND_SP_OOBSZ 0x10
|
||||
|
||||
#define PAGE_PARAM_LENGTH 0x100
|
||||
#define PAGE_PARAMETER_DEF 0x0
|
||||
#define PAGE_PARAMETER_RED_1 0x100
|
||||
#define PAGE_PARAMETER_RED_2 0x200
|
||||
|
||||
#define ONFI_SIG_ADDR 0x20
|
||||
|
||||
#define NAND_MAX_CHIPS 0x4
|
||||
#define NAND_MAX_OOBSZ 512
|
||||
#define NAND_MAX_PAGESZ 16384
|
||||
|
||||
#define NAND_SMALL_PAGE_SIZE 0x200
|
||||
|
||||
#define NAND_16_BIT 0x00000001
|
||||
|
||||
#define NAND_ECC_NONE 0x0
|
||||
#define NAND_ECC_SOFT 0x1
|
||||
#define NAND_ECC_FULLHW 0x2
|
||||
#define NAND_ECC_PARTHW 0x4
|
||||
#define NAND_ECC_MODE_MASK 0x7
|
||||
|
||||
#define ECC_OK 0
|
||||
#define ECC_CORRECTABLE 1
|
||||
#define ECC_ERROR_ECC (-1)
|
||||
#define ECC_UNCORRECTABLE (-2)
|
||||
|
||||
#define NAND_MAN_SAMSUNG 0xec
|
||||
#define NAND_MAN_HYNIX 0xad
|
||||
#define NAND_MAN_STMICRO 0x20
|
||||
|
||||
struct nand_id {
|
||||
uint8_t man_id;
|
||||
uint8_t dev_id;
|
||||
};
|
||||
|
||||
struct nand_params {
|
||||
struct nand_id id;
|
||||
char *name;
|
||||
uint32_t chip_size;
|
||||
uint32_t page_size;
|
||||
uint32_t oob_size;
|
||||
uint32_t pages_per_block;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* nand debug levels */
|
||||
#define NDBG_NAND 0x01
|
||||
#define NDBG_CDEV 0x02
|
||||
#define NDBG_GEN 0x04
|
||||
#define NDBG_GEOM 0x08
|
||||
#define NDBG_BUS 0x10
|
||||
#define NDBG_SIM 0x20
|
||||
#define NDBG_CTRL 0x40
|
||||
#define NDBG_DRV 0x80
|
||||
#define NDBG_ECC 0x100
|
||||
|
||||
/* nand_debug_function */
|
||||
void nand_debug(int level, const char *fmt, ...);
|
||||
extern int nand_debug_flag;
|
||||
|
||||
/* ONFI features bit*/
|
||||
#define ONFI_FEAT_16BIT 0x01
|
||||
#define ONFI_FEAT_MULT_LUN 0x02
|
||||
#define ONFI_FEAT_INTLV_OPS 0x04
|
||||
#define ONFI_FEAT_CPBK_RESTRICT 0x08
|
||||
#define ONFI_FEAT_SRC_SYNCH 0x10
|
||||
|
||||
/* ONFI optional commands bits */
|
||||
#define ONFI_OPTCOM_PROG_CACHE 0x01
|
||||
#define ONFI_OPTCOM_READ_CACHE 0x02
|
||||
#define ONFI_OPTCOM_GETSET_FEAT 0x04
|
||||
#define ONFI_OPTCOM_STATUS_ENH 0x08
|
||||
#define ONFI_OPTCOM_COPYBACK 0x10
|
||||
#define ONFI_OPTCOM_UNIQUE_ID 0x20
|
||||
|
||||
|
||||
/* Layout of parameter page is defined in ONFI */
|
||||
struct onfi_params {
|
||||
char signature[4];
|
||||
uint16_t rev;
|
||||
uint16_t features;
|
||||
uint16_t optional_commands;
|
||||
uint8_t res1[22];
|
||||
char manufacturer_name[12];
|
||||
char device_model[20];
|
||||
uint8_t manufacturer_id;
|
||||
uint16_t date;
|
||||
uint8_t res2[13];
|
||||
uint32_t bytes_per_page;
|
||||
uint16_t spare_bytes_per_page;
|
||||
uint32_t bytes_per_partial_page;
|
||||
uint16_t spare_bytes_per_partial_page;
|
||||
uint32_t pages_per_block;
|
||||
uint32_t blocks_per_lun;
|
||||
uint8_t luns;
|
||||
uint8_t address_cycles;
|
||||
uint8_t bits_per_cell;
|
||||
uint16_t max_bad_block_per_lun;
|
||||
uint16_t block_endurance;
|
||||
uint8_t guaranteed_valid_blocks;
|
||||
uint16_t valid_block_endurance;
|
||||
uint8_t programs_per_page;
|
||||
uint8_t partial_prog_attr;
|
||||
uint8_t bits_of_ecc;
|
||||
uint8_t interleaved_addr_bits;
|
||||
uint8_t interleaved_oper_attr;
|
||||
uint8_t res3[13];
|
||||
uint8_t pin_capacitance;
|
||||
uint16_t asynch_timing_mode_support;
|
||||
uint16_t asynch_prog_cache_timing_mode_support;
|
||||
uint16_t t_prog; /* us, max page program time */
|
||||
uint16_t t_bers; /* us, max block erase time */
|
||||
uint16_t t_r; /* us, max page read time */
|
||||
uint16_t t_ccs; /* ns, min change column setup time */
|
||||
uint16_t source_synch_timing_mode_support;
|
||||
uint8_t source_synch_feat;
|
||||
uint16_t clk_input_capacitance;
|
||||
uint16_t io_capacitance;
|
||||
uint16_t input_capacitance;
|
||||
uint8_t input_capacitance_max;
|
||||
uint8_t driver_strength_support;
|
||||
uint8_t res4[12];
|
||||
uint16_t vendor_rev;
|
||||
uint8_t vendor_spec[8];
|
||||
uint16_t crc;
|
||||
};
|
||||
|
||||
struct nand_ecc_data {
|
||||
int eccsize; /* Number of data bytes per ECC step */
|
||||
int eccmode;
|
||||
int eccbytes; /* Number of ECC bytes per step */
|
||||
|
||||
uint16_t *eccpositions; /* Positions of ecc bytes */
|
||||
uint8_t ecccalculated[NAND_MAX_OOBSZ];
|
||||
uint8_t eccread[NAND_MAX_OOBSZ];
|
||||
};
|
||||
|
||||
struct ecc_stat {
|
||||
uint32_t ecc_succeded;
|
||||
uint32_t ecc_corrected;
|
||||
uint32_t ecc_failed;
|
||||
};
|
||||
|
||||
struct page_stat {
|
||||
struct ecc_stat ecc_stat;
|
||||
uint32_t page_read;
|
||||
uint32_t page_raw_read;
|
||||
uint32_t page_written;
|
||||
uint32_t page_raw_written;
|
||||
};
|
||||
|
||||
struct block_stat {
|
||||
uint32_t block_erased;
|
||||
};
|
||||
|
||||
struct chip_geom {
|
||||
uint32_t chip_size;
|
||||
uint32_t block_size;
|
||||
uint32_t page_size;
|
||||
uint32_t oob_size;
|
||||
|
||||
uint32_t luns;
|
||||
uint32_t blks_per_lun;
|
||||
uint32_t blks_per_chip;
|
||||
uint32_t pgs_per_blk;
|
||||
|
||||
uint32_t pg_mask;
|
||||
uint32_t blk_mask;
|
||||
uint32_t lun_mask;
|
||||
uint8_t blk_shift;
|
||||
uint8_t lun_shift;
|
||||
};
|
||||
|
||||
struct nand_chip {
|
||||
device_t dev;
|
||||
struct nand_id id;
|
||||
struct chip_geom chip_geom;
|
||||
|
||||
uint16_t t_prog; /* us, max page program time */
|
||||
uint16_t t_bers; /* us, max block erase time */
|
||||
uint16_t t_r; /* us, max page read time */
|
||||
uint16_t t_ccs; /* ns, min change column setup time */
|
||||
uint8_t num;
|
||||
uint8_t flags;
|
||||
|
||||
struct page_stat *pg_stat;
|
||||
struct block_stat *blk_stat;
|
||||
struct nand_softc *nand;
|
||||
struct nand_bbt *bbt;
|
||||
struct nand_ops *ops;
|
||||
struct cdev *cdev;
|
||||
|
||||
struct disk *ndisk;
|
||||
struct disk *rdisk;
|
||||
struct bio_queue_head bioq; /* bio queue */
|
||||
struct mtx qlock; /* bioq lock */
|
||||
struct taskqueue *tq; /* private task queue for i/o request */
|
||||
struct task iotask; /* i/o processing */
|
||||
|
||||
};
|
||||
|
||||
struct nand_softc {
|
||||
uint8_t flags;
|
||||
|
||||
char *chip_cdev_name;
|
||||
struct nand_ecc_data ecc;
|
||||
};
|
||||
|
||||
/* NAND ops */
|
||||
int nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len);
|
||||
int nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
|
||||
uint32_t len);
|
||||
int nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
|
||||
uint32_t len);
|
||||
int nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
|
||||
uint32_t len);
|
||||
int nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
|
||||
uint32_t len);
|
||||
int nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
|
||||
uint32_t len);
|
||||
int nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
|
||||
uint32_t len);
|
||||
|
||||
int nand_select_cs(device_t dev, uint8_t cs);
|
||||
|
||||
int nand_read_parameter(struct nand_softc *nand, struct onfi_params *param);
|
||||
int nand_synch_reset(struct nand_softc *nand);
|
||||
int nand_chng_read_col(device_t dev, uint32_t col, void *buf, size_t len);
|
||||
int nand_chng_write_col(device_t dev, uint32_t col, void *buf, size_t len);
|
||||
int nand_get_feature(device_t dev, uint8_t feat, void* buf);
|
||||
int nand_set_feature(device_t dev, uint8_t feat, void* buf);
|
||||
|
||||
|
||||
int nand_erase_block_intlv(device_t dev, uint32_t block);
|
||||
int nand_copyback_read(device_t dev, uint32_t page, uint32_t col,
|
||||
void *buf, size_t len);
|
||||
int nand_copyback_prog(device_t dev, uint32_t page, uint32_t col,
|
||||
void *buf, size_t len);
|
||||
int nand_copyback_prog_intlv(device_t dev, uint32_t page);
|
||||
int nand_prog_cache(device_t dev, uint32_t page, uint32_t col,
|
||||
void *buf, size_t len, uint8_t end);
|
||||
int nand_prog_intlv(device_t dev, uint32_t page, uint32_t col,
|
||||
void *buf, size_t len);
|
||||
int nand_read_cache(device_t dev, uint32_t page, uint32_t col,
|
||||
void *buf, size_t len, uint8_t end);
|
||||
|
||||
int nand_write_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data);
|
||||
int nand_read_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data);
|
||||
|
||||
int nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc);
|
||||
int nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
|
||||
uint8_t *readecc, uint8_t *calcecc);
|
||||
|
||||
/* Chip initialization */
|
||||
void nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
|
||||
int ecc_bytes, int ecc_size, uint16_t* eccposition, char* cdev_name);
|
||||
void nand_detach(struct nand_softc *nand);
|
||||
struct nand_params *nand_get_params(struct nand_id *id);
|
||||
|
||||
void nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params);
|
||||
void nand_set_params(struct nand_chip *chip, struct nand_params *params);
|
||||
int nand_init_stat(struct nand_chip *chip);
|
||||
void nand_destroy_stat(struct nand_chip *chip);
|
||||
|
||||
/* BBT */
|
||||
int nand_init_bbt(struct nand_chip *chip);
|
||||
void nand_destroy_bbt(struct nand_chip *chip);
|
||||
int nand_update_bbt(struct nand_chip *chip);
|
||||
int nand_mark_bad_block(struct nand_chip* chip, uint32_t block_num);
|
||||
int nand_check_bad_block(struct nand_chip* chip, uint32_t block_num);
|
||||
|
||||
/* cdev creation/removal */
|
||||
int nand_make_dev(struct nand_chip* chip);
|
||||
void nand_destroy_dev(struct nand_chip *chip);
|
||||
|
||||
int create_geom_disk(struct nand_chip* chip);
|
||||
int create_geom_raw_disk(struct nand_chip *chip);
|
||||
void destroy_geom_disk(struct nand_chip *chip);
|
||||
void destroy_geom_raw_disk(struct nand_chip *chip);
|
||||
|
||||
int init_chip_geom(struct chip_geom* cg, uint32_t luns, uint32_t blks_per_lun,
|
||||
uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size);
|
||||
int nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
|
||||
uint32_t *blk, uint32_t *pg);
|
||||
int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row);
|
||||
int nand_check_page_boundary(struct nand_chip *chip, uint32_t page);
|
||||
void nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param);
|
||||
|
||||
#endif /* _DEV_NAND_H_ */
|
273
sys/dev/nand/nand_bbt.c
Normal file
273
sys/dev/nand/nand_bbt.c
Normal file
@ -0,0 +1,273 @@
|
||||
/*-
|
||||
* Copyright (c) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
|
||||
#include "nand_if.h"
|
||||
|
||||
#define BBT_PRIMARY_PATTERN 0x01020304
|
||||
#define BBT_SECONDARY_PATTERN 0x05060708
|
||||
|
||||
enum bbt_place {
|
||||
BBT_NONE,
|
||||
BBT_PRIMARY,
|
||||
BBT_SECONDARY
|
||||
};
|
||||
|
||||
struct nand_bbt {
|
||||
struct nand_chip *chip;
|
||||
uint32_t primary_map;
|
||||
uint32_t secondary_map;
|
||||
enum bbt_place active;
|
||||
struct bbt_header *hdr;
|
||||
uint32_t tab_len;
|
||||
uint32_t *table;
|
||||
};
|
||||
|
||||
struct bbt_header {
|
||||
uint32_t pattern;
|
||||
int32_t seq_nr;
|
||||
};
|
||||
|
||||
static int nand_bbt_save(struct nand_bbt *);
|
||||
static int nand_bbt_load_hdr(struct nand_bbt *, struct bbt_header *, int8_t);
|
||||
static int nand_bbt_load_table(struct nand_bbt *);
|
||||
static int nand_bbt_prescan(struct nand_bbt *);
|
||||
|
||||
int
|
||||
nand_init_bbt(struct nand_chip *chip)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
struct nand_bbt *bbt;
|
||||
int err;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
|
||||
bbt = malloc(sizeof(struct nand_bbt), M_NAND, M_ZERO | M_WAITOK);
|
||||
if (!bbt) {
|
||||
device_printf(chip->dev,
|
||||
"Cannot allocate memory for bad block struct");
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
bbt->chip = chip;
|
||||
bbt->active = BBT_NONE;
|
||||
bbt->primary_map = cg->chip_size - cg->block_size;
|
||||
bbt->secondary_map = cg->chip_size - 2 * cg->block_size;
|
||||
bbt->tab_len = cg->blks_per_chip * sizeof(uint32_t);
|
||||
bbt->hdr = malloc(sizeof(struct bbt_header) + bbt->tab_len, M_NAND,
|
||||
M_WAITOK);
|
||||
if (!bbt->hdr) {
|
||||
device_printf(chip->dev, "Cannot allocate %d bytes for BB "
|
||||
"Table", bbt->tab_len);
|
||||
free(bbt, M_NAND);
|
||||
return (ENOMEM);
|
||||
}
|
||||
bbt->hdr->seq_nr = 0;
|
||||
bbt->table = (uint32_t *)((uint8_t *)bbt->hdr +
|
||||
sizeof(struct bbt_header));
|
||||
|
||||
err = nand_bbt_load_table(bbt);
|
||||
if (err) {
|
||||
free(bbt->table, M_NAND);
|
||||
free(bbt, M_NAND);
|
||||
return (err);
|
||||
}
|
||||
|
||||
chip->bbt = bbt;
|
||||
if (bbt->active == BBT_NONE) {
|
||||
bbt->active = BBT_PRIMARY;
|
||||
memset(bbt->table, 0xff, bbt->tab_len);
|
||||
nand_bbt_prescan(bbt);
|
||||
nand_bbt_save(bbt);
|
||||
} else
|
||||
device_printf(chip->dev, "Found BBT table for chip\n");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nand_destroy_bbt(struct nand_chip *chip)
|
||||
{
|
||||
|
||||
if (chip->bbt) {
|
||||
nand_bbt_save(chip->bbt);
|
||||
|
||||
free(chip->bbt->hdr, M_NAND);
|
||||
free(chip->bbt, M_NAND);
|
||||
chip->bbt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nand_update_bbt(struct nand_chip *chip)
|
||||
{
|
||||
|
||||
nand_bbt_save(chip->bbt);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_bbt_save(struct nand_bbt *bbt)
|
||||
{
|
||||
enum bbt_place next;
|
||||
uint32_t addr;
|
||||
int32_t err;
|
||||
|
||||
if (bbt->active == BBT_PRIMARY) {
|
||||
addr = bbt->secondary_map;
|
||||
bbt->hdr->pattern = BBT_SECONDARY_PATTERN;
|
||||
next = BBT_SECONDARY;
|
||||
} else {
|
||||
addr = bbt->primary_map;
|
||||
bbt->hdr->pattern = BBT_PRIMARY_PATTERN;
|
||||
next = BBT_PRIMARY;
|
||||
}
|
||||
|
||||
err = nand_erase_blocks(bbt->chip, addr,
|
||||
bbt->chip->chip_geom.block_size);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
bbt->hdr->seq_nr++;
|
||||
|
||||
err = nand_prog_pages_raw(bbt->chip, addr, bbt->hdr,
|
||||
bbt->tab_len + sizeof(struct bbt_header));
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
bbt->active = next;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_bbt_load_hdr(struct nand_bbt *bbt, struct bbt_header *hdr, int8_t primary)
|
||||
{
|
||||
uint32_t addr;
|
||||
|
||||
if (primary)
|
||||
addr = bbt->primary_map;
|
||||
else
|
||||
addr = bbt->secondary_map;
|
||||
|
||||
return (nand_read_pages_raw(bbt->chip, addr, hdr,
|
||||
sizeof(struct bbt_header)));
|
||||
}
|
||||
|
||||
static int
|
||||
nand_bbt_load_table(struct nand_bbt *bbt)
|
||||
{
|
||||
struct bbt_header hdr1, hdr2;
|
||||
uint32_t address = 0;
|
||||
int err = 0;
|
||||
|
||||
bzero(&hdr1, sizeof(hdr1));
|
||||
bzero(&hdr2, sizeof(hdr2));
|
||||
|
||||
nand_bbt_load_hdr(bbt, &hdr1, 1);
|
||||
if (hdr1.pattern == BBT_PRIMARY_PATTERN) {
|
||||
bbt->active = BBT_PRIMARY;
|
||||
address = bbt->primary_map;
|
||||
} else
|
||||
bzero(&hdr1, sizeof(hdr1));
|
||||
|
||||
|
||||
nand_bbt_load_hdr(bbt, &hdr2, 0);
|
||||
if ((hdr2.pattern == BBT_SECONDARY_PATTERN) &&
|
||||
(hdr2.seq_nr > hdr1.seq_nr)) {
|
||||
bbt->active = BBT_SECONDARY;
|
||||
address = bbt->secondary_map;
|
||||
} else
|
||||
bzero(&hdr2, sizeof(hdr2));
|
||||
|
||||
if (bbt->active != BBT_NONE)
|
||||
err = nand_read_pages_raw(bbt->chip, address, bbt->hdr,
|
||||
bbt->tab_len + sizeof(struct bbt_header));
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_bbt_prescan(struct nand_bbt *bbt)
|
||||
{
|
||||
int32_t i;
|
||||
uint8_t bad;
|
||||
bool printed_hash = 0;
|
||||
|
||||
device_printf(bbt->chip->dev, "No BBT found. Prescan chip...\n");
|
||||
for (i = 0; i < bbt->chip->chip_geom.blks_per_chip; i++) {
|
||||
if (NAND_IS_BLK_BAD(bbt->chip->dev, i, &bad))
|
||||
return (ENXIO);
|
||||
|
||||
if (bad) {
|
||||
device_printf(bbt->chip->dev, "Bad block(%d)\n", i);
|
||||
bbt->table[i] = 0x0FFFFFFF;
|
||||
}
|
||||
if (!(i % 100)) {
|
||||
printf("#");
|
||||
printed_hash = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (printed_hash)
|
||||
printf("\n");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nand_check_bad_block(struct nand_chip *chip, uint32_t block_number)
|
||||
{
|
||||
|
||||
if (!chip || !chip->bbt)
|
||||
return (0);
|
||||
|
||||
if ((chip->bbt->table[block_number] & 0xF0000000) == 0)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nand_mark_bad_block(struct nand_chip *chip, uint32_t block_number)
|
||||
{
|
||||
|
||||
chip->bbt->table[block_number] = 0x0FFFFFFF;
|
||||
|
||||
return (0);
|
||||
}
|
413
sys/dev/nand/nand_cdev.c
Normal file
413
sys/dev/nand/nand_cdev.c
Normal file
@ -0,0 +1,413 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandbus.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nand_if.h"
|
||||
#include "nandbus_if.h"
|
||||
|
||||
static int nand_page_stat(struct nand_chip *, struct page_stat_io *);
|
||||
static int nand_block_stat(struct nand_chip *, struct block_stat_io *);
|
||||
|
||||
static d_ioctl_t nand_ioctl;
|
||||
static d_open_t nand_open;
|
||||
static d_strategy_t nand_strategy;
|
||||
|
||||
static struct cdevsw nand_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_name = "nand",
|
||||
.d_open = nand_open,
|
||||
.d_read = physread,
|
||||
.d_write = physwrite,
|
||||
.d_ioctl = nand_ioctl,
|
||||
.d_strategy = nand_strategy,
|
||||
};
|
||||
|
||||
static int
|
||||
offset_to_page(struct chip_geom *cg, uint32_t offset)
|
||||
{
|
||||
|
||||
return (offset / cg->page_size);
|
||||
}
|
||||
|
||||
static int
|
||||
offset_to_page_off(struct chip_geom *cg, uint32_t offset)
|
||||
{
|
||||
|
||||
return (offset % cg->page_size);
|
||||
}
|
||||
|
||||
int
|
||||
nand_make_dev(struct nand_chip *chip)
|
||||
{
|
||||
struct nandbus_ivar *ivar;
|
||||
device_t parent, nandbus;
|
||||
int parent_unit, unit;
|
||||
char *name;
|
||||
|
||||
ivar = device_get_ivars(chip->dev);
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
|
||||
if (ivar->chip_cdev_name) {
|
||||
name = ivar->chip_cdev_name;
|
||||
|
||||
/*
|
||||
* If we got distinct name for chip device we can enumarete it
|
||||
* based on contoller number.
|
||||
*/
|
||||
parent = device_get_parent(nandbus);
|
||||
} else {
|
||||
name = "nand";
|
||||
parent = nandbus;
|
||||
}
|
||||
|
||||
parent_unit = device_get_unit(parent);
|
||||
unit = parent_unit * 4 + chip->num;
|
||||
chip->cdev = make_dev(&nand_cdevsw, unit, UID_ROOT, GID_WHEEL,
|
||||
0666, "%s%d.%d", name, parent_unit, chip->num);
|
||||
|
||||
if (chip->cdev == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
if (bootverbose)
|
||||
device_printf(chip->dev, "Created cdev %s%d.%d for chip "
|
||||
"[0x%0x, 0x%0x]\n", name, parent_unit, chip->num,
|
||||
ivar->man_id, ivar->dev_id);
|
||||
|
||||
chip->cdev->si_drv1 = chip;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nand_destroy_dev(struct nand_chip *chip)
|
||||
{
|
||||
|
||||
if (chip->cdev)
|
||||
destroy_dev(chip->cdev);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
device_t nandbus;
|
||||
int start_page, count, off, err = 0;
|
||||
uint8_t *ptr, *tmp;
|
||||
|
||||
nand_debug(NDBG_CDEV, "Read from chip%d [%p] at %d\n", chip->num,
|
||||
chip, offset);
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
start_page = offset_to_page(cg, offset);
|
||||
off = offset_to_page_off(cg, offset);
|
||||
count = (len > cg->page_size - off) ? cg->page_size - off : len;
|
||||
|
||||
ptr = (uint8_t *)buf;
|
||||
while (len > 0) {
|
||||
if (len < cg->page_size) {
|
||||
tmp = malloc(cg->page_size, M_NAND, M_WAITOK);
|
||||
if (!tmp) {
|
||||
err = ENOMEM;
|
||||
break;
|
||||
}
|
||||
err = NAND_READ_PAGE(chip->dev, start_page,
|
||||
tmp, cg->page_size, 0);
|
||||
if (err) {
|
||||
free(tmp, M_NAND);
|
||||
break;
|
||||
}
|
||||
bcopy(tmp + off, ptr, count);
|
||||
free(tmp, M_NAND);
|
||||
} else {
|
||||
err = NAND_READ_PAGE(chip->dev, start_page,
|
||||
ptr, cg->page_size, 0);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
len -= count;
|
||||
start_page++;
|
||||
ptr += count;
|
||||
count = (len > cg->page_size) ? cg->page_size : len;
|
||||
off = 0;
|
||||
}
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
device_t nandbus;
|
||||
int off, start_page, err = 0;
|
||||
uint8_t *ptr;
|
||||
|
||||
nand_debug(NDBG_CDEV, "Write to chip %d [%p] at %d\n", chip->num,
|
||||
chip, offset);
|
||||
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
NANDBUS_LOCK(nandbus);
|
||||
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
start_page = offset_to_page(cg, offset);
|
||||
off = offset_to_page_off(cg, offset);
|
||||
|
||||
if (off != 0 || (len % cg->page_size) != 0) {
|
||||
printf("Not aligned write start [0x%08x] size [0x%08x]\n",
|
||||
off, len);
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
ptr = (uint8_t *)buf;
|
||||
while (len > 0) {
|
||||
err = NAND_PROGRAM_PAGE(chip->dev, start_page, ptr,
|
||||
cg->page_size, 0);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
len -= cg->page_size;
|
||||
start_page++;
|
||||
ptr += cg->page_size;
|
||||
}
|
||||
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static void
|
||||
nand_strategy(struct bio *bp)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
struct cdev *dev;
|
||||
int err = 0;
|
||||
|
||||
dev = bp->bio_dev;
|
||||
chip = dev->si_drv1;
|
||||
|
||||
nand_debug(NDBG_CDEV, "Strategy %s on chip %d [%p]\n",
|
||||
(bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : "WRITE",
|
||||
chip->num, chip);
|
||||
|
||||
if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
|
||||
err = nand_read(chip,
|
||||
bp->bio_offset & 0xffffffff,
|
||||
bp->bio_data, bp->bio_bcount);
|
||||
} else {
|
||||
err = nand_write(chip,
|
||||
bp->bio_offset & 0xffffffff,
|
||||
bp->bio_data, bp->bio_bcount);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
bp->bio_resid = 0;
|
||||
else {
|
||||
bp->bio_error = EIO;
|
||||
bp->bio_flags |= BIO_ERROR;
|
||||
bp->bio_resid = bp->bio_bcount;
|
||||
}
|
||||
|
||||
biodone(bp);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset,
|
||||
uint32_t len, uint8_t *data, uint8_t write)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
uint8_t *buf = NULL;
|
||||
int ret = 0;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
|
||||
buf = malloc(cg->oob_size, M_NAND, M_WAITOK);
|
||||
if (!buf)
|
||||
return (ENOMEM);
|
||||
|
||||
memset(buf, 0xff, cg->oob_size);
|
||||
|
||||
if (!write) {
|
||||
ret = nand_read_oob(chip, page, buf, cg->oob_size);
|
||||
copyout(buf, data, len);
|
||||
} else {
|
||||
copyin(data, buf, len);
|
||||
ret = nand_prog_oob(chip, page, buf, cg->oob_size);
|
||||
}
|
||||
|
||||
free(buf, M_NAND);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
|
||||
struct thread *td)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
struct nand_oob_rw *oob_rw = NULL;
|
||||
struct nand_raw_rw *raw_rw = NULL;
|
||||
device_t nandbus;
|
||||
uint8_t *buf = NULL;
|
||||
int ret = 0;
|
||||
uint8_t status;
|
||||
|
||||
chip = (struct nand_chip *)dev->si_drv1;
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
|
||||
if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
|
||||
raw_rw = (struct nand_raw_rw *)data;
|
||||
buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
|
||||
}
|
||||
switch(cmd) {
|
||||
case NAND_IO_ERASE:
|
||||
ret = nand_erase_blocks(chip, ((off_t *)data)[0],
|
||||
((off_t *)data)[1]);
|
||||
break;
|
||||
|
||||
case NAND_IO_OOB_READ:
|
||||
oob_rw = (struct nand_oob_rw *)data;
|
||||
ret = nand_oob_access(chip, oob_rw->page, 0,
|
||||
oob_rw->len, oob_rw->data, 0);
|
||||
break;
|
||||
|
||||
case NAND_IO_OOB_PROG:
|
||||
oob_rw = (struct nand_oob_rw *)data;
|
||||
ret = nand_oob_access(chip, oob_rw->page, 0,
|
||||
oob_rw->len, oob_rw->data, 1);
|
||||
break;
|
||||
|
||||
case NAND_IO_GET_STATUS:
|
||||
NANDBUS_LOCK(nandbus);
|
||||
ret = NANDBUS_GET_STATUS(nandbus, &status);
|
||||
if (ret == 0)
|
||||
*(uint8_t *)data = status;
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
break;
|
||||
|
||||
case NAND_IO_RAW_PROG:
|
||||
ret = copyin(raw_rw->data, buf, raw_rw->len);
|
||||
if (ret)
|
||||
break;
|
||||
ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
|
||||
raw_rw->len);
|
||||
break;
|
||||
|
||||
case NAND_IO_RAW_READ:
|
||||
ret = nand_read_pages_raw(chip, raw_rw->off, buf,
|
||||
raw_rw->len);
|
||||
if (ret)
|
||||
break;
|
||||
ret = copyout(buf, raw_rw->data, raw_rw->len);
|
||||
break;
|
||||
|
||||
case NAND_IO_PAGE_STAT:
|
||||
ret = nand_page_stat(chip, (struct page_stat_io *)data);
|
||||
break;
|
||||
|
||||
case NAND_IO_BLOCK_STAT:
|
||||
ret = nand_block_stat(chip, (struct block_stat_io *)data);
|
||||
break;
|
||||
|
||||
case NAND_IO_GET_CHIP_PARAM:
|
||||
nand_get_chip_param(chip, (struct chip_param_io *)data);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown nand_ioctl request \n");
|
||||
ret = EIO;
|
||||
}
|
||||
|
||||
if (buf)
|
||||
free(buf, M_NAND);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_page_stat(struct nand_chip *chip, struct page_stat_io *page_stat)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
struct page_stat *stat;
|
||||
int num_pages;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
num_pages = cg->pgs_per_blk * cg->blks_per_lun * cg->luns;
|
||||
if (page_stat->page_num >= num_pages)
|
||||
return (EINVAL);
|
||||
|
||||
stat = &chip->pg_stat[page_stat->page_num];
|
||||
page_stat->page_read = stat->page_read;
|
||||
page_stat->page_written = stat->page_written;
|
||||
page_stat->page_raw_read = stat->page_raw_read;
|
||||
page_stat->page_raw_written = stat->page_raw_written;
|
||||
page_stat->ecc_succeded = stat->ecc_stat.ecc_succeded;
|
||||
page_stat->ecc_corrected = stat->ecc_stat.ecc_corrected;
|
||||
page_stat->ecc_failed = stat->ecc_stat.ecc_failed;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_block_stat(struct nand_chip *chip, struct block_stat_io *block_stat)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
uint32_t block_num = block_stat->block_num;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
if (block_num >= cg->blks_per_lun * cg->luns)
|
||||
return (EINVAL);
|
||||
|
||||
block_stat->block_erased = chip->blk_stat[block_num].block_erased;
|
||||
|
||||
return (0);
|
||||
}
|
90
sys/dev/nand/nand_dev.h
Normal file
90
sys/dev/nand/nand_dev.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _DEV_NAND_CDEV_H_
|
||||
#define _DEV_NAND_CDEV_H_
|
||||
|
||||
#include <sys/ioccom.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
struct nand_raw_rw {
|
||||
off_t off;
|
||||
off_t len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct nand_oob_rw {
|
||||
uint32_t page;
|
||||
off_t len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
#define NAND_IOCTL_GROUP 'N'
|
||||
#define NAND_IO_ERASE _IOWR(NAND_IOCTL_GROUP, 0x0, off_t[2])
|
||||
|
||||
#define NAND_IO_OOB_READ _IOWR(NAND_IOCTL_GROUP, 0x1, struct nand_oob_rw)
|
||||
|
||||
#define NAND_IO_OOB_PROG _IOWR(NAND_IOCTL_GROUP, 0x2, struct nand_oob_rw)
|
||||
|
||||
#define NAND_IO_RAW_READ _IOWR(NAND_IOCTL_GROUP, 0x3, struct nand_raw_rw)
|
||||
|
||||
#define NAND_IO_RAW_PROG _IOWR(NAND_IOCTL_GROUP, 0x4, struct nand_raw_rw)
|
||||
|
||||
#define NAND_IO_GET_STATUS _IOWR(NAND_IOCTL_GROUP, 0x5, uint8_t)
|
||||
|
||||
struct page_stat_io {
|
||||
uint32_t page_num;
|
||||
uint32_t page_read;
|
||||
uint32_t page_written;
|
||||
uint32_t page_raw_read;
|
||||
uint32_t page_raw_written;
|
||||
uint32_t ecc_succeded;
|
||||
uint32_t ecc_corrected;
|
||||
uint32_t ecc_failed;
|
||||
};
|
||||
#define NAND_IO_PAGE_STAT _IOWR(NAND_IOCTL_GROUP, 0x6, \
|
||||
struct page_stat_io)
|
||||
|
||||
struct block_stat_io {
|
||||
uint32_t block_num;
|
||||
uint32_t block_erased;
|
||||
};
|
||||
#define NAND_IO_BLOCK_STAT _IOWR(NAND_IOCTL_GROUP, 0x7, \
|
||||
struct block_stat_io)
|
||||
|
||||
struct chip_param_io {
|
||||
uint32_t page_size;
|
||||
uint32_t oob_size;
|
||||
|
||||
uint32_t blocks;
|
||||
uint32_t pages_per_block;
|
||||
};
|
||||
#define NAND_IO_GET_CHIP_PARAM _IOWR(NAND_IOCTL_GROUP, 0x8, \
|
||||
struct chip_param_io)
|
||||
|
||||
#endif /* _DEV_NAND_CDEV_H_ */
|
56
sys/dev/nand/nand_ecc_pos.h
Normal file
56
sys/dev/nand/nand_ecc_pos.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _DEV_NAND_ECC_POS_H_
|
||||
#define _DEV_NAND_ECC_POS_H_
|
||||
|
||||
static uint16_t default_software_ecc_positions_16[] = {2, 0, 1, 7, 4, 6};
|
||||
|
||||
static uint16_t default_software_ecc_positions_64[] = {
|
||||
|
||||
42, 40, 41, 45, 43, 44, 48, 46,
|
||||
47, 51, 49, 50, 54, 52, 53, 57,
|
||||
55, 56, 60, 58, 59, 63, 61, 62
|
||||
};
|
||||
|
||||
static uint16_t default_software_ecc_positions_128[] = {
|
||||
8, 9, 10, 11, 12, 13,
|
||||
18, 19, 20, 21, 22, 23,
|
||||
28, 29, 30, 31, 32, 33,
|
||||
38, 39, 40, 41, 42, 43,
|
||||
48, 49, 50, 51, 52, 53,
|
||||
58, 59, 60, 61, 62, 63,
|
||||
68, 69, 70, 71, 72, 73,
|
||||
78, 79, 80, 81, 82, 83,
|
||||
88, 89, 90, 91, 92, 93,
|
||||
98, 99, 100, 101, 102, 103,
|
||||
108, 109, 110, 111, 112, 113,
|
||||
118, 119, 120, 121, 122, 123,
|
||||
};
|
||||
#endif /* _DEV_NAND_ECC_POS_H_ */
|
||||
|
1320
sys/dev/nand/nand_generic.c
Normal file
1320
sys/dev/nand/nand_generic.c
Normal file
File diff suppressed because it is too large
Load Diff
414
sys/dev/nand/nand_geom.c
Normal file
414
sys/dev/nand/nand_geom.c
Normal file
@ -0,0 +1,414 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/bio.h>
|
||||
#include <geom/geom.h>
|
||||
#include <geom/geom_disk.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandbus.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nand_if.h"
|
||||
#include "nandbus_if.h"
|
||||
|
||||
#define BIO_NAND_STD ((void *)1)
|
||||
#define BIO_NAND_RAW ((void *)2)
|
||||
|
||||
static disk_ioctl_t nand_ioctl;
|
||||
static disk_getattr_t nand_getattr;
|
||||
static disk_strategy_t nand_strategy;
|
||||
static disk_strategy_t nand_strategy_raw;
|
||||
|
||||
static int
|
||||
nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
|
||||
{
|
||||
|
||||
nand_debug(NDBG_GEOM, "Read from chip %d [%p] at %d", chip->num, chip,
|
||||
offset);
|
||||
|
||||
return (nand_read_pages(chip, offset, buf, len));
|
||||
}
|
||||
|
||||
static int
|
||||
nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len)
|
||||
{
|
||||
|
||||
nand_debug(NDBG_GEOM, "Write to chip %d [%p] at %d", chip->num, chip,
|
||||
offset);
|
||||
|
||||
return (nand_prog_pages(chip, offset, buf, len));
|
||||
}
|
||||
|
||||
static int
|
||||
nand_read_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
|
||||
{
|
||||
nand_debug(NDBG_GEOM, "Raw read from chip %d [%p] at %d", chip->num,
|
||||
chip, offset);
|
||||
|
||||
return (nand_read_pages_raw(chip, offset, buf, len));
|
||||
}
|
||||
|
||||
static int
|
||||
nand_write_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
|
||||
{
|
||||
|
||||
nand_debug(NDBG_GEOM, "Raw write to chip %d [%p] at %d", chip->num,
|
||||
chip, offset);
|
||||
|
||||
return (nand_prog_pages_raw(chip, offset, buf, len));
|
||||
}
|
||||
|
||||
static void
|
||||
nand_strategy(struct bio *bp)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
|
||||
chip = (struct nand_chip *)bp->bio_disk->d_drv1;
|
||||
|
||||
bp->bio_driver1 = BIO_NAND_STD;
|
||||
|
||||
nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]",
|
||||
(bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" :
|
||||
((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" :
|
||||
((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")),
|
||||
chip->num, chip);
|
||||
|
||||
mtx_lock(&chip->qlock);
|
||||
bioq_insert_tail(&chip->bioq, bp);
|
||||
mtx_unlock(&chip->qlock);
|
||||
taskqueue_enqueue(chip->tq, &chip->iotask);
|
||||
}
|
||||
|
||||
static void
|
||||
nand_strategy_raw(struct bio *bp)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
|
||||
chip = (struct nand_chip *)bp->bio_disk->d_drv1;
|
||||
|
||||
/* Inform taskqueue that it's a raw access */
|
||||
bp->bio_driver1 = BIO_NAND_RAW;
|
||||
|
||||
nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]",
|
||||
(bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" :
|
||||
((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" :
|
||||
((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")),
|
||||
chip->num, chip);
|
||||
|
||||
mtx_lock(&chip->qlock);
|
||||
bioq_insert_tail(&chip->bioq, bp);
|
||||
mtx_unlock(&chip->qlock);
|
||||
taskqueue_enqueue(chip->tq, &chip->iotask);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset,
|
||||
uint32_t len, uint8_t *data, uint8_t write)
|
||||
{
|
||||
struct chip_geom *cg;
|
||||
int ret = 0;
|
||||
|
||||
cg = &chip->chip_geom;
|
||||
|
||||
if (!write)
|
||||
ret = nand_read_oob(chip, page, data, cg->oob_size);
|
||||
else
|
||||
ret = nand_prog_oob(chip, page, data, cg->oob_size);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_getattr(struct bio *bp)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
struct chip_geom *cg;
|
||||
device_t dev;
|
||||
|
||||
if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
chip = (struct nand_chip *)bp->bio_disk->d_drv1;
|
||||
cg = &(chip->chip_geom);
|
||||
|
||||
dev = device_get_parent(chip->dev);
|
||||
dev = device_get_parent(dev);
|
||||
|
||||
do {
|
||||
if (g_handleattr_int(bp, "NAND::oobsize", cg->oob_size))
|
||||
break;
|
||||
else if (g_handleattr_int(bp, "NAND::pagesize", cg->page_size))
|
||||
break;
|
||||
else if (g_handleattr_int(bp, "NAND::blocksize",
|
||||
cg->block_size))
|
||||
break;
|
||||
else if (g_handleattr(bp, "NAND::device", &(dev),
|
||||
sizeof(device_t)))
|
||||
break;
|
||||
|
||||
return (ERESTART);
|
||||
} while (0);
|
||||
|
||||
return (EJUSTRETURN);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag,
|
||||
struct thread *td)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
struct nand_oob_rw *oob_rw = NULL;
|
||||
struct nand_raw_rw *raw_rw = NULL;
|
||||
device_t nandbus;
|
||||
uint8_t *buf = NULL;
|
||||
int ret = 0;
|
||||
uint8_t status;
|
||||
|
||||
chip = (struct nand_chip *)ndisk->d_drv1;
|
||||
nandbus = device_get_parent(chip->dev);
|
||||
|
||||
if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
|
||||
raw_rw = (struct nand_raw_rw *)data;
|
||||
buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
|
||||
}
|
||||
switch (cmd) {
|
||||
case NAND_IO_ERASE:
|
||||
ret = nand_erase_blocks(chip, ((off_t *)data)[0],
|
||||
((off_t *)data)[1]);
|
||||
break;
|
||||
|
||||
case NAND_IO_OOB_READ:
|
||||
oob_rw = (struct nand_oob_rw *)data;
|
||||
ret = nand_oob_access(chip, oob_rw->page, 0,
|
||||
oob_rw->len, oob_rw->data, 0);
|
||||
break;
|
||||
|
||||
case NAND_IO_OOB_PROG:
|
||||
oob_rw = (struct nand_oob_rw *)data;
|
||||
ret = nand_oob_access(chip, oob_rw->page, 0,
|
||||
oob_rw->len, oob_rw->data, 1);
|
||||
break;
|
||||
|
||||
case NAND_IO_GET_STATUS:
|
||||
NANDBUS_LOCK(nandbus);
|
||||
ret = NANDBUS_GET_STATUS(nandbus, &status);
|
||||
if (ret == 0)
|
||||
*(uint8_t *)data = status;
|
||||
NANDBUS_UNLOCK(nandbus);
|
||||
break;
|
||||
|
||||
case NAND_IO_RAW_PROG:
|
||||
copyin(raw_rw->data, buf, raw_rw->len);
|
||||
ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
|
||||
raw_rw->len);
|
||||
break;
|
||||
|
||||
case NAND_IO_RAW_READ:
|
||||
ret = nand_read_pages_raw(chip, raw_rw->off, buf,
|
||||
raw_rw->len);
|
||||
copyout(buf, raw_rw->data, raw_rw->len);
|
||||
break;
|
||||
|
||||
case NAND_IO_GET_CHIP_PARAM:
|
||||
nand_get_chip_param(chip, (struct chip_param_io *)data);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown nand_ioctl request \n");
|
||||
ret = EIO;
|
||||
}
|
||||
|
||||
if (buf)
|
||||
free(buf, M_NAND);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
nand_io_proc(void *arg, int pending)
|
||||
{
|
||||
struct nand_chip *chip = arg;
|
||||
struct bio *bp;
|
||||
int err = 0;
|
||||
|
||||
for (;;) {
|
||||
mtx_lock(&chip->qlock);
|
||||
bp = bioq_takefirst(&chip->bioq);
|
||||
mtx_unlock(&chip->qlock);
|
||||
if (bp == NULL)
|
||||
break;
|
||||
|
||||
if (bp->bio_driver1 == BIO_NAND_STD) {
|
||||
if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
|
||||
err = nand_read(chip,
|
||||
bp->bio_offset & 0xffffffff,
|
||||
bp->bio_data, bp->bio_bcount);
|
||||
} else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) {
|
||||
err = nand_write(chip,
|
||||
bp->bio_offset & 0xffffffff,
|
||||
bp->bio_data, bp->bio_bcount);
|
||||
}
|
||||
} else if (bp->bio_driver1 == BIO_NAND_RAW) {
|
||||
if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
|
||||
err = nand_read_raw(chip,
|
||||
bp->bio_offset & 0xffffffff,
|
||||
bp->bio_data, bp->bio_bcount);
|
||||
} else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) {
|
||||
err = nand_write_raw(chip,
|
||||
bp->bio_offset & 0xffffffff,
|
||||
bp->bio_data, bp->bio_bcount);
|
||||
}
|
||||
} else
|
||||
panic("Unknown access type in bio->bio_driver1\n");
|
||||
|
||||
if ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE) {
|
||||
nand_debug(NDBG_GEOM, "Delete on chip%d offset %lld "
|
||||
"length %ld\n", chip->num, bp->bio_offset,
|
||||
bp->bio_bcount);
|
||||
err = nand_erase_blocks(chip,
|
||||
bp->bio_offset & 0xffffffff,
|
||||
bp->bio_bcount);
|
||||
}
|
||||
|
||||
if (err == 0 || err == ECC_CORRECTABLE)
|
||||
bp->bio_resid = 0;
|
||||
else {
|
||||
nand_debug(NDBG_GEOM,"nand_[read|write|erase_blocks] "
|
||||
"error: %d\n", err);
|
||||
|
||||
bp->bio_error = EIO;
|
||||
bp->bio_flags |= BIO_ERROR;
|
||||
bp->bio_resid = bp->bio_bcount;
|
||||
}
|
||||
biodone(bp);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
create_geom_disk(struct nand_chip *chip)
|
||||
{
|
||||
struct disk *ndisk, *rdisk;
|
||||
|
||||
/* Create the disk device */
|
||||
ndisk = disk_alloc();
|
||||
ndisk->d_strategy = nand_strategy;
|
||||
ndisk->d_ioctl = nand_ioctl;
|
||||
ndisk->d_getattr = nand_getattr;
|
||||
ndisk->d_name = "gnand";
|
||||
ndisk->d_drv1 = chip;
|
||||
ndisk->d_maxsize = chip->chip_geom.block_size;
|
||||
ndisk->d_sectorsize = chip->chip_geom.page_size;
|
||||
ndisk->d_mediasize = chip->chip_geom.chip_size;
|
||||
ndisk->d_unit = chip->num +
|
||||
10 * device_get_unit(device_get_parent(chip->dev));
|
||||
|
||||
/*
|
||||
* When using BBT, make two last blocks of device unavailable
|
||||
* to user (because those are used to store BBT table).
|
||||
*/
|
||||
if (chip->bbt != NULL)
|
||||
ndisk->d_mediasize -= (2 * chip->chip_geom.block_size);
|
||||
|
||||
ndisk->d_flags = DISKFLAG_CANDELETE;
|
||||
|
||||
snprintf(ndisk->d_ident, sizeof(ndisk->d_ident),
|
||||
"nand: Man:0x%02x Dev:0x%02x", chip->id.man_id, chip->id.dev_id);
|
||||
|
||||
disk_create(ndisk, DISK_VERSION);
|
||||
|
||||
/* Create the RAW disk device */
|
||||
rdisk = disk_alloc();
|
||||
rdisk->d_strategy = nand_strategy_raw;
|
||||
rdisk->d_ioctl = nand_ioctl;
|
||||
rdisk->d_getattr = nand_getattr;
|
||||
rdisk->d_name = "gnand.raw";
|
||||
rdisk->d_drv1 = chip;
|
||||
rdisk->d_maxsize = chip->chip_geom.block_size;
|
||||
rdisk->d_sectorsize = chip->chip_geom.page_size;
|
||||
rdisk->d_mediasize = chip->chip_geom.chip_size;
|
||||
rdisk->d_unit = chip->num +
|
||||
10 * device_get_unit(device_get_parent(chip->dev));
|
||||
|
||||
rdisk->d_flags = DISKFLAG_CANDELETE;
|
||||
|
||||
snprintf(rdisk->d_ident, sizeof(rdisk->d_ident),
|
||||
"nand_raw: Man:0x%02x Dev:0x%02x", chip->id.man_id,
|
||||
chip->id.dev_id);
|
||||
|
||||
disk_create(rdisk, DISK_VERSION);
|
||||
|
||||
chip->ndisk = ndisk;
|
||||
chip->rdisk = rdisk;
|
||||
|
||||
mtx_init(&chip->qlock, "NAND I/O lock", NULL, MTX_DEF);
|
||||
bioq_init(&chip->bioq);
|
||||
|
||||
TASK_INIT(&chip->iotask, 0, nand_io_proc, chip);
|
||||
chip->tq = taskqueue_create("nand_taskq", M_WAITOK,
|
||||
taskqueue_thread_enqueue, &chip->tq);
|
||||
taskqueue_start_threads(&chip->tq, 1, PI_DISK, "nand taskq");
|
||||
|
||||
if (bootverbose)
|
||||
device_printf(chip->dev, "Created gnand%d for chip [0x%0x, "
|
||||
"0x%0x]\n", ndisk->d_unit, chip->id.man_id,
|
||||
chip->id.dev_id);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
destroy_geom_disk(struct nand_chip *chip)
|
||||
{
|
||||
struct bio *bp;
|
||||
|
||||
taskqueue_free(chip->tq);
|
||||
disk_destroy(chip->ndisk);
|
||||
disk_destroy(chip->rdisk);
|
||||
|
||||
mtx_lock(&chip->qlock);
|
||||
for (;;) {
|
||||
bp = bioq_takefirst(&chip->bioq);
|
||||
if (bp == NULL)
|
||||
break;
|
||||
bp->bio_error = EIO;
|
||||
bp->bio_flags |= BIO_ERROR;
|
||||
bp->bio_resid = bp->bio_bcount;
|
||||
|
||||
biodone(bp);
|
||||
}
|
||||
mtx_unlock(&chip->qlock);
|
||||
|
||||
mtx_destroy(&chip->qlock);
|
||||
}
|
60
sys/dev/nand/nand_id.c
Normal file
60
sys/dev/nand/nand_id.c
Normal file
@ -0,0 +1,60 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
|
||||
struct nand_params nand_ids[] = {
|
||||
{ { NAND_MAN_SAMSUNG, 0x75 }, "Samsung K9F5608U0B",
|
||||
0x20, 0x200, 0x10, 0x20, 0 },
|
||||
{ { NAND_MAN_SAMSUNG, 0xd3 }, "Samsung NAND 1GiB 3,3V 8-bit",
|
||||
0x400, 0x800, 0x40, 0x40, 0 },
|
||||
{ { NAND_MAN_HYNIX, 0x76 }, "Hynix NAND 64MiB 3,3V 8-bit",
|
||||
0x40, 0x200, 0x10, 0x20, 0 },
|
||||
{ { NAND_MAN_HYNIX, 0xdc }, "Hynix NAND 512MiB 3,3V 8-bit",
|
||||
0x200, 0x800, 0x40, 0x40, 0 },
|
||||
{ { NAND_MAN_HYNIX, 0x79 }, "NAND 128MB 3,3V 8-bit",
|
||||
0x80, 0x200, 0x10, 0x20, 0 },
|
||||
{ { NAND_MAN_STMICRO, 0xf1 }, "STMicro 128MB 3,3V 8-bit",
|
||||
0x80, 2048, 64, 0x40, 0 },
|
||||
};
|
||||
|
||||
struct nand_params *nand_get_params(struct nand_id *id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(nand_ids) / sizeof(nand_ids[0]); i++)
|
||||
if (nand_ids[i].id.man_id == id->man_id &&
|
||||
nand_ids[i].id.dev_id == id->dev_id)
|
||||
return (&nand_ids[i]);
|
||||
|
||||
return (NULL);
|
||||
}
|
168
sys/dev/nand/nand_if.m
Normal file
168
sys/dev/nand/nand_if.m
Normal file
@ -0,0 +1,168 @@
|
||||
#-
|
||||
# Copyright (C) 2009-2012 Semihalf
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
# NAND chip interface description
|
||||
#
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <dev/nand/nand.h>
|
||||
|
||||
INTERFACE nand;
|
||||
|
||||
CODE {
|
||||
static int nand_method_not_supported(device_t dev)
|
||||
{
|
||||
return (ENOENT);
|
||||
}
|
||||
};
|
||||
|
||||
# Read NAND page
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int read_page {
|
||||
device_t dev;
|
||||
uint32_t page;
|
||||
void* buf;
|
||||
uint32_t len;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
# Program NAND page
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int program_page {
|
||||
device_t dev;
|
||||
uint32_t page;
|
||||
void* buf;
|
||||
uint32_t len;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
# Program NAND page interleaved
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int program_page_intlv {
|
||||
device_t dev;
|
||||
uint32_t page;
|
||||
void* buf;
|
||||
uint32_t len;
|
||||
uint32_t offset;
|
||||
} DEFAULT nand_method_not_supported;
|
||||
|
||||
# Read NAND oob
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int read_oob {
|
||||
device_t dev;
|
||||
uint32_t page;
|
||||
void* buf;
|
||||
uint32_t len;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
# Program NAND oob
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int program_oob {
|
||||
device_t dev;
|
||||
uint32_t page;
|
||||
void* buf;
|
||||
uint32_t len;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
# Erase NAND block
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int erase_block {
|
||||
device_t dev;
|
||||
uint32_t block;
|
||||
};
|
||||
|
||||
# Erase NAND block interleaved
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int erase_block_intlv {
|
||||
device_t dev;
|
||||
uint32_t block;
|
||||
} DEFAULT nand_method_not_supported;
|
||||
|
||||
# NAND get status
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int get_status {
|
||||
device_t dev;
|
||||
uint8_t *status;
|
||||
};
|
||||
|
||||
# NAND check if block is bad
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int is_blk_bad {
|
||||
device_t dev;
|
||||
uint32_t block_number;
|
||||
uint8_t *bad;
|
||||
};
|
||||
|
||||
# NAND get ECC
|
||||
#
|
||||
#
|
||||
METHOD int get_ecc {
|
||||
device_t dev;
|
||||
void *buf;
|
||||
void *ecc;
|
||||
int *needwrite;
|
||||
};
|
||||
|
||||
# NAND correct ECC
|
||||
#
|
||||
#
|
||||
METHOD int correct_ecc {
|
||||
device_t dev;
|
||||
void *buf;
|
||||
void *readecc;
|
||||
void *calcecc;
|
||||
};
|
||||
|
530
sys/dev/nand/nandbus.c
Normal file
530
sys/dev/nand/nandbus.c
Normal file
@ -0,0 +1,530 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/condvar.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandbus.h>
|
||||
#include "nand_if.h"
|
||||
#include "nandbus_if.h"
|
||||
#include "nfc_if.h"
|
||||
|
||||
#define NAND_NCS 4
|
||||
|
||||
static int nandbus_probe(device_t dev);
|
||||
static int nandbus_attach(device_t dev);
|
||||
static int nandbus_detach(device_t dev);
|
||||
|
||||
static int nandbus_child_location_str(device_t, device_t, char *, size_t);
|
||||
static int nandbus_child_pnpinfo_str(device_t, device_t, char *, size_t);
|
||||
|
||||
static int nandbus_get_status(device_t, uint8_t *);
|
||||
static void nandbus_read_buffer(device_t, void *, uint32_t);
|
||||
static int nandbus_select_cs(device_t, uint8_t);
|
||||
static int nandbus_send_command(device_t, uint8_t);
|
||||
static int nandbus_send_address(device_t, uint8_t);
|
||||
static int nandbus_start_command(device_t);
|
||||
static int nandbus_wait_ready(device_t, uint8_t *);
|
||||
static void nandbus_write_buffer(device_t, void *, uint32_t);
|
||||
static int nandbus_get_ecc(device_t, void *, uint32_t, void *, int *);
|
||||
static int nandbus_correct_ecc(device_t, void *, int, void *, void *);
|
||||
static void nandbus_lock(device_t);
|
||||
static void nandbus_unlock(device_t);
|
||||
|
||||
static int nand_readid(device_t, uint8_t *, uint8_t *);
|
||||
static int nand_probe_onfi(device_t, uint8_t *);
|
||||
static int nand_reset(device_t);
|
||||
|
||||
struct nandbus_softc {
|
||||
device_t dev;
|
||||
struct cv nandbus_cv;
|
||||
struct mtx nandbus_mtx;
|
||||
uint8_t busy;
|
||||
};
|
||||
|
||||
static device_method_t nandbus_methods[] = {
|
||||
/* device interface */
|
||||
DEVMETHOD(device_probe, nandbus_probe),
|
||||
DEVMETHOD(device_attach, nandbus_attach),
|
||||
DEVMETHOD(device_detach, nandbus_detach),
|
||||
DEVMETHOD(device_shutdown, bus_generic_shutdown),
|
||||
|
||||
/* bus interface */
|
||||
DEVMETHOD(bus_print_child, bus_generic_print_child),
|
||||
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
|
||||
DEVMETHOD(bus_child_pnpinfo_str, nandbus_child_pnpinfo_str),
|
||||
DEVMETHOD(bus_child_location_str, nandbus_child_location_str),
|
||||
|
||||
/* nandbus interface */
|
||||
DEVMETHOD(nandbus_get_status, nandbus_get_status),
|
||||
DEVMETHOD(nandbus_read_buffer, nandbus_read_buffer),
|
||||
DEVMETHOD(nandbus_select_cs, nandbus_select_cs),
|
||||
DEVMETHOD(nandbus_send_command, nandbus_send_command),
|
||||
DEVMETHOD(nandbus_send_address, nandbus_send_address),
|
||||
DEVMETHOD(nandbus_start_command,nandbus_start_command),
|
||||
DEVMETHOD(nandbus_wait_ready, nandbus_wait_ready),
|
||||
DEVMETHOD(nandbus_write_buffer, nandbus_write_buffer),
|
||||
DEVMETHOD(nandbus_get_ecc, nandbus_get_ecc),
|
||||
DEVMETHOD(nandbus_correct_ecc, nandbus_correct_ecc),
|
||||
DEVMETHOD(nandbus_lock, nandbus_lock),
|
||||
DEVMETHOD(nandbus_unlock, nandbus_unlock),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
devclass_t nandbus_devclass;
|
||||
|
||||
driver_t nandbus_driver = {
|
||||
"nandbus",
|
||||
nandbus_methods,
|
||||
sizeof(struct nandbus_softc)
|
||||
};
|
||||
|
||||
DRIVER_MODULE(nandbus, nand, nandbus_driver, nandbus_devclass, 0, 0);
|
||||
|
||||
int
|
||||
nandbus_create(device_t nfc)
|
||||
{
|
||||
device_t child;
|
||||
|
||||
child = device_add_child(nfc, "nandbus", -1);
|
||||
if (!child)
|
||||
return (ENODEV);
|
||||
|
||||
bus_generic_attach(nfc);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
void
|
||||
nandbus_destroy(device_t nfc)
|
||||
{
|
||||
device_t *children;
|
||||
int nchildren, i;
|
||||
|
||||
mtx_lock(&Giant);
|
||||
/* Detach & delete all children */
|
||||
if (!device_get_children(nfc, &children, &nchildren)) {
|
||||
for (i = 0; i < nchildren; i++)
|
||||
device_delete_child(nfc, children[i]);
|
||||
|
||||
free(children, M_TEMP);
|
||||
}
|
||||
mtx_unlock(&Giant);
|
||||
}
|
||||
|
||||
static int
|
||||
nandbus_probe(device_t dev)
|
||||
{
|
||||
|
||||
device_set_desc(dev, "NAND bus");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandbus_attach(device_t dev)
|
||||
{
|
||||
device_t child, nfc;
|
||||
struct nand_id chip_id;
|
||||
struct nandbus_softc *sc;
|
||||
struct nandbus_ivar *ivar;
|
||||
struct nand_softc *nfc_sc;
|
||||
struct nand_params *chip_params;
|
||||
uint8_t cs, onfi;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
|
||||
nfc = device_get_parent(dev);
|
||||
nfc_sc = device_get_softc(nfc);
|
||||
|
||||
mtx_init(&sc->nandbus_mtx, "nandbus lock", MTX_DEF, 0);
|
||||
cv_init(&sc->nandbus_cv, "nandbus cv");
|
||||
|
||||
/* Check each possible CS for existing nand devices */
|
||||
for (cs = 0; cs < NAND_NCS; cs++) {
|
||||
nand_debug(NDBG_BUS,"probe chip select %x", cs);
|
||||
|
||||
/* Select & reset chip */
|
||||
if (nandbus_select_cs(dev, cs))
|
||||
break;
|
||||
|
||||
if (nand_reset(dev))
|
||||
continue;
|
||||
|
||||
/* Read manufacturer and device id */
|
||||
if (nand_readid(dev, &chip_id.man_id, &chip_id.dev_id))
|
||||
continue;
|
||||
|
||||
if (chip_id.man_id == 0xff)
|
||||
continue;
|
||||
|
||||
/* Check if chip is ONFI compliant */
|
||||
if (nand_probe_onfi(dev, &onfi) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ivar = malloc(sizeof(struct nandbus_ivar),
|
||||
M_NAND, M_WAITOK);
|
||||
|
||||
if (onfi == 1) {
|
||||
ivar->cs = cs;
|
||||
ivar->cols = 0;
|
||||
ivar->rows = 0;
|
||||
ivar->params = NULL;
|
||||
ivar->man_id = chip_id.man_id;
|
||||
ivar->dev_id = chip_id.dev_id;
|
||||
ivar->is_onfi = onfi;
|
||||
ivar->chip_cdev_name = nfc_sc->chip_cdev_name;
|
||||
|
||||
child = device_add_child(dev, NULL, -1);
|
||||
device_set_ivars(child, ivar);
|
||||
continue;
|
||||
}
|
||||
|
||||
chip_params = nand_get_params(&chip_id);
|
||||
if (chip_params == NULL) {
|
||||
nand_debug(NDBG_BUS,"Chip description not found! "
|
||||
"(manuf: 0x%0x, chipid: 0x%0x)\n",
|
||||
chip_id.man_id, chip_id.dev_id);
|
||||
free(ivar, M_NAND);
|
||||
continue;
|
||||
}
|
||||
|
||||
ivar->cs = cs;
|
||||
ivar->cols = 1;
|
||||
ivar->rows = 2;
|
||||
ivar->params = chip_params;
|
||||
ivar->man_id = chip_id.man_id;
|
||||
ivar->dev_id = chip_id.dev_id;
|
||||
ivar->is_onfi = onfi;
|
||||
ivar->chip_cdev_name = nfc_sc->chip_cdev_name;
|
||||
|
||||
/*
|
||||
* Check what type of device we have.
|
||||
* devices bigger than 32MiB have on more row (3)
|
||||
*/
|
||||
if (chip_params->chip_size > 32)
|
||||
ivar->rows++;
|
||||
/* Large page devices have one more col (2) */
|
||||
if (chip_params->chip_size >= 128 &&
|
||||
chip_params->page_size > 512)
|
||||
ivar->cols++;
|
||||
|
||||
child = device_add_child(dev, NULL, -1);
|
||||
device_set_ivars(child, ivar);
|
||||
}
|
||||
|
||||
bus_generic_attach(dev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandbus_detach(device_t dev)
|
||||
{
|
||||
struct nandbus_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
bus_generic_detach(dev);
|
||||
|
||||
mtx_destroy(&sc->nandbus_mtx);
|
||||
cv_destroy(&sc->nandbus_cv);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandbus_child_location_str(device_t bus, device_t child, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
struct nandbus_ivar *ivar = device_get_ivars(child);
|
||||
|
||||
snprintf(buf, buflen, "at cs#%d", ivar->cs);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
// XXX man id, model id ????
|
||||
*buf = '\0';
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_readid(device_t bus, uint8_t *man_id, uint8_t *dev_id)
|
||||
{
|
||||
device_t nfc;
|
||||
|
||||
if (!bus || !man_id || !dev_id)
|
||||
return (EINVAL);
|
||||
|
||||
nand_debug(NDBG_BUS,"read id");
|
||||
|
||||
nfc = device_get_parent(bus);
|
||||
|
||||
if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) {
|
||||
nand_debug(NDBG_BUS,"Error : could not send READ ID command");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (NFC_SEND_ADDRESS(nfc, 0)) {
|
||||
nand_debug(NDBG_BUS,"Error : could not sent address to chip");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (NFC_START_COMMAND(nfc) != 0) {
|
||||
nand_debug(NDBG_BUS,"Error : could not start command");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
DELAY(25);
|
||||
|
||||
*man_id = NFC_READ_BYTE(nfc);
|
||||
*dev_id = NFC_READ_BYTE(nfc);
|
||||
|
||||
nand_debug(NDBG_BUS,"manufacturer id: %x chip id: %x", *man_id,
|
||||
*dev_id);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_probe_onfi(device_t bus, uint8_t *onfi_compliant)
|
||||
{
|
||||
device_t nfc;
|
||||
char onfi_id[] = {'o', 'n', 'f', 'i', '\0'};
|
||||
int i;
|
||||
|
||||
nand_debug(NDBG_BUS,"probing ONFI");
|
||||
|
||||
nfc = device_get_parent(bus);
|
||||
|
||||
if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) {
|
||||
nand_debug(NDBG_BUS,"Error : could not sent READ ID command");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (NFC_SEND_ADDRESS(nfc, ONFI_SIG_ADDR)) {
|
||||
nand_debug(NDBG_BUS,"Error : could not sent address to chip");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (NFC_START_COMMAND(nfc) != 0) {
|
||||
nand_debug(NDBG_BUS,"Error : could not start command");
|
||||
return (ENXIO);
|
||||
}
|
||||
for (i = 0; onfi_id[i] != '\0'; i++)
|
||||
if (NFC_READ_BYTE(nfc) != onfi_id[i]) {
|
||||
nand_debug(NDBG_BUS,"ONFI non-compliant");
|
||||
*onfi_compliant = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
nand_debug(NDBG_BUS,"ONFI compliant");
|
||||
*onfi_compliant = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nand_reset(device_t bus)
|
||||
{
|
||||
device_t nfc;
|
||||
nand_debug(NDBG_BUS,"resetting...");
|
||||
|
||||
nfc = device_get_parent(bus);
|
||||
|
||||
if (NFC_SEND_COMMAND(nfc, NAND_CMD_RESET) != 0) {
|
||||
nand_debug(NDBG_BUS,"Error : could not sent RESET command");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (NFC_START_COMMAND(nfc) != 0) {
|
||||
nand_debug(NDBG_BUS,"Error : could not start RESET command");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
DELAY(1000);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nandbus_lock(device_t dev)
|
||||
{
|
||||
struct nandbus_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->nandbus_mtx);
|
||||
if (sc->busy)
|
||||
cv_wait(&sc->nandbus_cv, &sc->nandbus_mtx);
|
||||
sc->busy = 1;
|
||||
mtx_unlock(&sc->nandbus_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
nandbus_unlock(device_t dev)
|
||||
{
|
||||
struct nandbus_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->nandbus_mtx);
|
||||
sc->busy = 0;
|
||||
cv_signal(&sc->nandbus_cv);
|
||||
mtx_unlock(&sc->nandbus_mtx);
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_select_cs(device_t dev, uint8_t cs)
|
||||
{
|
||||
|
||||
return (NFC_SELECT_CS(device_get_parent(dev), cs));
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_send_command(device_t dev, uint8_t command)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = NFC_SEND_COMMAND(device_get_parent(dev), command)))
|
||||
nand_debug(NDBG_BUS,"Err: Could not send command %x, err %x",
|
||||
command, err);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_send_address(device_t dev, uint8_t address)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = NFC_SEND_ADDRESS(device_get_parent(dev), address)))
|
||||
nand_debug(NDBG_BUS,"Err: Could not send address %x, err %x",
|
||||
address, err);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_start_command(device_t dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = NFC_START_COMMAND(device_get_parent(dev))))
|
||||
nand_debug(NDBG_BUS,"Err: Could not start command, err %x",
|
||||
err);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
nandbus_read_buffer(device_t dev, void *buf, uint32_t len)
|
||||
{
|
||||
|
||||
NFC_READ_BUF(device_get_parent(dev), buf, len);
|
||||
}
|
||||
|
||||
void
|
||||
nandbus_write_buffer(device_t dev, void *buf, uint32_t len)
|
||||
{
|
||||
|
||||
NFC_WRITE_BUF(device_get_parent(dev), buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_get_status(device_t dev, uint8_t *status)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = NANDBUS_SEND_COMMAND(dev, NAND_CMD_STATUS)))
|
||||
return (err);
|
||||
if ((err = NANDBUS_START_COMMAND(dev)))
|
||||
return (err);
|
||||
|
||||
*status = NFC_READ_BYTE(device_get_parent(dev));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_wait_ready(device_t dev, uint8_t *status)
|
||||
{
|
||||
struct timeval tv, tv2;
|
||||
|
||||
tv2.tv_sec = 0;
|
||||
tv2.tv_usec = 50 * 5000; /* 10ms */
|
||||
|
||||
getmicrotime(&tv);
|
||||
timevaladd(&tv, &tv2);
|
||||
|
||||
do {
|
||||
if (NANDBUS_GET_STATUS(dev, status))
|
||||
return (ENXIO);
|
||||
|
||||
if (*status & NAND_STATUS_RDY)
|
||||
return (0);
|
||||
|
||||
getmicrotime(&tv2);
|
||||
} while (timevalcmp(&tv2, &tv, <=));
|
||||
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_get_ecc(device_t dev, void *buf, uint32_t pagesize, void *ecc,
|
||||
int *needwrite)
|
||||
{
|
||||
|
||||
return (NFC_GET_ECC(device_get_parent(dev), buf, pagesize, ecc, needwrite));
|
||||
}
|
||||
|
||||
int
|
||||
nandbus_correct_ecc(device_t dev, void *buf, int pagesize, void *readecc,
|
||||
void *calcecc)
|
||||
{
|
||||
|
||||
return (NFC_CORRECT_ECC(device_get_parent(dev), buf, pagesize,
|
||||
readecc, calcecc));
|
||||
}
|
||||
|
49
sys/dev/nand/nandbus.h
Normal file
49
sys/dev/nand/nandbus.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NANDBUS_H_
|
||||
#define _NANDBUS_H_
|
||||
|
||||
struct nandbus_ivar {
|
||||
uint8_t cs;
|
||||
uint8_t cols;
|
||||
uint8_t rows;
|
||||
uint8_t man_id;
|
||||
uint8_t dev_id;
|
||||
uint8_t is_onfi;
|
||||
char *chip_cdev_name;
|
||||
struct nand_params *params;
|
||||
};
|
||||
|
||||
extern devclass_t nandbus_devclass;
|
||||
extern driver_t nandbus_driver;
|
||||
|
||||
int nandbus_create(device_t nfc);
|
||||
void nandbus_destroy(device_t nfc);
|
||||
|
||||
#endif /* _NANDBUS_H_ */
|
100
sys/dev/nand/nandbus_if.m
Normal file
100
sys/dev/nand/nandbus_if.m
Normal file
@ -0,0 +1,100 @@
|
||||
#-
|
||||
# Copyright (C) 2009-2012 Semihalf
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
# NAND bus interface description
|
||||
#
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <dev/nand/nand.h>
|
||||
|
||||
INTERFACE nandbus;
|
||||
|
||||
METHOD int get_status {
|
||||
device_t dev;
|
||||
uint8_t * status;
|
||||
};
|
||||
|
||||
METHOD void read_buffer {
|
||||
device_t dev;
|
||||
void * buf;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
METHOD int select_cs {
|
||||
device_t dev;
|
||||
uint8_t cs;
|
||||
};
|
||||
|
||||
METHOD int send_command {
|
||||
device_t dev;
|
||||
uint8_t command;
|
||||
};
|
||||
|
||||
METHOD int send_address {
|
||||
device_t dev;
|
||||
uint8_t address;
|
||||
};
|
||||
|
||||
METHOD int start_command {
|
||||
device_t dev;
|
||||
};
|
||||
|
||||
METHOD int wait_ready {
|
||||
device_t dev;
|
||||
uint8_t * status;
|
||||
}
|
||||
|
||||
METHOD void write_buffer {
|
||||
device_t dev;
|
||||
void * buf;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
METHOD int get_ecc {
|
||||
device_t dev;
|
||||
void * buf;
|
||||
uint32_t pagesize;
|
||||
void * ecc;
|
||||
int * needwrite;
|
||||
};
|
||||
|
||||
METHOD int correct_ecc {
|
||||
device_t dev;
|
||||
void * buf;
|
||||
int pagesize;
|
||||
void * readecc;
|
||||
void * calcecc;
|
||||
};
|
||||
|
||||
METHOD void lock {
|
||||
device_t dev;
|
||||
};
|
||||
|
||||
METHOD void unlock {
|
||||
device_t dev;
|
||||
};
|
||||
|
665
sys/dev/nand/nandsim.c
Normal file
665
sys/dev/nand/nandsim.c
Normal file
@ -0,0 +1,665 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Simulated NAND controller driver */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/malloc.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandsim.h>
|
||||
#include <dev/nand/nandsim_chip.h>
|
||||
#include <dev/nand/nandsim_log.h>
|
||||
#include <dev/nand/nandsim_swap.h>
|
||||
|
||||
struct sim_param sim;
|
||||
struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
|
||||
|
||||
static struct cdev *nandsim_dev;
|
||||
static d_ioctl_t nandsim_ioctl;
|
||||
|
||||
static void nandsim_init_sim_param(struct sim_param *);
|
||||
static int nandsim_create_ctrl(struct sim_ctrl *);
|
||||
static int nandsim_destroy_ctrl(int);
|
||||
static int nandsim_ctrl_status(struct sim_ctrl *);
|
||||
static int nandsim_create_chip(struct sim_chip *);
|
||||
static int nandsim_destroy_chip(struct sim_ctrl_chip *);
|
||||
static int nandsim_chip_status(struct sim_chip *);
|
||||
static int nandsim_start_ctrl(int);
|
||||
static int nandsim_stop_ctrl(int);
|
||||
static int nandsim_inject_error(struct sim_error *);
|
||||
static int nandsim_get_block_state(struct sim_block_state *);
|
||||
static int nandsim_set_block_state(struct sim_block_state *);
|
||||
static int nandsim_modify(struct sim_mod *);
|
||||
static int nandsim_dump(struct sim_dump *);
|
||||
static int nandsim_restore(struct sim_dump *);
|
||||
static int nandsim_freeze(struct sim_ctrl_chip *);
|
||||
static void nandsim_print_log(struct sim_log *);
|
||||
static struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t);
|
||||
|
||||
static struct cdevsw nandsim_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_ioctl = nandsim_ioctl,
|
||||
.d_name = "nandsim",
|
||||
};
|
||||
|
||||
int
|
||||
nandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
|
||||
int flags, struct thread *td)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case NANDSIM_SIM_PARAM:
|
||||
nandsim_init_sim_param((struct sim_param *)data);
|
||||
break;
|
||||
case NANDSIM_CREATE_CTRL:
|
||||
ret = nandsim_create_ctrl((struct sim_ctrl *)data);
|
||||
break;
|
||||
case NANDSIM_DESTROY_CTRL:
|
||||
ret = nandsim_destroy_ctrl(*(int *)data);
|
||||
break;
|
||||
case NANDSIM_STATUS_CTRL:
|
||||
ret = nandsim_ctrl_status((struct sim_ctrl *)data);
|
||||
break;
|
||||
case NANDSIM_CREATE_CHIP:
|
||||
ret = nandsim_create_chip((struct sim_chip *)data);
|
||||
break;
|
||||
case NANDSIM_DESTROY_CHIP:
|
||||
ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data);
|
||||
break;
|
||||
case NANDSIM_STATUS_CHIP:
|
||||
ret = nandsim_chip_status((struct sim_chip *)data);
|
||||
break;
|
||||
case NANDSIM_MODIFY:
|
||||
ret = nandsim_modify((struct sim_mod *)data);
|
||||
break;
|
||||
case NANDSIM_START_CTRL:
|
||||
ret = nandsim_start_ctrl(*(int *)data);
|
||||
break;
|
||||
case NANDSIM_STOP_CTRL:
|
||||
ret = nandsim_stop_ctrl(*(int *)data);
|
||||
break;
|
||||
case NANDSIM_INJECT_ERROR:
|
||||
ret = nandsim_inject_error((struct sim_error *)data);
|
||||
break;
|
||||
case NANDSIM_SET_BLOCK_STATE:
|
||||
ret = nandsim_set_block_state((struct sim_block_state *)data);
|
||||
break;
|
||||
case NANDSIM_GET_BLOCK_STATE:
|
||||
ret = nandsim_get_block_state((struct sim_block_state *)data);
|
||||
break;
|
||||
case NANDSIM_PRINT_LOG:
|
||||
nandsim_print_log((struct sim_log *)data);
|
||||
break;
|
||||
case NANDSIM_DUMP:
|
||||
ret = nandsim_dump((struct sim_dump *)data);
|
||||
break;
|
||||
case NANDSIM_RESTORE:
|
||||
ret = nandsim_restore((struct sim_dump *)data);
|
||||
break;
|
||||
case NANDSIM_FREEZE:
|
||||
ret = nandsim_freeze((struct sim_ctrl_chip *)data);
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_init_sim_param(struct sim_param *param)
|
||||
{
|
||||
|
||||
if (!param)
|
||||
return;
|
||||
|
||||
nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level,
|
||||
param->log_output);
|
||||
nandsim_log_level = param->log_level;
|
||||
nandsim_log_output = param->log_output;
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_create_ctrl(struct sim_ctrl *ctrl)
|
||||
{
|
||||
struct sim_ctrl_conf *sim_ctrl;
|
||||
|
||||
nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num,
|
||||
ctrl->num_cs);
|
||||
|
||||
if (ctrl->num >= MAX_SIM_DEV) {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
sim_ctrl = &ctrls[ctrl->num];
|
||||
if(sim_ctrl->created)
|
||||
return (EEXIST);
|
||||
|
||||
sim_ctrl->num = ctrl->num;
|
||||
sim_ctrl->num_cs = ctrl->num_cs;
|
||||
sim_ctrl->ecc = ctrl->ecc;
|
||||
memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout,
|
||||
MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
|
||||
strlcpy(sim_ctrl->filename, ctrl->filename,
|
||||
FILENAME_SIZE);
|
||||
sim_ctrl->created = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_destroy_ctrl(int ctrl_num)
|
||||
{
|
||||
|
||||
nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num);
|
||||
|
||||
if (ctrl_num >= MAX_SIM_DEV) {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (!ctrls[ctrl_num].created) {
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
if (ctrls[ctrl_num].running) {
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num]));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_ctrl_status(struct sim_ctrl *ctrl)
|
||||
{
|
||||
|
||||
nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num,
|
||||
ctrl->num_cs);
|
||||
|
||||
if (ctrl->num >= MAX_SIM_DEV) {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
ctrl->num_cs = ctrls[ctrl->num].num_cs;
|
||||
ctrl->ecc = ctrls[ctrl->num].ecc;
|
||||
memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout,
|
||||
MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
|
||||
strlcpy(ctrl->filename, ctrls[ctrl->num].filename,
|
||||
FILENAME_SIZE);
|
||||
ctrl->running = ctrls[ctrl->num].running;
|
||||
ctrl->created = ctrls[ctrl->num].created;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_create_chip(struct sim_chip *chip)
|
||||
{
|
||||
struct sim_chip *sim_chip;
|
||||
|
||||
nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num,
|
||||
chip->ctrl_num);
|
||||
|
||||
if (chip->ctrl_num >= MAX_SIM_DEV ||
|
||||
chip->num >= MAX_CTRL_CS) {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (ctrls[chip->ctrl_num].chips[chip->num]) {
|
||||
return (EEXIST);
|
||||
}
|
||||
|
||||
sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM,
|
||||
M_WAITOK);
|
||||
if (sim_chip == NULL) {
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
memcpy(sim_chip, chip, sizeof(*sim_chip));
|
||||
ctrls[chip->ctrl_num].chips[chip->num] = sim_chip;
|
||||
sim_chip->created = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_destroy_chip(struct sim_ctrl_chip *chip)
|
||||
{
|
||||
struct sim_ctrl_conf *ctrl_conf;
|
||||
|
||||
nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num,
|
||||
chip->ctrl_num);
|
||||
|
||||
if (chip->ctrl_num >= MAX_SIM_DEV ||
|
||||
chip->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
ctrl_conf = &ctrls[chip->ctrl_num];
|
||||
|
||||
if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num])
|
||||
return (ENODEV);
|
||||
|
||||
if (ctrl_conf->running)
|
||||
return (EBUSY);
|
||||
|
||||
free(ctrl_conf->chips[chip->chip_num], M_NANDSIM);
|
||||
ctrl_conf->chips[chip->chip_num] = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_chip_status(struct sim_chip *chip)
|
||||
{
|
||||
struct sim_ctrl_conf *ctrl_conf;
|
||||
|
||||
nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num,
|
||||
chip->ctrl_num);
|
||||
|
||||
if (chip->ctrl_num >= MAX_SIM_DEV &&
|
||||
chip->num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
ctrl_conf = &ctrls[chip->ctrl_num];
|
||||
if (!ctrl_conf->chips[chip->num])
|
||||
chip->created = 0;
|
||||
else
|
||||
memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_start_ctrl(int num)
|
||||
{
|
||||
device_t nexus, ndev;
|
||||
devclass_t nexus_devclass;
|
||||
int ret = 0;
|
||||
|
||||
nand_debug(NDBG_SIM,"start ctlr num:%d", num);
|
||||
|
||||
if (num >= MAX_SIM_DEV)
|
||||
return (EINVAL);
|
||||
|
||||
if (!ctrls[num].created)
|
||||
return (ENODEV);
|
||||
|
||||
if (ctrls[num].running)
|
||||
return (EBUSY);
|
||||
|
||||
/* We will add our device as a child of the nexus0 device */
|
||||
if (!(nexus_devclass = devclass_find("nexus")) ||
|
||||
!(nexus = devclass_get_device(nexus_devclass, 0)))
|
||||
return (EFAULT);
|
||||
|
||||
/*
|
||||
* Create a newbus device representing this frontend instance
|
||||
*
|
||||
* XXX powerpc nexus doesn't implement bus_add_child, so child
|
||||
* must be added by device_add_child().
|
||||
*/
|
||||
#if defined(__powerpc__)
|
||||
ndev = device_add_child(nexus, "nandsim", num);
|
||||
#else
|
||||
ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num);
|
||||
#endif
|
||||
if (!ndev)
|
||||
return (EFAULT);
|
||||
|
||||
mtx_lock(&Giant);
|
||||
ret = device_probe_and_attach(ndev);
|
||||
mtx_unlock(&Giant);
|
||||
|
||||
if (ret == 0) {
|
||||
ctrls[num].sim_ctrl_dev = ndev;
|
||||
ctrls[num].running = 1;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_stop_ctrl(int num)
|
||||
{
|
||||
device_t nexus;
|
||||
devclass_t nexus_devclass;
|
||||
int ret = 0;
|
||||
|
||||
nand_debug(NDBG_SIM,"stop controller num:%d", num);
|
||||
|
||||
if (num >= MAX_SIM_DEV) {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (!ctrls[num].created || !ctrls[num].running) {
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
/* We will add our device as a child of the nexus0 device */
|
||||
if (!(nexus_devclass = devclass_find("nexus")) ||
|
||||
!(nexus = devclass_get_device(nexus_devclass, 0))) {
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
mtx_lock(&Giant);
|
||||
if (ctrls[num].sim_ctrl_dev) {
|
||||
ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev);
|
||||
ctrls[num].sim_ctrl_dev = NULL;
|
||||
}
|
||||
mtx_unlock(&Giant);
|
||||
|
||||
ctrls[num].running = 0;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static struct nandsim_chip *
|
||||
get_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
|
||||
if (!ctrls[ctrl_num].sim_ctrl_dev)
|
||||
return (NULL);
|
||||
|
||||
sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev);
|
||||
return (sc->chips[chip_num]);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_print_log(struct sim_log *sim_log)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
int len1, len2;
|
||||
|
||||
if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev)
|
||||
return;
|
||||
|
||||
sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev);
|
||||
if (sc->log_buff) {
|
||||
len1 = strlen(&sc->log_buff[sc->log_idx + 1]);
|
||||
if (len1 >= sim_log->len)
|
||||
len1 = sim_log->len;
|
||||
copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1);
|
||||
len2 = strlen(sc->log_buff);
|
||||
if (len2 >= (sim_log->len - len1))
|
||||
len2 = (sim_log->len - len1);
|
||||
copyout(sc->log_buff, &sim_log->log[len1], len2);
|
||||
sim_log->len = len1 + len2;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_inject_error(struct sim_error *error)
|
||||
{
|
||||
struct nandsim_chip *chip;
|
||||
struct block_space *bs;
|
||||
struct onfi_params *param;
|
||||
int page, page_size, block, offset;
|
||||
|
||||
nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n",
|
||||
error->chip_num, error->ctrl_num);
|
||||
|
||||
if (error->ctrl_num >= MAX_SIM_DEV ||
|
||||
error->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running)
|
||||
return (ENODEV);
|
||||
|
||||
chip = get_nandsim_chip(error->ctrl_num, error->chip_num);
|
||||
param = &chip->params;
|
||||
page_size = param->bytes_per_page + param->spare_bytes_per_page;
|
||||
block = error->page_num / param->pages_per_block;
|
||||
page = error->page_num % param->pages_per_block;
|
||||
|
||||
bs = get_bs(chip->swap, block, 1);
|
||||
if (!bs)
|
||||
return (EINVAL);
|
||||
|
||||
offset = (page * page_size) + error->column;
|
||||
memset(&bs->blk_ptr[offset], error->pattern, error->len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_set_block_state(struct sim_block_state *bs)
|
||||
{
|
||||
struct onfi_params *params;
|
||||
struct nandsim_chip *chip;
|
||||
int blocks;
|
||||
|
||||
nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n",
|
||||
bs->chip_num, bs->ctrl_num, bs->block_num);
|
||||
|
||||
if (bs->ctrl_num >= MAX_SIM_DEV ||
|
||||
bs->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
|
||||
params = &chip->params;
|
||||
blocks = params->luns * params->blocks_per_lun;
|
||||
|
||||
if (bs->block_num > blocks)
|
||||
return (EINVAL);
|
||||
|
||||
chip->blk_state[bs->block_num].is_bad = bs->state;
|
||||
|
||||
if (bs->wearout >= 0)
|
||||
chip->blk_state[bs->block_num].wear_lev = bs->wearout;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_get_block_state(struct sim_block_state *bs)
|
||||
{
|
||||
struct onfi_params *params;
|
||||
struct nandsim_chip *chip;
|
||||
int blocks;
|
||||
|
||||
if (bs->ctrl_num >= MAX_SIM_DEV ||
|
||||
bs->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n",
|
||||
bs->chip_num, bs->ctrl_num, bs->block_num);
|
||||
|
||||
chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
|
||||
params = &chip->params;
|
||||
blocks = params->luns * params->blocks_per_lun;
|
||||
|
||||
if (bs->block_num > blocks)
|
||||
return (EINVAL);
|
||||
|
||||
bs->state = chip->blk_state[bs->block_num].is_bad;
|
||||
bs->wearout = chip->blk_state[bs->block_num].wear_lev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_dump(struct sim_dump *dump)
|
||||
{
|
||||
struct nandsim_chip *chip;
|
||||
struct block_space *bs;
|
||||
int blk_size;
|
||||
|
||||
nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num);
|
||||
|
||||
if (dump->ctrl_num >= MAX_SIM_DEV ||
|
||||
dump->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
|
||||
blk_size = chip->cg.block_size +
|
||||
(chip->cg.oob_size * chip->cg.pgs_per_blk);
|
||||
|
||||
bs = get_bs(chip->swap, dump->block_num, 0);
|
||||
if (!bs)
|
||||
return (EINVAL);
|
||||
|
||||
if (dump->len > blk_size)
|
||||
dump->len = blk_size;
|
||||
|
||||
copyout(bs->blk_ptr, dump->data, dump->len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_restore(struct sim_dump *dump)
|
||||
{
|
||||
struct nandsim_chip *chip;
|
||||
struct block_space *bs;
|
||||
int blk_size;
|
||||
|
||||
nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num,
|
||||
dump->chip_num);
|
||||
|
||||
if (dump->ctrl_num >= MAX_SIM_DEV ||
|
||||
dump->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
|
||||
blk_size = chip->cg.block_size +
|
||||
(chip->cg.oob_size * chip->cg.pgs_per_blk);
|
||||
|
||||
bs = get_bs(chip->swap, dump->block_num, 1);
|
||||
if (!bs)
|
||||
return (EINVAL);
|
||||
|
||||
if (dump->len > blk_size)
|
||||
dump->len = blk_size;
|
||||
|
||||
|
||||
copyin(dump->data, bs->blk_ptr, dump->len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_freeze(struct sim_ctrl_chip *ctrl_chip)
|
||||
{
|
||||
struct nandsim_chip *chip;
|
||||
|
||||
if (ctrl_chip->ctrl_num >= MAX_SIM_DEV ||
|
||||
ctrl_chip->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num);
|
||||
nandsim_chip_freeze(chip);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_modify(struct sim_mod *mod)
|
||||
{
|
||||
struct sim_chip *sim_conf = NULL;
|
||||
struct nandsim_chip *sim_chip = NULL;
|
||||
|
||||
nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num,
|
||||
mod->chip_num);
|
||||
|
||||
if (mod->field != SIM_MOD_LOG_LEVEL) {
|
||||
if (mod->ctrl_num >= MAX_SIM_DEV ||
|
||||
mod->chip_num >= MAX_CTRL_CS)
|
||||
return (EINVAL);
|
||||
|
||||
sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num];
|
||||
sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num);
|
||||
}
|
||||
|
||||
switch (mod->field) {
|
||||
case SIM_MOD_LOG_LEVEL:
|
||||
nandsim_log_level = mod->new_value;
|
||||
break;
|
||||
case SIM_MOD_ERASE_TIME:
|
||||
sim_conf->erase_time = sim_chip->erase_delay = mod->new_value;
|
||||
break;
|
||||
case SIM_MOD_PROG_TIME:
|
||||
sim_conf->prog_time = sim_chip->prog_delay = mod->new_value;
|
||||
break;
|
||||
case SIM_MOD_READ_TIME:
|
||||
sim_conf->read_time = sim_chip->read_delay = mod->new_value;
|
||||
break;
|
||||
case SIM_MOD_ERROR_RATIO:
|
||||
sim_conf->error_ratio = mod->new_value;
|
||||
sim_chip->error_ratio = mod->new_value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
static int
|
||||
nandsim_modevent(module_t mod __unused, int type, void *data __unused)
|
||||
{
|
||||
struct sim_ctrl_chip chip_ctrl;
|
||||
int i, j;
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
nandsim_dev = make_dev(&nandsim_cdevsw, 0,
|
||||
UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl");
|
||||
break;
|
||||
case MOD_UNLOAD:
|
||||
for (i = 0; i < MAX_SIM_DEV; i++) {
|
||||
nandsim_stop_ctrl(i);
|
||||
chip_ctrl.ctrl_num = i;
|
||||
for (j = 0; j < MAX_CTRL_CS; j++) {
|
||||
chip_ctrl.chip_num = j;
|
||||
nandsim_destroy_chip(&chip_ctrl);
|
||||
}
|
||||
nandsim_destroy_ctrl(i);
|
||||
}
|
||||
destroy_dev(nandsim_dev);
|
||||
break;
|
||||
case MOD_SHUTDOWN:
|
||||
break;
|
||||
default:
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
DEV_MODULE(nandsim, nandsim_modevent, NULL);
|
||||
MODULE_VERSION(nandsim, 1);
|
175
sys/dev/nand/nandsim.h
Normal file
175
sys/dev/nand/nandsim.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NANDSIM_H_
|
||||
#define _NANDSIM_H_
|
||||
|
||||
#include <sys/ioccom.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define MAX_SIM_DEV 4
|
||||
#define MAX_CTRL_CS 4
|
||||
#define MAX_ECC_BYTES 512
|
||||
#define MAX_BAD_BLOCKS 512
|
||||
#define DEV_MODEL_STR_SIZE 21
|
||||
#define MAN_STR_SIZE 13
|
||||
#define FILENAME_SIZE 20
|
||||
|
||||
#define MAX_CHIPS (MAX_SIM_DEV*MAX_CTRL_CS)
|
||||
|
||||
#define NANDSIM_OUTPUT_NONE 0x0
|
||||
#define NANDSIM_OUTPUT_CONSOLE 0x1
|
||||
#define NANDSIM_OUTPUT_RAM 0x2
|
||||
#define NANDSIM_OUTPUT_FILE 0x3
|
||||
|
||||
struct sim_ctrl_chip {
|
||||
uint8_t ctrl_num;
|
||||
uint8_t chip_num;
|
||||
};
|
||||
|
||||
#define NANDSIM_BASE 'A'
|
||||
|
||||
struct sim_param {
|
||||
uint8_t log_level;
|
||||
uint8_t log_output;
|
||||
};
|
||||
|
||||
#define NANDSIM_SIM_PARAM _IOW(NANDSIM_BASE, 1, struct sim_param)
|
||||
|
||||
struct sim_ctrl {
|
||||
uint8_t running;
|
||||
uint8_t created;
|
||||
uint8_t num;
|
||||
uint8_t num_cs;
|
||||
uint8_t ecc;
|
||||
char filename[FILENAME_SIZE];
|
||||
uint16_t ecc_layout[MAX_ECC_BYTES];
|
||||
};
|
||||
#define NANDSIM_CREATE_CTRL _IOW(NANDSIM_BASE, 2, struct sim_ctrl)
|
||||
#define NANDSIM_DESTROY_CTRL _IOW(NANDSIM_BASE, 3, int)
|
||||
|
||||
struct sim_chip {
|
||||
uint8_t num;
|
||||
uint8_t ctrl_num;
|
||||
uint8_t created;
|
||||
uint8_t device_id;
|
||||
uint8_t manufact_id;
|
||||
char device_model[DEV_MODEL_STR_SIZE];
|
||||
char manufacturer[MAN_STR_SIZE];
|
||||
uint8_t col_addr_cycles;
|
||||
uint8_t row_addr_cycles;
|
||||
uint8_t features;
|
||||
uint8_t width;
|
||||
uint32_t page_size;
|
||||
uint32_t oob_size;
|
||||
uint32_t pgs_per_blk;
|
||||
uint32_t blks_per_lun;
|
||||
uint32_t luns;
|
||||
|
||||
uint32_t prog_time;
|
||||
uint32_t erase_time;
|
||||
uint32_t read_time;
|
||||
uint32_t ccs_time;
|
||||
|
||||
uint32_t error_ratio;
|
||||
uint32_t wear_level;
|
||||
uint32_t bad_block_map[MAX_BAD_BLOCKS];
|
||||
uint8_t is_wp;
|
||||
};
|
||||
|
||||
#define NANDSIM_CREATE_CHIP _IOW(NANDSIM_BASE, 3, struct sim_chip)
|
||||
|
||||
struct sim_chip_destroy {
|
||||
uint8_t ctrl_num;
|
||||
uint8_t chip_num;
|
||||
};
|
||||
#define NANDSIM_DESTROY_CHIP _IOW(NANDSIM_BASE, 4, struct sim_chip_destroy)
|
||||
|
||||
#define NANDSIM_START_CTRL _IOW(NANDSIM_BASE, 5, int)
|
||||
#define NANDSIM_STOP_CTRL _IOW(NANDSIM_BASE, 6, int)
|
||||
#define NANDSIM_RESTART_CTRL _IOW(NANDSIM_BASE, 7, int)
|
||||
|
||||
#define NANDSIM_STATUS_CTRL _IOWR(NANDSIM_BASE, 8, struct sim_ctrl)
|
||||
#define NANDSIM_STATUS_CHIP _IOWR(NANDSIM_BASE, 9, struct sim_chip)
|
||||
|
||||
struct sim_mod {
|
||||
uint8_t chip_num;
|
||||
uint8_t ctrl_num;
|
||||
uint32_t field;
|
||||
uint32_t new_value;
|
||||
};
|
||||
#define SIM_MOD_LOG_LEVEL 0
|
||||
#define SIM_MOD_ERASE_TIME 1
|
||||
#define SIM_MOD_PROG_TIME 2
|
||||
#define SIM_MOD_READ_TIME 3
|
||||
#define SIM_MOD_CCS_TIME 4
|
||||
#define SIM_MOD_ERROR_RATIO 5
|
||||
|
||||
#define NANDSIM_MODIFY _IOW(NANDSIM_BASE, 10, struct sim_mod)
|
||||
#define NANDSIM_FREEZE _IOW(NANDSIM_BASE, 11, struct sim_ctrl_chip)
|
||||
|
||||
struct sim_error {
|
||||
uint8_t ctrl_num;
|
||||
uint8_t chip_num;
|
||||
uint32_t page_num;
|
||||
uint32_t column;
|
||||
uint32_t len;
|
||||
uint32_t pattern;
|
||||
};
|
||||
#define NANDSIM_INJECT_ERROR _IOW(NANDSIM_BASE, 20, struct sim_error)
|
||||
|
||||
#define NANDSIM_GOOD_BLOCK 0
|
||||
#define NANDSIM_BAD_BLOCK 1
|
||||
struct sim_block_state {
|
||||
uint8_t ctrl_num;
|
||||
uint8_t chip_num;
|
||||
uint32_t block_num;
|
||||
int wearout;
|
||||
uint8_t state;
|
||||
};
|
||||
#define NANDSIM_SET_BLOCK_STATE _IOW(NANDSIM_BASE, 21, struct sim_block_state)
|
||||
#define NANDSIM_GET_BLOCK_STATE _IOWR(NANDSIM_BASE, 22, struct sim_block_state)
|
||||
|
||||
struct sim_log {
|
||||
uint8_t ctrl_num;
|
||||
char* log;
|
||||
size_t len;
|
||||
};
|
||||
#define NANDSIM_PRINT_LOG _IOWR(NANDSIM_BASE, 23, struct sim_log)
|
||||
|
||||
struct sim_dump {
|
||||
uint8_t ctrl_num;
|
||||
uint8_t chip_num;
|
||||
uint32_t block_num;
|
||||
uint32_t len;
|
||||
void* data;
|
||||
};
|
||||
#define NANDSIM_DUMP _IOWR(NANDSIM_BASE, 24, struct sim_dump)
|
||||
#define NANDSIM_RESTORE _IOWR(NANDSIM_BASE, 25, struct sim_dump)
|
||||
|
||||
#endif /* _NANDSIM_H_ */
|
901
sys/dev/nand/nandsim_chip.c
Normal file
901
sys/dev/nand/nandsim_chip.c
Normal file
@ -0,0 +1,901 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/sched.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandsim_chip.h>
|
||||
#include <dev/nand/nandsim_log.h>
|
||||
#include <dev/nand/nandsim_swap.h>
|
||||
|
||||
MALLOC_DEFINE(M_NANDSIM, "NANDsim", "NANDsim dynamic data");
|
||||
|
||||
#define NANDSIM_CHIP_LOCK(chip) mtx_lock(&(chip)->ns_lock)
|
||||
#define NANDSIM_CHIP_UNLOCK(chip) mtx_unlock(&(chip)->ns_lock)
|
||||
|
||||
static nandsim_evh_t erase_evh;
|
||||
static nandsim_evh_t idle_evh;
|
||||
static nandsim_evh_t poweron_evh;
|
||||
static nandsim_evh_t reset_evh;
|
||||
static nandsim_evh_t read_evh;
|
||||
static nandsim_evh_t readid_evh;
|
||||
static nandsim_evh_t readparam_evh;
|
||||
static nandsim_evh_t write_evh;
|
||||
|
||||
static void nandsim_loop(void *);
|
||||
static void nandsim_undefined(struct nandsim_chip *, uint8_t);
|
||||
static void nandsim_bad_address(struct nandsim_chip *, uint8_t *);
|
||||
static void nandsim_ignore_address(struct nandsim_chip *, uint8_t);
|
||||
static void nandsim_sm_error(struct nandsim_chip *);
|
||||
static void nandsim_start_handler(struct nandsim_chip *, nandsim_evh_t);
|
||||
|
||||
static void nandsim_callout_eh(void *);
|
||||
static int nandsim_delay(struct nandsim_chip *, int);
|
||||
|
||||
static int nandsim_bbm_init(struct nandsim_chip *, uint32_t, uint32_t *);
|
||||
static int nandsim_blk_state_init(struct nandsim_chip *, uint32_t, uint32_t);
|
||||
static void nandsim_blk_state_destroy(struct nandsim_chip *);
|
||||
static int nandchip_is_block_valid(struct nandsim_chip *, int);
|
||||
|
||||
static void nandchip_set_status(struct nandsim_chip *, uint8_t);
|
||||
static void nandchip_clear_status(struct nandsim_chip *, uint8_t);
|
||||
|
||||
struct proc *nandsim_proc;
|
||||
|
||||
struct nandsim_chip *
|
||||
nandsim_chip_init(struct nandsim_softc* sc, uint8_t chip_num,
|
||||
struct sim_chip *sim_chip)
|
||||
{
|
||||
struct nandsim_chip *chip;
|
||||
struct onfi_params *chip_param;
|
||||
char swapfile[20];
|
||||
uint32_t size;
|
||||
int error;
|
||||
|
||||
chip = malloc(sizeof(*chip), M_NANDSIM, M_WAITOK | M_ZERO);
|
||||
if (!chip)
|
||||
return (NULL);
|
||||
|
||||
mtx_init(&chip->ns_lock, "nandsim lock", NULL, MTX_DEF);
|
||||
callout_init(&chip->ns_callout, CALLOUT_MPSAFE);
|
||||
STAILQ_INIT(&chip->nandsim_events);
|
||||
|
||||
chip->chip_num = chip_num;
|
||||
chip->ctrl_num = sim_chip->ctrl_num;
|
||||
chip->sc = sc;
|
||||
|
||||
if (!sim_chip->is_wp)
|
||||
nandchip_set_status(chip, NAND_STATUS_WP);
|
||||
|
||||
chip_param = &chip->params;
|
||||
|
||||
chip->id.dev_id = sim_chip->device_id;
|
||||
chip->id.man_id = sim_chip->manufact_id;
|
||||
|
||||
chip->error_ratio = sim_chip->error_ratio;
|
||||
chip->wear_level = sim_chip->wear_level;
|
||||
chip->prog_delay = sim_chip->prog_time;
|
||||
chip->erase_delay = sim_chip->erase_time;
|
||||
chip->read_delay = sim_chip->read_time;
|
||||
|
||||
chip_param->t_prog = sim_chip->prog_time;
|
||||
chip_param->t_bers = sim_chip->erase_time;
|
||||
chip_param->t_r = sim_chip->read_time;
|
||||
bcopy("onfi", &chip_param->signature, 4);
|
||||
|
||||
chip_param->manufacturer_id = sim_chip->manufact_id;
|
||||
strncpy(chip_param->manufacturer_name, sim_chip->manufacturer, 12);
|
||||
chip_param->manufacturer_name[11] = 0;
|
||||
strncpy(chip_param->device_model, sim_chip->device_model, 20);
|
||||
chip_param->device_model[19] = 0;
|
||||
|
||||
chip_param->bytes_per_page = sim_chip->page_size;
|
||||
chip_param->spare_bytes_per_page = sim_chip->oob_size;
|
||||
chip_param->pages_per_block = sim_chip->pgs_per_blk;
|
||||
chip_param->blocks_per_lun = sim_chip->blks_per_lun;
|
||||
chip_param->luns = sim_chip->luns;
|
||||
|
||||
init_chip_geom(&chip->cg, chip_param->luns, chip_param->blocks_per_lun,
|
||||
chip_param->pages_per_block, chip_param->bytes_per_page,
|
||||
chip_param->spare_bytes_per_page);
|
||||
|
||||
chip_param->address_cycles = sim_chip->row_addr_cycles |
|
||||
(sim_chip->col_addr_cycles << 4);
|
||||
chip_param->features = sim_chip->features;
|
||||
if (sim_chip->width == 16)
|
||||
chip_param->features |= ONFI_FEAT_16BIT;
|
||||
|
||||
size = chip_param->blocks_per_lun * chip_param->luns;
|
||||
|
||||
error = nandsim_blk_state_init(chip, size, sim_chip->wear_level);
|
||||
if (error) {
|
||||
mtx_destroy(&chip->ns_lock);
|
||||
free(chip, M_NANDSIM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
error = nandsim_bbm_init(chip, size, sim_chip->bad_block_map);
|
||||
if (error) {
|
||||
mtx_destroy(&chip->ns_lock);
|
||||
nandsim_blk_state_destroy(chip);
|
||||
free(chip, M_NANDSIM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
nandsim_start_handler(chip, poweron_evh);
|
||||
|
||||
nand_debug(NDBG_SIM,"Create thread for chip%d [%8p]", chip->chip_num,
|
||||
chip);
|
||||
/* Create chip thread */
|
||||
error = kproc_kthread_add(nandsim_loop, chip, &nandsim_proc,
|
||||
&chip->nandsim_td, RFSTOPPED | RFHIGHPID,
|
||||
0, "nandsim", "chip");
|
||||
if (error) {
|
||||
mtx_destroy(&chip->ns_lock);
|
||||
nandsim_blk_state_destroy(chip);
|
||||
free(chip, M_NANDSIM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
thread_lock(chip->nandsim_td);
|
||||
sched_class(chip->nandsim_td, PRI_REALTIME);
|
||||
sched_add(chip->nandsim_td, SRQ_BORING);
|
||||
thread_unlock(chip->nandsim_td);
|
||||
|
||||
size = (chip_param->bytes_per_page +
|
||||
chip_param->spare_bytes_per_page) *
|
||||
chip_param->pages_per_block;
|
||||
|
||||
sprintf(swapfile, "chip%d%d.swp", chip->ctrl_num, chip->chip_num);
|
||||
chip->swap = nandsim_swap_init(swapfile, chip_param->blocks_per_lun *
|
||||
chip_param->luns, size);
|
||||
if (!chip->swap)
|
||||
nandsim_chip_destroy(chip);
|
||||
|
||||
/* Wait for new thread to enter main loop */
|
||||
tsleep(chip->nandsim_td, PWAIT, "ns_chip", 1 * hz);
|
||||
|
||||
return (chip);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_blk_state_init(struct nandsim_chip *chip, uint32_t size,
|
||||
uint32_t wear_lev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!chip || size == 0)
|
||||
return (-1);
|
||||
|
||||
chip->blk_state = malloc(size * sizeof(struct nandsim_block_state),
|
||||
M_NANDSIM, M_WAITOK | M_ZERO);
|
||||
if (!chip->blk_state) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (wear_lev)
|
||||
chip->blk_state[i].wear_lev = wear_lev;
|
||||
else
|
||||
chip->blk_state[i].wear_lev = -1;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_blk_state_destroy(struct nandsim_chip *chip)
|
||||
{
|
||||
|
||||
if (chip && chip->blk_state)
|
||||
free(chip->blk_state, M_NANDSIM);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_bbm_init(struct nandsim_chip *chip, uint32_t size,
|
||||
uint32_t *sim_bbm)
|
||||
{
|
||||
uint32_t index;
|
||||
int i;
|
||||
|
||||
if ((chip == NULL) || (size == 0))
|
||||
return (-1);
|
||||
|
||||
if (chip->blk_state == NULL)
|
||||
return (-1);
|
||||
|
||||
if (sim_bbm == NULL)
|
||||
return (0);
|
||||
|
||||
for (i = 0; i < MAX_BAD_BLOCKS; i++) {
|
||||
index = sim_bbm[i];
|
||||
|
||||
if (index == 0xffffffff)
|
||||
break;
|
||||
else if (index > size)
|
||||
return (-1);
|
||||
else
|
||||
chip->blk_state[index].is_bad = 1;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nandsim_chip_destroy(struct nandsim_chip *chip)
|
||||
{
|
||||
struct nandsim_ev *ev;
|
||||
|
||||
ev = create_event(chip, NANDSIM_EV_EXIT, 0);
|
||||
if (ev)
|
||||
send_event(ev);
|
||||
}
|
||||
|
||||
void
|
||||
nandsim_chip_freeze(struct nandsim_chip *chip)
|
||||
{
|
||||
|
||||
chip->flags |= NANDSIM_CHIP_FROZEN;
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_loop(void *arg)
|
||||
{
|
||||
struct nandsim_chip *chip = (struct nandsim_chip *)arg;
|
||||
struct nandsim_ev *ev;
|
||||
|
||||
nand_debug(NDBG_SIM,"Start main loop for chip%d [%8p]", chip->chip_num,
|
||||
chip);
|
||||
for(;;) {
|
||||
NANDSIM_CHIP_LOCK(chip);
|
||||
if (!(chip->flags & NANDSIM_CHIP_ACTIVE)) {
|
||||
chip->flags |= NANDSIM_CHIP_ACTIVE;
|
||||
wakeup(chip->nandsim_td);
|
||||
}
|
||||
|
||||
if (STAILQ_EMPTY(&chip->nandsim_events)) {
|
||||
nand_debug(NDBG_SIM,"Chip%d [%8p] going sleep",
|
||||
chip->chip_num, chip);
|
||||
msleep(chip, &chip->ns_lock, PRIBIO, "nandev", 0);
|
||||
}
|
||||
|
||||
ev = STAILQ_FIRST(&chip->nandsim_events);
|
||||
STAILQ_REMOVE_HEAD(&chip->nandsim_events, links);
|
||||
NANDSIM_CHIP_UNLOCK(chip);
|
||||
if (ev->type == NANDSIM_EV_EXIT) {
|
||||
NANDSIM_CHIP_LOCK(chip);
|
||||
destroy_event(ev);
|
||||
wakeup(ev);
|
||||
while (!STAILQ_EMPTY(&chip->nandsim_events)) {
|
||||
ev = STAILQ_FIRST(&chip->nandsim_events);
|
||||
STAILQ_REMOVE_HEAD(&chip->nandsim_events,
|
||||
links);
|
||||
destroy_event(ev);
|
||||
wakeup(ev);
|
||||
};
|
||||
NANDSIM_CHIP_UNLOCK(chip);
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "destroyed\n");
|
||||
mtx_destroy(&chip->ns_lock);
|
||||
nandsim_blk_state_destroy(chip);
|
||||
nandsim_swap_destroy(chip->swap);
|
||||
free(chip, M_NANDSIM);
|
||||
nandsim_proc = NULL;
|
||||
|
||||
kthread_exit();
|
||||
}
|
||||
|
||||
if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
|
||||
nand_debug(NDBG_SIM,"Chip [%x] get event [%x]",
|
||||
chip->chip_num, ev->type);
|
||||
chip->ev_handler(chip, ev->type, ev->data);
|
||||
}
|
||||
|
||||
wakeup(ev);
|
||||
destroy_event(ev);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct nandsim_ev *
|
||||
create_event(struct nandsim_chip *chip, uint8_t type, uint8_t data_size)
|
||||
{
|
||||
struct nandsim_ev *ev;
|
||||
|
||||
ev = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
|
||||
if (!ev) {
|
||||
nand_debug(NDBG_SIM,"Cannot create event");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (data_size > 0)
|
||||
ev->data = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
|
||||
ev->type = type;
|
||||
ev->chip = chip;
|
||||
|
||||
return (ev);
|
||||
}
|
||||
|
||||
void
|
||||
destroy_event(struct nandsim_ev *ev)
|
||||
{
|
||||
|
||||
if (ev->data)
|
||||
free(ev->data, M_NANDSIM);
|
||||
free(ev, M_NANDSIM);
|
||||
}
|
||||
|
||||
int
|
||||
send_event(struct nandsim_ev *ev)
|
||||
{
|
||||
struct nandsim_chip *chip = ev->chip;
|
||||
|
||||
if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
|
||||
nand_debug(NDBG_SIM,"Chip%d [%p] send event %x",
|
||||
chip->chip_num, chip, ev->type);
|
||||
|
||||
NANDSIM_CHIP_LOCK(chip);
|
||||
STAILQ_INSERT_TAIL(&chip->nandsim_events, ev, links);
|
||||
NANDSIM_CHIP_UNLOCK(chip);
|
||||
|
||||
wakeup(chip);
|
||||
if ((ev->type != NANDSIM_EV_TIMEOUT) && chip->nandsim_td &&
|
||||
(curthread != chip->nandsim_td))
|
||||
tsleep(ev, PWAIT, "ns_ev", 5 * hz);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_callout_eh(void *arg)
|
||||
{
|
||||
struct nandsim_ev *ev = (struct nandsim_ev *)arg;
|
||||
|
||||
send_event(ev);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_delay(struct nandsim_chip *chip, int timeout)
|
||||
{
|
||||
struct nandsim_ev *ev;
|
||||
struct timeval delay;
|
||||
int tm;
|
||||
|
||||
nand_debug(NDBG_SIM,"Chip[%d] Set delay: %d", chip->chip_num, timeout);
|
||||
|
||||
ev = create_event(chip, NANDSIM_EV_TIMEOUT, 0);
|
||||
if (!ev)
|
||||
return (-1);
|
||||
|
||||
chip->sm_state = NANDSIM_STATE_TIMEOUT;
|
||||
tm = (timeout/10000) * (hz / 100);
|
||||
if (callout_reset(&chip->ns_callout, tm, nandsim_callout_eh, ev))
|
||||
return (-1);
|
||||
|
||||
delay.tv_sec = chip->read_delay / 1000000;
|
||||
delay.tv_usec = chip->read_delay % 1000000;
|
||||
timevaladd(&chip->delay_tv, &delay);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_start_handler(struct nandsim_chip *chip, nandsim_evh_t evh)
|
||||
{
|
||||
struct nandsim_ev *ev;
|
||||
|
||||
chip->ev_handler = evh;
|
||||
|
||||
nand_debug(NDBG_SIM,"Start handler %p for chip%d [%p]", evh,
|
||||
chip->chip_num, chip);
|
||||
ev = create_event(chip, NANDSIM_EV_START, 0);
|
||||
if (!ev)
|
||||
nandsim_sm_error(chip);
|
||||
|
||||
send_event(ev);
|
||||
}
|
||||
|
||||
static void
|
||||
nandchip_set_data(struct nandsim_chip *chip, uint8_t *data, uint32_t len,
|
||||
uint32_t idx)
|
||||
{
|
||||
|
||||
nand_debug(NDBG_SIM,"Chip [%x] data %p [%x] at %x", chip->chip_num,
|
||||
data, len, idx);
|
||||
chip->data.data_ptr = data;
|
||||
chip->data.size = len;
|
||||
chip->data.index = idx;
|
||||
}
|
||||
|
||||
static int
|
||||
nandchip_chip_space(struct nandsim_chip *chip, int32_t row, int32_t column,
|
||||
size_t size, uint8_t writing)
|
||||
{
|
||||
struct block_space *blk_space;
|
||||
uint32_t lun, block, page, offset, block_size;
|
||||
int err;
|
||||
|
||||
block_size = chip->cg.block_size +
|
||||
(chip->cg.oob_size * chip->cg.pgs_per_blk);
|
||||
|
||||
err = nand_row_to_blkpg(&chip->cg, row, &lun, &block, &page);
|
||||
if (err) {
|
||||
nand_debug(NDBG_SIM,"cannot get address\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (!nandchip_is_block_valid(chip, block)) {
|
||||
nandchip_set_data(chip, NULL, 0, 0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
blk_space = get_bs(chip->swap, block, writing);
|
||||
if (!blk_space) {
|
||||
nandchip_set_data(chip, NULL, 0, 0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (size > block_size)
|
||||
size = block_size;
|
||||
|
||||
if (size == block_size) {
|
||||
offset = 0;
|
||||
column = 0;
|
||||
} else
|
||||
offset = page * (chip->cg.page_size + chip->cg.oob_size);
|
||||
|
||||
nandchip_set_data(chip, &blk_space->blk_ptr[offset], size, column);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandchip_get_addr_byte(struct nandsim_chip *chip, void *data, uint32_t *value)
|
||||
{
|
||||
int ncycles = 0;
|
||||
uint8_t byte;
|
||||
uint8_t *buffer;
|
||||
|
||||
buffer = (uint8_t *)value;
|
||||
byte = *((uint8_t *)data);
|
||||
|
||||
KASSERT((chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW ||
|
||||
chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL),
|
||||
("unexpected state"));
|
||||
|
||||
if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
|
||||
ncycles = chip->params.address_cycles & 0xf;
|
||||
buffer[chip->sm_addr_cycle++] = byte;
|
||||
} else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
|
||||
ncycles = (chip->params.address_cycles >> 4) & 0xf;
|
||||
buffer[chip->sm_addr_cycle++] = byte;
|
||||
}
|
||||
|
||||
nand_debug(NDBG_SIM, "Chip [%x] read addr byte: %02x (%d of %d)\n",
|
||||
chip->chip_num, byte, chip->sm_addr_cycle, ncycles);
|
||||
|
||||
if (chip->sm_addr_cycle == ncycles) {
|
||||
chip->sm_addr_cycle = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
nandchip_is_block_valid(struct nandsim_chip *chip, int block_num)
|
||||
{
|
||||
|
||||
if (!chip || !chip->blk_state)
|
||||
return (0);
|
||||
|
||||
if (chip->blk_state[block_num].wear_lev == 0 ||
|
||||
chip->blk_state[block_num].is_bad)
|
||||
return (0);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
nandchip_set_status(struct nandsim_chip *chip, uint8_t flags)
|
||||
{
|
||||
|
||||
chip->chip_status |= flags;
|
||||
}
|
||||
|
||||
static void
|
||||
nandchip_clear_status(struct nandsim_chip *chip, uint8_t flags)
|
||||
{
|
||||
|
||||
chip->chip_status &= ~flags;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
nandchip_get_status(struct nandsim_chip *chip)
|
||||
{
|
||||
return (chip->chip_status);
|
||||
}
|
||||
|
||||
void
|
||||
nandsim_chip_timeout(struct nandsim_chip *chip)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
getmicrotime(&tv);
|
||||
|
||||
if (chip->sm_state == NANDSIM_STATE_TIMEOUT &&
|
||||
timevalcmp(&tv, &chip->delay_tv, >=)) {
|
||||
nandchip_set_status(chip, NAND_STATUS_RDY);
|
||||
}
|
||||
}
|
||||
void
|
||||
poweron_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
uint8_t cmd;
|
||||
|
||||
if (type == NANDSIM_EV_START)
|
||||
chip->sm_state = NANDSIM_STATE_IDLE;
|
||||
else if (type == NANDSIM_EV_CMD) {
|
||||
cmd = *(uint8_t *)data;
|
||||
switch(cmd) {
|
||||
case NAND_CMD_RESET:
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
|
||||
nandsim_start_handler(chip, reset_evh);
|
||||
break;
|
||||
default:
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
}
|
||||
|
||||
void
|
||||
idle_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
uint8_t cmd;
|
||||
|
||||
if (type == NANDSIM_EV_START) {
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in IDLE state\n");
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_CMD;
|
||||
} else if (type == NANDSIM_EV_CMD) {
|
||||
nandchip_clear_status(chip, NAND_STATUS_FAIL);
|
||||
getmicrotime(&chip->delay_tv);
|
||||
cmd = *(uint8_t *)data;
|
||||
switch(cmd) {
|
||||
case NAND_CMD_READ_ID:
|
||||
nandsim_start_handler(chip, readid_evh);
|
||||
break;
|
||||
case NAND_CMD_READ_PARAMETER:
|
||||
nandsim_start_handler(chip, readparam_evh);
|
||||
break;
|
||||
case NAND_CMD_READ:
|
||||
nandsim_start_handler(chip, read_evh);
|
||||
break;
|
||||
case NAND_CMD_PROG:
|
||||
nandsim_start_handler(chip, write_evh);
|
||||
break;
|
||||
case NAND_CMD_ERASE:
|
||||
nandsim_start_handler(chip, erase_evh);
|
||||
break;
|
||||
default:
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
}
|
||||
|
||||
void
|
||||
readid_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
struct onfi_params *params;
|
||||
uint8_t addr;
|
||||
|
||||
params = &chip->params;
|
||||
|
||||
if (type == NANDSIM_EV_START) {
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in READID state\n");
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
|
||||
} else if (type == NANDSIM_EV_ADDR) {
|
||||
|
||||
addr = *((uint8_t *)data);
|
||||
|
||||
if (addr == 0x0)
|
||||
nandchip_set_data(chip, (uint8_t *)&chip->id, 2, 0);
|
||||
else if (addr == ONFI_SIG_ADDR)
|
||||
nandchip_set_data(chip, (uint8_t *)¶ms->signature,
|
||||
4, 0);
|
||||
else
|
||||
nandsim_bad_address(chip, &addr);
|
||||
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
}
|
||||
|
||||
void
|
||||
readparam_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
struct onfi_params *params;
|
||||
uint8_t addr;
|
||||
|
||||
params = &chip->params;
|
||||
|
||||
if (type == NANDSIM_EV_START) {
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in READPARAM state\n");
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
|
||||
} else if (type == NANDSIM_EV_ADDR) {
|
||||
addr = *((uint8_t *)data);
|
||||
|
||||
if (addr == 0) {
|
||||
nandchip_set_data(chip, (uint8_t *)params,
|
||||
sizeof(*params), 0);
|
||||
} else
|
||||
nandsim_bad_address(chip, &addr);
|
||||
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
}
|
||||
|
||||
void
|
||||
read_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
static uint32_t column = 0, row = 0;
|
||||
uint32_t size;
|
||||
uint8_t cmd;
|
||||
|
||||
size = chip->cg.page_size + chip->cg.oob_size;
|
||||
|
||||
switch (type) {
|
||||
case NANDSIM_EV_START:
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in READ state\n");
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
|
||||
break;
|
||||
case NANDSIM_EV_ADDR:
|
||||
if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
|
||||
if (nandchip_get_addr_byte(chip, data, &column))
|
||||
break;
|
||||
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
|
||||
} else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
|
||||
if (nandchip_get_addr_byte(chip, data, &row))
|
||||
break;
|
||||
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_CMD;
|
||||
} else
|
||||
nandsim_ignore_address(chip, *((uint8_t *)data));
|
||||
break;
|
||||
case NANDSIM_EV_CMD:
|
||||
cmd = *(uint8_t *)data;
|
||||
if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
|
||||
cmd == NAND_CMD_READ_END) {
|
||||
if (chip->read_delay != 0 &&
|
||||
nandsim_delay(chip, chip->read_delay) == 0)
|
||||
nandchip_clear_status(chip, NAND_STATUS_RDY);
|
||||
else {
|
||||
nandchip_chip_space(chip, row, column, size, 0);
|
||||
nandchip_set_status(chip, NAND_STATUS_RDY);
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
}
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
case NANDSIM_EV_TIMEOUT:
|
||||
if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
|
||||
nandchip_chip_space(chip, row, column, size, 0);
|
||||
nandchip_set_status(chip, NAND_STATUS_RDY);
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void
|
||||
write_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
static uint32_t column, row;
|
||||
uint32_t size;
|
||||
uint8_t cmd;
|
||||
int err;
|
||||
|
||||
size = chip->cg.page_size + chip->cg.oob_size;
|
||||
|
||||
switch(type) {
|
||||
case NANDSIM_EV_START:
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in WRITE state\n");
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
|
||||
break;
|
||||
case NANDSIM_EV_ADDR:
|
||||
if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
|
||||
if (nandchip_get_addr_byte(chip, data, &column))
|
||||
break;
|
||||
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
|
||||
} else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
|
||||
if (nandchip_get_addr_byte(chip, data, &row))
|
||||
break;
|
||||
|
||||
err = nandchip_chip_space(chip, row, column, size, 1);
|
||||
if (err == -1)
|
||||
nandchip_set_status(chip, NAND_STATUS_FAIL);
|
||||
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_CMD;
|
||||
} else
|
||||
nandsim_ignore_address(chip, *((uint8_t *)data));
|
||||
break;
|
||||
case NANDSIM_EV_CMD:
|
||||
cmd = *(uint8_t *)data;
|
||||
if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
|
||||
cmd == NAND_CMD_PROG_END) {
|
||||
if (chip->prog_delay != 0 &&
|
||||
nandsim_delay(chip, chip->prog_delay) == 0)
|
||||
nandchip_clear_status(chip, NAND_STATUS_RDY);
|
||||
else {
|
||||
nandchip_set_status(chip, NAND_STATUS_RDY);
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
}
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
case NANDSIM_EV_TIMEOUT:
|
||||
if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
nandchip_set_status(chip, NAND_STATUS_RDY);
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
erase_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
static uint32_t row, block_size;
|
||||
uint32_t lun, block, page;
|
||||
int err;
|
||||
uint8_t cmd;
|
||||
|
||||
block_size = chip->cg.block_size +
|
||||
(chip->cg.oob_size * chip->cg.pgs_per_blk);
|
||||
|
||||
switch (type) {
|
||||
case NANDSIM_EV_START:
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in ERASE state\n");
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
|
||||
break;
|
||||
case NANDSIM_EV_CMD:
|
||||
cmd = *(uint8_t *)data;
|
||||
if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
|
||||
cmd == NAND_CMD_ERASE_END) {
|
||||
if (chip->data.data_ptr != NULL &&
|
||||
chip->data.size == block_size)
|
||||
memset(chip->data.data_ptr, 0xff, block_size);
|
||||
else
|
||||
nand_debug(NDBG_SIM,"Bad block erase data\n");
|
||||
|
||||
err = nand_row_to_blkpg(&chip->cg, row, &lun,
|
||||
&block, &page);
|
||||
if (!err) {
|
||||
if (chip->blk_state[block].wear_lev > 0)
|
||||
chip->blk_state[block].wear_lev--;
|
||||
}
|
||||
|
||||
if (chip->erase_delay != 0 &&
|
||||
nandsim_delay(chip, chip->erase_delay) == 0)
|
||||
nandchip_clear_status(chip, NAND_STATUS_RDY);
|
||||
else {
|
||||
nandchip_set_status(chip, NAND_STATUS_RDY);
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
}
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
case NANDSIM_EV_ADDR:
|
||||
if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
|
||||
if (nandchip_get_addr_byte(chip, data, &row))
|
||||
break;
|
||||
|
||||
err = nandchip_chip_space(chip, row, 0, block_size, 1);
|
||||
if (err == -1) {
|
||||
nandchip_set_status(chip, NAND_STATUS_FAIL);
|
||||
}
|
||||
chip->sm_state = NANDSIM_STATE_WAIT_CMD;
|
||||
} else
|
||||
nandsim_ignore_address(chip, *((uint8_t *)data));
|
||||
break;
|
||||
case NANDSIM_EV_TIMEOUT:
|
||||
if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
|
||||
nandchip_set_status(chip, NAND_STATUS_RDY);
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
reset_evh(struct nandsim_chip *chip, uint32_t type, void *data)
|
||||
{
|
||||
|
||||
if (type == NANDSIM_EV_START) {
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
|
||||
chip->sm_state = NANDSIM_STATE_TIMEOUT;
|
||||
nandchip_set_data(chip, NULL, 0, 0);
|
||||
DELAY(500);
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
} else
|
||||
nandsim_undefined(chip, type);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_undefined(struct nandsim_chip *chip, uint8_t type)
|
||||
{
|
||||
|
||||
nandsim_log(chip, NANDSIM_LOG_ERR,
|
||||
"ERR: Chip received ev %x in state %x\n",
|
||||
type, chip->sm_state);
|
||||
nandsim_start_handler(chip, idle_evh);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_bad_address(struct nandsim_chip *chip, uint8_t *addr)
|
||||
{
|
||||
|
||||
nandsim_log(chip, NANDSIM_LOG_ERR,
|
||||
"ERR: Chip received out of range address"
|
||||
"%02x%02x - %02x%02x%02x\n", addr[0], addr[1], addr[2],
|
||||
addr[3], addr[4]);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_ignore_address(struct nandsim_chip *chip, uint8_t byte)
|
||||
{
|
||||
nandsim_log(chip, NANDSIM_LOG_SM, "ignored address byte: %d\n", byte);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_sm_error(struct nandsim_chip *chip)
|
||||
{
|
||||
|
||||
nandsim_log(chip, NANDSIM_LOG_ERR, "ERR: State machine error."
|
||||
"Restart required.\n");
|
||||
}
|
159
sys/dev/nand/nandsim_chip.h
Normal file
159
sys/dev/nand/nandsim_chip.h
Normal file
@ -0,0 +1,159 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NANDSIM_CHIP_H
|
||||
#define _NANDSIM_CHIP_H
|
||||
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/callout.h>
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandsim.h>
|
||||
#include <dev/nand/nandsim_swap.h>
|
||||
|
||||
MALLOC_DECLARE(M_NANDSIM);
|
||||
|
||||
#define MAX_CS_NUM 4
|
||||
struct nandsim_chip;
|
||||
|
||||
typedef void nandsim_evh_t(struct nandsim_chip *chip, uint32_t ev, void *data);
|
||||
|
||||
enum addr_type {
|
||||
ADDR_NONE,
|
||||
ADDR_ID,
|
||||
ADDR_ROW,
|
||||
ADDR_ROWCOL
|
||||
};
|
||||
|
||||
struct nandsim_softc {
|
||||
struct nand_softc nand_dev;
|
||||
device_t dev;
|
||||
|
||||
struct nandsim_chip *chips[MAX_CS_NUM];
|
||||
struct nandsim_chip *active_chip;
|
||||
|
||||
uint8_t address_cycle;
|
||||
enum addr_type address_type;
|
||||
int log_idx;
|
||||
char *log_buff;
|
||||
struct alq *alq;
|
||||
};
|
||||
|
||||
struct nandsim_ev {
|
||||
STAILQ_ENTRY(nandsim_ev) links;
|
||||
struct nandsim_chip *chip;
|
||||
uint8_t type;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct nandsim_data {
|
||||
uint8_t *data_ptr;
|
||||
uint32_t index;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct nandsim_block_state {
|
||||
int32_t wear_lev;
|
||||
uint8_t is_bad;
|
||||
};
|
||||
|
||||
#define NANDSIM_CHIP_ACTIVE 0x1
|
||||
#define NANDSIM_CHIP_FROZEN 0x2
|
||||
#define NANDSIM_CHIP_GET_STATUS 0x4
|
||||
|
||||
struct nandsim_chip {
|
||||
struct nandsim_softc *sc;
|
||||
struct thread *nandsim_td;
|
||||
|
||||
STAILQ_HEAD(, nandsim_ev) nandsim_events;
|
||||
nandsim_evh_t *ev_handler;
|
||||
struct mtx ns_lock;
|
||||
struct callout ns_callout;
|
||||
|
||||
struct chip_geom cg;
|
||||
struct nand_id id;
|
||||
struct onfi_params params;
|
||||
struct nandsim_data data;
|
||||
struct nandsim_block_state *blk_state;
|
||||
|
||||
struct chip_swap *swap;
|
||||
|
||||
uint32_t error_ratio;
|
||||
uint32_t wear_level;
|
||||
uint32_t sm_state;
|
||||
uint32_t sm_addr_cycle;
|
||||
|
||||
uint32_t erase_delay;
|
||||
uint32_t prog_delay;
|
||||
uint32_t read_delay;
|
||||
struct timeval delay_tv;
|
||||
|
||||
uint8_t flags;
|
||||
uint8_t chip_status;
|
||||
uint8_t ctrl_num;
|
||||
uint8_t chip_num;
|
||||
};
|
||||
|
||||
struct sim_ctrl_conf {
|
||||
uint8_t num;
|
||||
uint8_t num_cs;
|
||||
uint8_t ecc;
|
||||
uint8_t running;
|
||||
uint8_t created;
|
||||
device_t sim_ctrl_dev;
|
||||
struct sim_chip *chips[MAX_CTRL_CS];
|
||||
uint16_t ecc_layout[MAX_ECC_BYTES];
|
||||
char filename[FILENAME_SIZE];
|
||||
};
|
||||
|
||||
#define NANDSIM_STATE_IDLE 0x0
|
||||
#define NANDSIM_STATE_WAIT_ADDR_BYTE 0x1
|
||||
#define NANDSIM_STATE_WAIT_CMD 0x2
|
||||
#define NANDSIM_STATE_TIMEOUT 0x3
|
||||
#define NANDSIM_STATE_WAIT_ADDR_ROW 0x4
|
||||
#define NANDSIM_STATE_WAIT_ADDR_COL 0x5
|
||||
|
||||
#define NANDSIM_EV_START 0x1
|
||||
#define NANDSIM_EV_CMD 0x2
|
||||
#define NANDSIM_EV_ADDR 0x3
|
||||
#define NANDSIM_EV_TIMEOUT 0x4
|
||||
#define NANDSIM_EV_EXIT 0xff
|
||||
|
||||
struct nandsim_chip *nandsim_chip_init(struct nandsim_softc *,
|
||||
uint8_t, struct sim_chip *);
|
||||
void nandsim_chip_destroy(struct nandsim_chip *);
|
||||
void nandsim_chip_freeze(struct nandsim_chip *);
|
||||
void nandsim_chip_timeout(struct nandsim_chip *);
|
||||
int nandsim_chip_check_bad_block(struct nandsim_chip *, int);
|
||||
|
||||
uint8_t nandchip_get_status(struct nandsim_chip *);
|
||||
|
||||
void destroy_event(struct nandsim_ev *);
|
||||
int send_event(struct nandsim_ev *);
|
||||
struct nandsim_ev *create_event(struct nandsim_chip *, uint8_t, uint8_t);
|
||||
|
||||
#endif /* _NANDSIM_CHIP_H */
|
396
sys/dev/nand/nandsim_ctrl.c
Normal file
396
sys/dev/nand/nandsim_ctrl.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Simulated NAND controller driver */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandbus.h>
|
||||
#include <dev/nand/nandsim.h>
|
||||
#include <dev/nand/nandsim_log.h>
|
||||
#include <dev/nand/nandsim_chip.h>
|
||||
#include "nfc_if.h"
|
||||
|
||||
#define ADDRESS_SIZE 5
|
||||
|
||||
extern struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
|
||||
|
||||
static void byte_corrupt(struct nandsim_chip *, uint8_t *);
|
||||
|
||||
static int nandsim_attach(device_t);
|
||||
static int nandsim_detach(device_t);
|
||||
static int nandsim_probe(device_t);
|
||||
|
||||
static uint8_t nandsim_read_byte(device_t);
|
||||
static uint16_t nandsim_read_word(device_t);
|
||||
static int nandsim_select_cs(device_t, uint8_t);
|
||||
static void nandsim_write_byte(device_t, uint8_t);
|
||||
static void nandsim_write_word(device_t, uint16_t);
|
||||
static void nandsim_read_buf(device_t, void *, uint32_t);
|
||||
static void nandsim_write_buf(device_t, void *, uint32_t);
|
||||
static int nandsim_send_command(device_t, uint8_t);
|
||||
static int nandsim_send_address(device_t, uint8_t);
|
||||
|
||||
static device_method_t nandsim_methods[] = {
|
||||
DEVMETHOD(device_probe, nandsim_probe),
|
||||
DEVMETHOD(device_attach, nandsim_attach),
|
||||
DEVMETHOD(device_detach, nandsim_detach),
|
||||
|
||||
DEVMETHOD(nfc_select_cs, nandsim_select_cs),
|
||||
DEVMETHOD(nfc_send_command, nandsim_send_command),
|
||||
DEVMETHOD(nfc_send_address, nandsim_send_address),
|
||||
DEVMETHOD(nfc_read_byte, nandsim_read_byte),
|
||||
DEVMETHOD(nfc_read_word, nandsim_read_word),
|
||||
DEVMETHOD(nfc_write_byte, nandsim_write_byte),
|
||||
DEVMETHOD(nfc_read_buf, nandsim_read_buf),
|
||||
DEVMETHOD(nfc_write_buf, nandsim_write_buf),
|
||||
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static driver_t nandsim_driver = {
|
||||
"nandsim",
|
||||
nandsim_methods,
|
||||
sizeof(struct nandsim_softc),
|
||||
};
|
||||
|
||||
static devclass_t nandsim_devclass;
|
||||
DRIVER_MODULE(nandsim, nexus, nandsim_driver, nandsim_devclass, 0, 0);
|
||||
DRIVER_MODULE(nandbus, nandsim, nandbus_driver, nandbus_devclass, 0, 0);
|
||||
|
||||
static int
|
||||
nandsim_probe(device_t dev)
|
||||
{
|
||||
|
||||
device_set_desc(dev, "NAND controller simulator");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_attach(device_t dev)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
struct sim_ctrl_conf *params;
|
||||
struct sim_chip *chip;
|
||||
uint16_t *eccpos;
|
||||
int i, err;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
params = &ctrls[device_get_unit(dev)];
|
||||
|
||||
if (strlen(params->filename) == 0)
|
||||
snprintf(params->filename, FILENAME_SIZE, "ctrl%d.log",
|
||||
params->num);
|
||||
|
||||
nandsim_log_init(sc, params->filename);
|
||||
for (i = 0; i < params->num_cs; i++) {
|
||||
chip = params->chips[i];
|
||||
if (chip && chip->device_id != 0) {
|
||||
sc->chips[i] = nandsim_chip_init(sc, i, chip);
|
||||
if (chip->features & ONFI_FEAT_16BIT)
|
||||
sc->nand_dev.flags |= NAND_16_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->ecc_layout[0] != 0xffff)
|
||||
eccpos = params->ecc_layout;
|
||||
else
|
||||
eccpos = NULL;
|
||||
|
||||
nand_init(&sc->nand_dev, dev, params->ecc, 0, 0, eccpos, "nandsim");
|
||||
|
||||
err = nandbus_create(dev);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_detach(device_t dev)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
struct sim_ctrl_conf *params;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
params = &ctrls[device_get_unit(dev)];
|
||||
|
||||
for (i = 0; i < params->num_cs; i++)
|
||||
if (sc->chips[i] != NULL)
|
||||
nandsim_chip_destroy(sc->chips[i]);
|
||||
|
||||
nandsim_log_close(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_select_cs(device_t dev, uint8_t cs)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (cs >= MAX_CS_NUM)
|
||||
return (EINVAL);
|
||||
|
||||
sc->active_chip = sc->chips[cs];
|
||||
|
||||
if (sc->active_chip)
|
||||
nandsim_log(sc->active_chip, NANDSIM_LOG_EV,
|
||||
"Select cs %d\n", cs);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_send_command(device_t dev, uint8_t command)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
struct nandsim_chip *chip;
|
||||
struct nandsim_ev *ev;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
chip = sc->active_chip;
|
||||
|
||||
if (chip == NULL)
|
||||
return (0);
|
||||
|
||||
nandsim_log(chip, NANDSIM_LOG_EV, "Send command %x\n", command);
|
||||
|
||||
switch (command) {
|
||||
case NAND_CMD_READ_ID:
|
||||
case NAND_CMD_READ_PARAMETER:
|
||||
sc->address_type = ADDR_ID;
|
||||
break;
|
||||
case NAND_CMD_ERASE:
|
||||
sc->address_type = ADDR_ROW;
|
||||
break;
|
||||
case NAND_CMD_READ:
|
||||
case NAND_CMD_PROG:
|
||||
sc->address_type = ADDR_ROWCOL;
|
||||
break;
|
||||
default:
|
||||
sc->address_type = ADDR_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (command == NAND_CMD_STATUS)
|
||||
chip->flags |= NANDSIM_CHIP_GET_STATUS;
|
||||
else {
|
||||
ev = create_event(chip, NANDSIM_EV_CMD, 1);
|
||||
*(uint8_t *)ev->data = command;
|
||||
send_event(ev);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandsim_send_address(device_t dev, uint8_t addr)
|
||||
{
|
||||
struct nandsim_ev *ev;
|
||||
struct nandsim_softc *sc;
|
||||
struct nandsim_chip *chip;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
chip = sc->active_chip;
|
||||
|
||||
if (chip == NULL)
|
||||
return (0);
|
||||
|
||||
KASSERT((sc->address_type != ADDR_NONE), ("unexpected address"));
|
||||
nandsim_log(chip, NANDSIM_LOG_EV, "Send addr %x\n", addr);
|
||||
|
||||
ev = create_event(chip, NANDSIM_EV_ADDR, 1);
|
||||
|
||||
*((uint8_t *)(ev->data)) = addr;
|
||||
|
||||
send_event(ev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
nandsim_read_byte(device_t dev)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
struct nandsim_chip *chip;
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
chip = sc->active_chip;
|
||||
|
||||
if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
|
||||
if (chip->flags & NANDSIM_CHIP_GET_STATUS) {
|
||||
nandsim_chip_timeout(chip);
|
||||
ret = nandchip_get_status(chip);
|
||||
chip->flags &= ~NANDSIM_CHIP_GET_STATUS;
|
||||
} else if (chip->data.index < chip->data.size) {
|
||||
ret = chip->data.data_ptr[chip->data.index++];
|
||||
byte_corrupt(chip, &ret);
|
||||
}
|
||||
nandsim_log(chip, NANDSIM_LOG_DATA, "read %02x\n", ret);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
nandsim_read_word(device_t dev)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
struct nandsim_chip *chip;
|
||||
uint16_t *data_ptr;
|
||||
uint16_t ret = 0xffff;
|
||||
uint8_t *byte_ret = (uint8_t *)&ret;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
chip = sc->active_chip;
|
||||
|
||||
if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
|
||||
if (chip->data.index < chip->data.size - 1) {
|
||||
data_ptr =
|
||||
(uint16_t *)&(chip->data.data_ptr[chip->data.index]);
|
||||
ret = *data_ptr;
|
||||
chip->data.index += 2;
|
||||
byte_corrupt(chip, byte_ret);
|
||||
byte_corrupt(chip, byte_ret + 1);
|
||||
}
|
||||
nandsim_log(chip, NANDSIM_LOG_DATA, "read %04x\n", ret);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_write_byte(device_t dev, uint8_t byte)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
struct nandsim_chip *chip;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
chip = sc->active_chip;
|
||||
|
||||
if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN) &&
|
||||
(chip->data.index < chip->data.size)) {
|
||||
byte_corrupt(chip, &byte);
|
||||
chip->data.data_ptr[chip->data.index] &= byte;
|
||||
chip->data.index++;
|
||||
nandsim_log(chip, NANDSIM_LOG_DATA, "write %02x\n", byte);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_write_word(device_t dev, uint16_t word)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
struct nandsim_chip *chip;
|
||||
uint16_t *data_ptr;
|
||||
uint8_t *byte_ptr = (uint8_t *)&word;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
chip = sc->active_chip;
|
||||
|
||||
if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
|
||||
if ((chip->data.index + 1) < chip->data.size) {
|
||||
byte_corrupt(chip, byte_ptr);
|
||||
byte_corrupt(chip, byte_ptr + 1);
|
||||
data_ptr =
|
||||
(uint16_t *)&(chip->data.data_ptr[chip->data.index]);
|
||||
*data_ptr &= word;
|
||||
chip->data.index += 2;
|
||||
}
|
||||
|
||||
nandsim_log(chip, NANDSIM_LOG_DATA, "write %04x\n", word);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_read_buf(device_t dev, void *buf, uint32_t len)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
uint16_t *buf16 = (uint16_t *)buf;
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (sc->nand_dev.flags & NAND_16_BIT) {
|
||||
for (i = 0; i < len / 2; i++)
|
||||
buf16[i] = nandsim_read_word(dev);
|
||||
} else {
|
||||
for (i = 0; i < len; i++)
|
||||
buf8[i] = nandsim_read_byte(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nandsim_write_buf(device_t dev, void *buf, uint32_t len)
|
||||
{
|
||||
struct nandsim_softc *sc;
|
||||
uint16_t *buf16 = (uint16_t *)buf;
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (sc->nand_dev.flags & NAND_16_BIT) {
|
||||
for (i = 0; i < len / 2; i++)
|
||||
nandsim_write_word(dev, buf16[i]);
|
||||
} else {
|
||||
for (i = 0; i < len; i++)
|
||||
nandsim_write_byte(dev, buf8[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
byte_corrupt(struct nandsim_chip *chip, uint8_t *byte)
|
||||
{
|
||||
uint32_t rand;
|
||||
uint8_t bit;
|
||||
|
||||
rand = random();
|
||||
if ((rand % 1000000) < chip->error_ratio) {
|
||||
bit = rand % 8;
|
||||
if (*byte & (1 << bit))
|
||||
*byte &= ~(1 << bit);
|
||||
else
|
||||
*byte |= (1 << bit);
|
||||
}
|
||||
}
|
186
sys/dev/nand/nandsim_log.c
Normal file
186
sys/dev/nand/nandsim_log.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/alq.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <machine/stdarg.h>
|
||||
|
||||
#include <dev/nand/nandsim_log.h>
|
||||
|
||||
int nandsim_log_level;
|
||||
int nandsim_log_output;
|
||||
int log_size = NANDSIM_RAM_LOG_SIZE;
|
||||
|
||||
static int nandsim_entry_size = NANDSIM_ENTRY_SIZE;
|
||||
static int nandsim_entry_count = NANDSIM_ENTRY_COUNT;
|
||||
static int str_index = 0;
|
||||
static char string[NANDSIM_ENTRY_SIZE + 1] = {0};
|
||||
|
||||
int
|
||||
nandsim_log_init(struct nandsim_softc *sc, char *filename)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (nandsim_log_output == NANDSIM_OUTPUT_FILE) {
|
||||
error = alq_open(&sc->alq, filename,
|
||||
curthread->td_ucred, 0644,
|
||||
nandsim_entry_size, nandsim_entry_count);
|
||||
} else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) {
|
||||
sc->log_buff = malloc(log_size, M_NANDSIM, M_WAITOK | M_ZERO);
|
||||
if (!sc->log_buff)
|
||||
error = ENOMEM;
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
nandsim_log_close(struct nandsim_softc *sc)
|
||||
{
|
||||
|
||||
if (nandsim_log_output == NANDSIM_OUTPUT_FILE) {
|
||||
memset(&string[str_index], 0, NANDSIM_ENTRY_SIZE - str_index);
|
||||
alq_write(sc->alq, (void *) string, ALQ_NOWAIT);
|
||||
str_index = 0;
|
||||
string[0] = '\0';
|
||||
alq_close(sc->alq);
|
||||
} else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) {
|
||||
free(sc->log_buff, M_NANDSIM);
|
||||
sc->log_buff = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nandsim_log(struct nandsim_chip *chip, int level, const char *fmt, ...)
|
||||
{
|
||||
char hdr[TIME_STR_SIZE];
|
||||
char tmp[NANDSIM_ENTRY_SIZE];
|
||||
struct nandsim_softc *sc;
|
||||
struct timeval currtime;
|
||||
va_list ap;
|
||||
int hdr_len, len, rest;
|
||||
|
||||
if (nandsim_log_output == NANDSIM_OUTPUT_NONE)
|
||||
return;
|
||||
|
||||
if (chip == NULL)
|
||||
return;
|
||||
|
||||
sc = chip->sc;
|
||||
if (!sc->alq && nandsim_log_output == NANDSIM_OUTPUT_FILE)
|
||||
return;
|
||||
|
||||
if (level <= nandsim_log_level) {
|
||||
microtime(&currtime);
|
||||
hdr_len = sprintf(hdr, "%08jd.%08li [chip:%d, ctrl:%d]: ",
|
||||
(intmax_t)currtime.tv_sec, currtime.tv_usec,
|
||||
chip->chip_num, chip->ctrl_num);
|
||||
|
||||
switch(nandsim_log_output) {
|
||||
case NANDSIM_OUTPUT_CONSOLE:
|
||||
printf("%s", hdr);
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
break;
|
||||
case NANDSIM_OUTPUT_RAM:
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap);
|
||||
tmp[NANDSIM_ENTRY_SIZE - 1] = 0;
|
||||
va_end(ap);
|
||||
|
||||
rest = log_size - sc->log_idx - 1;
|
||||
if (rest >= hdr_len) {
|
||||
bcopy(hdr, &sc->log_buff[sc->log_idx],
|
||||
hdr_len);
|
||||
sc->log_idx += hdr_len;
|
||||
sc->log_buff[sc->log_idx] = 0;
|
||||
} else {
|
||||
bcopy(hdr, &sc->log_buff[sc->log_idx], rest);
|
||||
bcopy(&hdr[rest], sc->log_buff,
|
||||
hdr_len - rest);
|
||||
sc->log_idx = hdr_len - rest;
|
||||
sc->log_buff[sc->log_idx] = 0;
|
||||
}
|
||||
|
||||
rest = log_size - sc->log_idx - 1;
|
||||
if (rest >= len) {
|
||||
bcopy(tmp, &sc->log_buff[sc->log_idx], len);
|
||||
sc->log_idx += len;
|
||||
sc->log_buff[sc->log_idx] = 0;
|
||||
} else {
|
||||
bcopy(tmp, &sc->log_buff[sc->log_idx], rest);
|
||||
bcopy(&tmp[rest], sc->log_buff, len - rest);
|
||||
sc->log_idx = len - rest;
|
||||
sc->log_buff[sc->log_idx] = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NANDSIM_OUTPUT_FILE:
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap);
|
||||
tmp[NANDSIM_ENTRY_SIZE - 1] = 0;
|
||||
va_end(ap);
|
||||
|
||||
rest = NANDSIM_ENTRY_SIZE - str_index;
|
||||
if (rest >= hdr_len) {
|
||||
strcat(string, hdr);
|
||||
str_index += hdr_len;
|
||||
} else {
|
||||
strlcat(string, hdr, NANDSIM_ENTRY_SIZE + 1);
|
||||
alq_write(sc->alq, (void *) string,
|
||||
ALQ_NOWAIT);
|
||||
strcpy(string, &hdr[rest]);
|
||||
str_index = hdr_len - rest;
|
||||
}
|
||||
rest = NANDSIM_ENTRY_SIZE - str_index;
|
||||
if (rest >= len) {
|
||||
strcat(string, tmp);
|
||||
str_index += len;
|
||||
} else {
|
||||
strlcat(string, tmp, NANDSIM_ENTRY_SIZE + 1);
|
||||
alq_write(sc->alq, (void *) string,
|
||||
ALQ_NOWAIT);
|
||||
strcpy(string, &tmp[rest]);
|
||||
str_index = len - rest;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
52
sys/dev/nand/nandsim_log.h
Normal file
52
sys/dev/nand/nandsim_log.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NANDSIM_LOG_H
|
||||
#define _NANDSIM_LOG_H
|
||||
|
||||
#include <dev/nand/nandsim_chip.h>
|
||||
|
||||
#define NANDSIM_ENTRY_SIZE 128
|
||||
#define NANDSIM_ENTRY_COUNT 1024
|
||||
#define NANDSIM_RAM_LOG_SIZE 16384
|
||||
#define TIME_STR_SIZE 40
|
||||
|
||||
#define NANDSIM_LOG_ERR 1
|
||||
#define NANDSIM_LOG_SM 5
|
||||
#define NANDSIM_LOG_EV 10
|
||||
#define NANDSIM_LOG_DATA 15
|
||||
|
||||
extern int nandsim_log_level;
|
||||
extern int nandsim_log_output;
|
||||
|
||||
int nandsim_log_init(struct nandsim_softc *, char *);
|
||||
void nandsim_log_close(struct nandsim_softc *);
|
||||
void nandsim_log(struct nandsim_chip *, int, const char *, ...);
|
||||
|
||||
#endif /* _NANDSIM_LOG_H */
|
||||
|
389
sys/dev/nand/nandsim_swap.c
Normal file
389
sys/dev/nand/nandsim_swap.c
Normal file
@ -0,0 +1,389 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <dev/nand/nandsim_chip.h>
|
||||
#include <dev/nand/nandsim_swap.h>
|
||||
|
||||
static int init_block_state(struct chip_swap *);
|
||||
static void destroy_block_state(struct chip_swap *);
|
||||
|
||||
static int create_buffers(struct chip_swap *);
|
||||
static void destroy_buffers(struct chip_swap *);
|
||||
|
||||
static int swap_file_open(struct chip_swap *, const char *);
|
||||
static void swap_file_close(struct chip_swap *);
|
||||
static int swap_file_write(struct chip_swap *, struct block_state *);
|
||||
static int swap_file_read(struct chip_swap *, struct block_state *);
|
||||
|
||||
#define CHIP_SWAP_CMODE 0600
|
||||
#define CHIP_SWAP_BLOCKSPACES 2
|
||||
|
||||
static int
|
||||
init_block_state(struct chip_swap *swap)
|
||||
{
|
||||
struct block_state *blk_state;
|
||||
int i;
|
||||
|
||||
if (swap == NULL)
|
||||
return (-1);
|
||||
|
||||
blk_state = malloc(swap->nof_blks * sizeof(struct block_state),
|
||||
M_NANDSIM, M_WAITOK | M_ZERO);
|
||||
|
||||
for (i = 0; i < swap->nof_blks; i++)
|
||||
blk_state[i].offset = 0xffffffff;
|
||||
|
||||
swap->blk_state = blk_state;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_block_state(struct chip_swap *swap)
|
||||
{
|
||||
|
||||
if (swap == NULL)
|
||||
return;
|
||||
|
||||
if (swap->blk_state != NULL)
|
||||
free(swap->blk_state, M_NANDSIM);
|
||||
}
|
||||
|
||||
static int
|
||||
create_buffers(struct chip_swap *swap)
|
||||
{
|
||||
struct block_space *block_space;
|
||||
void *block;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) {
|
||||
block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK);
|
||||
block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK);
|
||||
block_space->blk_ptr = block;
|
||||
SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link);
|
||||
nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space,
|
||||
block);
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_buffers(struct chip_swap *swap)
|
||||
{
|
||||
struct block_space *blk_space;
|
||||
|
||||
if (swap == NULL)
|
||||
return;
|
||||
|
||||
blk_space = SLIST_FIRST(&swap->free_bs);
|
||||
while (blk_space) {
|
||||
SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
|
||||
nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
|
||||
blk_space, blk_space->blk_ptr);
|
||||
free(blk_space->blk_ptr, M_NANDSIM);
|
||||
free(blk_space, M_NANDSIM);
|
||||
blk_space = SLIST_FIRST(&swap->free_bs);
|
||||
}
|
||||
|
||||
blk_space = STAILQ_FIRST(&swap->used_bs);
|
||||
while (blk_space) {
|
||||
STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
|
||||
nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
|
||||
blk_space, blk_space->blk_ptr);
|
||||
free(blk_space->blk_ptr, M_NANDSIM);
|
||||
free(blk_space, M_NANDSIM);
|
||||
blk_space = STAILQ_FIRST(&swap->used_bs);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
swap_file_open(struct chip_swap *swap, const char *swap_file)
|
||||
{
|
||||
struct nameidata nd;
|
||||
int vfslocked, flags, error;
|
||||
|
||||
NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, swap_file,
|
||||
curthread);
|
||||
|
||||
flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC;
|
||||
|
||||
error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL);
|
||||
if (error) {
|
||||
nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file);
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
return (error);
|
||||
}
|
||||
|
||||
swap->swap_cred = crhold(curthread->td_ucred);
|
||||
vfslocked = NDHASGIANT(&nd);
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
|
||||
/* We just unlock so we hold a reference */
|
||||
VOP_UNLOCK(nd.ni_vp, 0);
|
||||
VFS_UNLOCK_GIANT(vfslocked);
|
||||
|
||||
swap->swap_vp = nd.ni_vp;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
swap_file_close(struct chip_swap *swap)
|
||||
{
|
||||
|
||||
if (swap == NULL)
|
||||
return;
|
||||
|
||||
if (swap->swap_vp == NULL)
|
||||
return;
|
||||
|
||||
vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread);
|
||||
crfree(swap->swap_cred);
|
||||
}
|
||||
|
||||
static int
|
||||
swap_file_write(struct chip_swap *swap, struct block_state *blk_state)
|
||||
{
|
||||
struct block_space *blk_space;
|
||||
struct thread *td;
|
||||
struct mount *mp;
|
||||
struct vnode *vp;
|
||||
struct uio auio;
|
||||
struct iovec aiov;
|
||||
int vfslocked;
|
||||
|
||||
if (swap == NULL || blk_state == NULL)
|
||||
return (-1);
|
||||
|
||||
blk_space = blk_state->blk_sp;
|
||||
if (blk_state->offset == -1) {
|
||||
blk_state->offset = swap->swap_offset;
|
||||
swap->swap_offset += swap->blk_size;
|
||||
}
|
||||
|
||||
nand_debug(NDBG_SIM,"saving %p[%p] at %x\n",
|
||||
blk_space, blk_space->blk_ptr, blk_state->offset);
|
||||
|
||||
bzero(&aiov, sizeof(aiov));
|
||||
bzero(&auio, sizeof(auio));
|
||||
|
||||
aiov.iov_base = blk_space->blk_ptr;
|
||||
aiov.iov_len = swap->blk_size;
|
||||
td = curthread;
|
||||
vp = swap->swap_vp;
|
||||
|
||||
auio.uio_iov = &aiov;
|
||||
auio.uio_offset = blk_state->offset;
|
||||
auio.uio_segflg = UIO_SYSSPACE;
|
||||
auio.uio_rw = UIO_WRITE;
|
||||
auio.uio_iovcnt = 1;
|
||||
auio.uio_resid = swap->blk_size;
|
||||
auio.uio_td = td;
|
||||
|
||||
vfslocked = VFS_LOCK_GIANT(vp->v_mount);
|
||||
vn_start_write(vp, &mp, V_WAIT);
|
||||
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
|
||||
VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred);
|
||||
VOP_UNLOCK(vp, 0);
|
||||
vn_finished_write(mp);
|
||||
VFS_UNLOCK_GIANT(vfslocked);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
swap_file_read(struct chip_swap *swap, struct block_state *blk_state)
|
||||
{
|
||||
struct block_space *blk_space;
|
||||
struct thread *td;
|
||||
struct vnode *vp;
|
||||
struct uio auio;
|
||||
struct iovec aiov;
|
||||
int vfslocked;
|
||||
|
||||
if (swap == NULL || blk_state == NULL)
|
||||
return (-1);
|
||||
|
||||
blk_space = blk_state->blk_sp;
|
||||
|
||||
nand_debug(NDBG_SIM,"restore %p[%p] at %x\n",
|
||||
blk_space, blk_space->blk_ptr, blk_state->offset);
|
||||
|
||||
bzero(&aiov, sizeof(aiov));
|
||||
bzero(&auio, sizeof(auio));
|
||||
|
||||
aiov.iov_base = blk_space->blk_ptr;
|
||||
aiov.iov_len = swap->blk_size;
|
||||
td = curthread;
|
||||
vp = swap->swap_vp;
|
||||
|
||||
auio.uio_iov = &aiov;
|
||||
auio.uio_offset = blk_state->offset;
|
||||
auio.uio_segflg = UIO_SYSSPACE;
|
||||
auio.uio_rw = UIO_READ;
|
||||
auio.uio_iovcnt = 1;
|
||||
auio.uio_resid = swap->blk_size;
|
||||
auio.uio_td = td;
|
||||
|
||||
vfslocked = VFS_LOCK_GIANT(vp->v_mount);
|
||||
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
|
||||
VOP_READ(vp, &auio, 0, swap->swap_cred);
|
||||
VOP_UNLOCK(vp, 0);
|
||||
VFS_UNLOCK_GIANT(vfslocked);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct chip_swap *
|
||||
nandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size)
|
||||
{
|
||||
struct chip_swap *swap;
|
||||
int err = 0;
|
||||
|
||||
if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0))
|
||||
return (NULL);
|
||||
|
||||
swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO);
|
||||
|
||||
SLIST_INIT(&swap->free_bs);
|
||||
STAILQ_INIT(&swap->used_bs);
|
||||
swap->blk_size = blk_size;
|
||||
swap->nof_blks = nof_blks;
|
||||
|
||||
err = init_block_state(swap);
|
||||
if (err) {
|
||||
nandsim_swap_destroy(swap);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
err = create_buffers(swap);
|
||||
if (err) {
|
||||
nandsim_swap_destroy(swap);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
err = swap_file_open(swap, swap_file);
|
||||
if (err) {
|
||||
nandsim_swap_destroy(swap);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (swap);
|
||||
}
|
||||
|
||||
void
|
||||
nandsim_swap_destroy(struct chip_swap *swap)
|
||||
{
|
||||
|
||||
if (swap == NULL)
|
||||
return;
|
||||
|
||||
destroy_block_state(swap);
|
||||
destroy_buffers(swap);
|
||||
swap_file_close(swap);
|
||||
free(swap, M_NANDSIM);
|
||||
}
|
||||
|
||||
struct block_space *
|
||||
get_bs(struct chip_swap *swap, uint32_t block, uint8_t writing)
|
||||
{
|
||||
struct block_state *blk_state, *old_blk_state = NULL;
|
||||
struct block_space *blk_space;
|
||||
|
||||
if (swap == NULL || (block >= swap->nof_blks))
|
||||
return (NULL);
|
||||
|
||||
blk_state = &swap->blk_state[block];
|
||||
nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status);
|
||||
|
||||
if (blk_state->status & BLOCK_ALLOCATED) {
|
||||
blk_space = blk_state->blk_sp;
|
||||
} else {
|
||||
blk_space = SLIST_FIRST(&swap->free_bs);
|
||||
if (blk_space) {
|
||||
SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
|
||||
STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
|
||||
used_link);
|
||||
} else {
|
||||
blk_space = STAILQ_FIRST(&swap->used_bs);
|
||||
old_blk_state = blk_space->blk_state;
|
||||
STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
|
||||
STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
|
||||
used_link);
|
||||
if (old_blk_state->status & BLOCK_DIRTY) {
|
||||
swap_file_write(swap, old_blk_state);
|
||||
old_blk_state->status &= ~BLOCK_DIRTY;
|
||||
old_blk_state->status |= BLOCK_SWAPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blk_space == NULL)
|
||||
return (NULL);
|
||||
|
||||
if (old_blk_state != NULL) {
|
||||
old_blk_state->status &= ~BLOCK_ALLOCATED;
|
||||
old_blk_state->blk_sp = NULL;
|
||||
}
|
||||
|
||||
blk_state->blk_sp = blk_space;
|
||||
blk_space->blk_state = blk_state;
|
||||
|
||||
if (!(blk_state->status & BLOCK_ALLOCATED)) {
|
||||
if (blk_state->status & BLOCK_SWAPPED)
|
||||
swap_file_read(swap, blk_state);
|
||||
else
|
||||
memset(blk_space->blk_ptr, 0xff, swap->blk_size);
|
||||
blk_state->status |= BLOCK_ALLOCATED;
|
||||
}
|
||||
|
||||
if (writing)
|
||||
blk_state->status |= BLOCK_DIRTY;
|
||||
|
||||
nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space,
|
||||
blk_space->blk_ptr, blk_state->status);
|
||||
|
||||
return (blk_space);
|
||||
}
|
64
sys/dev/nand/nandsim_swap.h
Normal file
64
sys/dev/nand/nandsim_swap.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NANDSIM_SWAP_CHIP_H_
|
||||
#define _NANDSIM_SWAP_CHIP_H_
|
||||
|
||||
struct block_space {
|
||||
SLIST_ENTRY(block_space) free_link;
|
||||
STAILQ_ENTRY(block_space) used_link;
|
||||
struct block_state *blk_state;
|
||||
uint8_t *blk_ptr;
|
||||
};
|
||||
|
||||
#define BLOCK_ALLOCATED 0x1
|
||||
#define BLOCK_SWAPPED 0x2
|
||||
#define BLOCK_DIRTY 0x4
|
||||
|
||||
struct block_state {
|
||||
struct block_space *blk_sp;
|
||||
uint32_t offset;
|
||||
uint8_t status;
|
||||
};
|
||||
|
||||
struct chip_swap {
|
||||
struct block_state *blk_state;
|
||||
SLIST_HEAD(,block_space) free_bs;
|
||||
STAILQ_HEAD(,block_space) used_bs;
|
||||
struct ucred *swap_cred;
|
||||
struct vnode *swap_vp;
|
||||
uint32_t swap_offset;
|
||||
uint32_t blk_size;
|
||||
uint32_t nof_blks;
|
||||
};
|
||||
|
||||
struct chip_swap *nandsim_swap_init(const char *, uint32_t, uint32_t);
|
||||
void nandsim_swap_destroy(struct chip_swap *);
|
||||
struct block_space *get_bs(struct chip_swap *, uint32_t, uint8_t);
|
||||
|
||||
#endif /* _NANDSIM_SWAP_CHIP_H_ */
|
165
sys/dev/nand/nfc_if.m
Normal file
165
sys/dev/nand/nfc_if.m
Normal file
@ -0,0 +1,165 @@
|
||||
#-
|
||||
# Copyright (C) 2009-2012 Semihalf
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
# NAND controller interface description
|
||||
#
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <dev/nand/nand.h>
|
||||
|
||||
INTERFACE nfc;
|
||||
|
||||
CODE {
|
||||
static int nfc_default_method(device_t dev)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int nfc_softecc_get(device_t dev, void *buf, int pagesize,
|
||||
void *ecc, int *needwrite)
|
||||
{
|
||||
*needwrite = 1;
|
||||
return (nand_softecc_get(dev, buf, pagesize, ecc));
|
||||
}
|
||||
|
||||
static int nfc_softecc_correct(device_t dev, void *buf, int pagesize,
|
||||
void *readecc, void *calcecc)
|
||||
{
|
||||
return (nand_softecc_correct(dev, buf, pagesize, readecc,
|
||||
calcecc));
|
||||
}
|
||||
};
|
||||
|
||||
# Send command to a NAND chip
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int send_command {
|
||||
device_t dev;
|
||||
uint8_t command;
|
||||
};
|
||||
|
||||
# Send address to a NAND chip
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int send_address {
|
||||
device_t dev;
|
||||
uint8_t address;
|
||||
};
|
||||
|
||||
# Read byte
|
||||
#
|
||||
# Return values:
|
||||
# byte read
|
||||
#
|
||||
METHOD uint8_t read_byte {
|
||||
device_t dev;
|
||||
};
|
||||
|
||||
# Write byte
|
||||
#
|
||||
METHOD void write_byte {
|
||||
device_t dev;
|
||||
uint8_t byte;
|
||||
};
|
||||
|
||||
# Read word
|
||||
#
|
||||
# Return values:
|
||||
# word read
|
||||
#
|
||||
METHOD uint16_t read_word {
|
||||
device_t dev;
|
||||
};
|
||||
|
||||
# Write word
|
||||
#
|
||||
METHOD void write_word {
|
||||
device_t dev;
|
||||
uint16_t word;
|
||||
};
|
||||
|
||||
# Read buf
|
||||
#
|
||||
METHOD void read_buf {
|
||||
device_t dev;
|
||||
void *buf;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
# Write buf
|
||||
#
|
||||
METHOD void write_buf {
|
||||
device_t dev;
|
||||
void *buf;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
# Select CS
|
||||
#
|
||||
METHOD int select_cs {
|
||||
device_t dev;
|
||||
uint8_t cs;
|
||||
};
|
||||
|
||||
# Read ready/busy signal
|
||||
#
|
||||
METHOD int read_rnb {
|
||||
device_t dev;
|
||||
};
|
||||
|
||||
# Start command
|
||||
#
|
||||
# Return values:
|
||||
# 0: Success
|
||||
#
|
||||
METHOD int start_command {
|
||||
device_t dev;
|
||||
} DEFAULT nfc_default_method;
|
||||
|
||||
# Generate ECC or get it from H/W
|
||||
#
|
||||
METHOD int get_ecc {
|
||||
device_t dev;
|
||||
void *buf;
|
||||
int pagesize;
|
||||
void *ecc;
|
||||
int *needwrite;
|
||||
} DEFAULT nfc_softecc_get;
|
||||
|
||||
# Correct ECC
|
||||
#
|
||||
METHOD int correct_ecc {
|
||||
device_t dev;
|
||||
void *buf;
|
||||
int pagesize;
|
||||
void *readecc;
|
||||
void *calcecc;
|
||||
} DEFAULT nfc_softecc_correct;
|
236
sys/dev/nand/nfc_mv.c
Normal file
236
sys/dev/nand/nfc_mv.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Integrated NAND controller driver */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/fdt.h>
|
||||
#include <arm/mv/mvvar.h>
|
||||
#include <arm/mv/mvwin.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandbus.h>
|
||||
#include "nfc_if.h"
|
||||
|
||||
#define MV_NAND_DATA (0x00)
|
||||
#define MV_NAND_COMMAND (0x01)
|
||||
#define MV_NAND_ADDRESS (0x02)
|
||||
|
||||
struct mv_nand_softc {
|
||||
struct nand_softc nand_dev;
|
||||
bus_space_handle_t sc_handle;
|
||||
bus_space_tag_t sc_tag;
|
||||
struct resource *res;
|
||||
int rid;
|
||||
};
|
||||
|
||||
static int mv_nand_attach(device_t);
|
||||
static int mv_nand_probe(device_t);
|
||||
static int mv_nand_send_command(device_t, uint8_t);
|
||||
static int mv_nand_send_address(device_t, uint8_t);
|
||||
static uint8_t mv_nand_read_byte(device_t);
|
||||
static void mv_nand_read_buf(device_t, void *, uint32_t);
|
||||
static void mv_nand_write_buf(device_t, void *, uint32_t);
|
||||
static int mv_nand_select_cs(device_t, uint8_t);
|
||||
static int mv_nand_read_rnb(device_t);
|
||||
|
||||
static device_method_t mv_nand_methods[] = {
|
||||
DEVMETHOD(device_probe, mv_nand_probe),
|
||||
DEVMETHOD(device_attach, mv_nand_attach),
|
||||
|
||||
DEVMETHOD(nfc_send_command, mv_nand_send_command),
|
||||
DEVMETHOD(nfc_send_address, mv_nand_send_address),
|
||||
DEVMETHOD(nfc_read_byte, mv_nand_read_byte),
|
||||
DEVMETHOD(nfc_read_buf, mv_nand_read_buf),
|
||||
DEVMETHOD(nfc_write_buf, mv_nand_write_buf),
|
||||
DEVMETHOD(nfc_select_cs, mv_nand_select_cs),
|
||||
DEVMETHOD(nfc_read_rnb, mv_nand_read_rnb),
|
||||
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static driver_t mv_nand_driver = {
|
||||
"nand",
|
||||
mv_nand_methods,
|
||||
sizeof(struct mv_nand_softc),
|
||||
};
|
||||
|
||||
static devclass_t mv_nand_devclass;
|
||||
DRIVER_MODULE(mv_nand, localbus, mv_nand_driver, mv_nand_devclass, 0, 0);
|
||||
|
||||
static int
|
||||
mv_nand_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (!ofw_bus_is_compatible(dev, "mrvl,nfc"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "Marvell NAND controller");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
mv_nand_attach(device_t dev)
|
||||
{
|
||||
struct mv_nand_softc *sc;
|
||||
int err;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
|
||||
RF_ACTIVE);
|
||||
if (sc->res == NULL) {
|
||||
device_printf(dev, "could not allocate resources!\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
sc->sc_tag = rman_get_bustag(sc->res);
|
||||
sc->sc_handle = rman_get_bushandle(sc->res);
|
||||
|
||||
nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL);
|
||||
|
||||
err = nandbus_create(dev);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
mv_nand_send_command(device_t dev, uint8_t command)
|
||||
{
|
||||
struct mv_nand_softc *sc;
|
||||
|
||||
nand_debug(NDBG_DRV,"mv_nand: send command %x", command);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_COMMAND, command);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
mv_nand_send_address(device_t dev, uint8_t addr)
|
||||
{
|
||||
struct mv_nand_softc *sc;
|
||||
|
||||
nand_debug(NDBG_DRV,"mv_nand: send address %x", addr);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_ADDRESS, addr);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
mv_nand_read_byte(device_t dev)
|
||||
{
|
||||
struct mv_nand_softc *sc;
|
||||
uint8_t data;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
data = bus_space_read_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA);
|
||||
|
||||
nand_debug(NDBG_DRV,"mv_nand: read %x", data);
|
||||
|
||||
return (data);
|
||||
}
|
||||
|
||||
static void
|
||||
mv_nand_read_buf(device_t dev, void* buf, uint32_t len)
|
||||
{
|
||||
struct mv_nand_softc *sc;
|
||||
int i;
|
||||
uint8_t *b = (uint8_t*)buf;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
b[i] = bus_space_read_1(sc->sc_tag, sc->sc_handle,
|
||||
MV_NAND_DATA);
|
||||
#ifdef NAND_DEBUG
|
||||
if (!(i % 16))
|
||||
printf("%s", i == 0 ? "mv_nand:\n" : "\n");
|
||||
printf(" %x", b[i]);
|
||||
if (i == len - 1)
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mv_nand_write_buf(device_t dev, void* buf, uint32_t len)
|
||||
{
|
||||
struct mv_nand_softc *sc;
|
||||
int i;
|
||||
uint8_t *b = (uint8_t*)buf;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
#ifdef NAND_DEBUG
|
||||
if (!(i % 16))
|
||||
printf("%s", i == 0 ? "mv_nand:\n" : "\n");
|
||||
printf(" %x", b[i]);
|
||||
if (i == len - 1)
|
||||
printf("\n");
|
||||
#endif
|
||||
bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA,
|
||||
b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mv_nand_select_cs(device_t dev, uint8_t cs)
|
||||
{
|
||||
|
||||
if (cs > 0)
|
||||
return (ENODEV);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
mv_nand_read_rnb(device_t dev)
|
||||
{
|
||||
|
||||
/* no-op */
|
||||
return (0); /* ready */
|
||||
}
|
621
sys/fs/nandfs/bmap.c
Normal file
621
sys/fs/nandfs/bmap.c
Normal file
@ -0,0 +1,621 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/signalvar.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/lockf.h>
|
||||
#include <sys/ktr.h>
|
||||
#include <sys/kdb.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/vnode_pager.h>
|
||||
|
||||
#include <machine/_inttypes.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/vnode_pager.h>
|
||||
|
||||
#include "nandfs_mount.h"
|
||||
#include "nandfs.h"
|
||||
#include "nandfs_subr.h"
|
||||
#include "bmap.h"
|
||||
|
||||
static int bmap_getlbns(struct nandfs_node *, nandfs_lbn_t,
|
||||
struct nandfs_indir *, int *);
|
||||
|
||||
int
|
||||
bmap_lookup(struct nandfs_node *node, nandfs_lbn_t lblk, nandfs_daddr_t *vblk)
|
||||
{
|
||||
struct nandfs_inode *ip;
|
||||
struct nandfs_indir a[NIADDR + 1], *ap;
|
||||
nandfs_daddr_t daddr;
|
||||
struct buf *bp;
|
||||
int error;
|
||||
int num, *nump;
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk %jx enter\n", __func__, node, lblk));
|
||||
ip = &node->nn_inode;
|
||||
|
||||
ap = a;
|
||||
nump = #
|
||||
|
||||
error = bmap_getlbns(node, lblk, ap, nump);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (num == 0) {
|
||||
*vblk = ip->i_db[lblk];
|
||||
return (0);
|
||||
}
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx trying ip->i_ib[%x]\n", __func__,
|
||||
node, lblk, ap->in_off));
|
||||
daddr = ip->i_ib[ap->in_off];
|
||||
for (bp = NULL, ++ap; --num; ap++) {
|
||||
if (daddr == 0) {
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with "
|
||||
"vblk 0\n", __func__, node, lblk));
|
||||
*vblk = 0;
|
||||
return (0);
|
||||
}
|
||||
if (ap->in_lbn == lblk) {
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx ap->in_lbn=%jx "
|
||||
"returning address of indirect block (%jx)\n",
|
||||
__func__, node, lblk, ap->in_lbn, daddr));
|
||||
*vblk = daddr;
|
||||
return (0);
|
||||
}
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx reading block "
|
||||
"ap->in_lbn=%jx\n", __func__, node, lblk, ap->in_lbn));
|
||||
|
||||
error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off];
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with %jx\n", __func__,
|
||||
node, lblk, daddr));
|
||||
*vblk = daddr;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bmap_dirty_meta(struct nandfs_node *node, nandfs_lbn_t lblk, int force)
|
||||
{
|
||||
struct nandfs_indir a[NIADDR+1], *ap;
|
||||
#ifdef DEBUG
|
||||
nandfs_daddr_t daddr;
|
||||
#endif
|
||||
struct buf *bp;
|
||||
int error;
|
||||
int num, *nump;
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx\n", __func__, node, lblk));
|
||||
|
||||
ap = a;
|
||||
nump = #
|
||||
|
||||
error = bmap_getlbns(node, lblk, ap, nump);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Direct block, nothing to do
|
||||
*/
|
||||
if (num == 0)
|
||||
return (0);
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p reading blocks\n", __func__, node));
|
||||
|
||||
for (bp = NULL, ++ap; --num; ap++) {
|
||||
error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off];
|
||||
MPASS(daddr != 0 || node->nn_ino == 3);
|
||||
#endif
|
||||
|
||||
error = nandfs_dirty_buf_meta(bp, force);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bmap_insert_block(struct nandfs_node *node, nandfs_lbn_t lblk,
|
||||
nandfs_daddr_t vblk)
|
||||
{
|
||||
struct nandfs_inode *ip;
|
||||
struct nandfs_indir a[NIADDR+1], *ap;
|
||||
struct buf *bp;
|
||||
nandfs_daddr_t daddr;
|
||||
int error;
|
||||
int num, *nump, i;
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx\n", __func__, node, lblk,
|
||||
vblk));
|
||||
|
||||
ip = &node->nn_inode;
|
||||
|
||||
ap = a;
|
||||
nump = #
|
||||
|
||||
error = bmap_getlbns(node, lblk, ap, nump);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx got num=%d\n", __func__,
|
||||
node, lblk, vblk, num));
|
||||
|
||||
if (num == 0) {
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx direct block\n", __func__,
|
||||
node, lblk));
|
||||
ip->i_db[lblk] = vblk;
|
||||
return (0);
|
||||
}
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block level %d\n",
|
||||
__func__, node, lblk, ap->in_off));
|
||||
|
||||
if (num == 1) {
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block: inserting "
|
||||
"%jx as vblk for indirect block %d\n", __func__, node,
|
||||
lblk, vblk, ap->in_off));
|
||||
ip->i_ib[ap->in_off] = vblk;
|
||||
return (0);
|
||||
}
|
||||
|
||||
bp = NULL;
|
||||
daddr = ip->i_ib[a[0].in_off];
|
||||
for (i = 1; i < num; i++) {
|
||||
if (bp)
|
||||
brelse(bp);
|
||||
if (daddr == 0) {
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx create "
|
||||
"block %jx %d\n", __func__, node, lblk, vblk,
|
||||
a[i].in_lbn, a[i].in_off));
|
||||
error = nandfs_bcreate_meta(node, a[i].in_lbn, NOCRED,
|
||||
0, &bp);
|
||||
if (error)
|
||||
return (error);
|
||||
} else {
|
||||
DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx read "
|
||||
"block %jx %d\n", __func__, node, daddr, vblk,
|
||||
a[i].in_lbn, a[i].in_off));
|
||||
error = nandfs_bread_meta(node, a[i].in_lbn, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
daddr = ((nandfs_daddr_t *)bp->b_data)[a[i].in_off];
|
||||
}
|
||||
i--;
|
||||
|
||||
DPRINTF(BMAP,
|
||||
("%s: bmap node %p lblk=%jx vblk=%jx inserting vblk level %d at "
|
||||
"offset %d at %jx\n", __func__, node, lblk, vblk, i, a[i].in_off,
|
||||
daddr));
|
||||
|
||||
if (!bp) {
|
||||
nandfs_error("%s: cannot find indirect block\n", __func__);
|
||||
return (-1);
|
||||
}
|
||||
((nandfs_daddr_t *)bp->b_data)[a[i].in_off] = vblk;
|
||||
|
||||
error = nandfs_dirty_buf_meta(bp, 0);
|
||||
if (error) {
|
||||
nandfs_warning("%s: dirty failed buf: %p\n", __func__, bp);
|
||||
return (error);
|
||||
}
|
||||
DPRINTF(BMAP, ("%s: exiting node %p lblk=%jx vblk=%jx\n", __func__,
|
||||
node, lblk, vblk));
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
CTASSERT(NIADDR <= 3);
|
||||
#define SINGLE 0 /* index of single indirect block */
|
||||
#define DOUBLE 1 /* index of double indirect block */
|
||||
#define TRIPLE 2 /* index of triple indirect block */
|
||||
|
||||
static __inline nandfs_lbn_t
|
||||
lbn_offset(struct nandfs_device *fsdev, int level)
|
||||
{
|
||||
nandfs_lbn_t res;
|
||||
|
||||
for (res = 1; level > 0; level--)
|
||||
res *= MNINDIR(fsdev);
|
||||
return (res);
|
||||
}
|
||||
|
||||
static nandfs_lbn_t
|
||||
blocks_inside(struct nandfs_device *fsdev, int level, struct nandfs_indir *nip)
|
||||
{
|
||||
nandfs_lbn_t blocks;
|
||||
|
||||
for (blocks = 1; level >= SINGLE; level--, nip++) {
|
||||
MPASS(nip->in_off >= 0 && nip->in_off < MNINDIR(fsdev));
|
||||
blocks += nip->in_off * lbn_offset(fsdev, level);
|
||||
}
|
||||
|
||||
return (blocks);
|
||||
}
|
||||
|
||||
static int
|
||||
bmap_truncate_indirect(struct nandfs_node *node, int level, nandfs_lbn_t *left,
|
||||
int *cleaned, struct nandfs_indir *ap, struct nandfs_indir *fp,
|
||||
nandfs_daddr_t *copy)
|
||||
{
|
||||
struct buf *bp;
|
||||
nandfs_lbn_t i, lbn, nlbn, factor, tosub;
|
||||
struct nandfs_device *fsdev;
|
||||
int error, lcleaned, modified;
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p level %d left %jx\n", __func__,
|
||||
node, level, *left));
|
||||
|
||||
fsdev = node->nn_nandfsdev;
|
||||
|
||||
MPASS(ap->in_off >= 0 && ap->in_off < MNINDIR(fsdev));
|
||||
|
||||
factor = lbn_offset(fsdev, level);
|
||||
lbn = ap->in_lbn;
|
||||
|
||||
error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
bcopy(bp->b_data, copy, fsdev->nd_blocksize);
|
||||
bqrelse(bp);
|
||||
|
||||
modified = 0;
|
||||
|
||||
i = ap->in_off;
|
||||
|
||||
if (ap != fp)
|
||||
ap++;
|
||||
for (nlbn = lbn + 1 - i * factor; i >= 0 && *left > 0; i--,
|
||||
nlbn += factor) {
|
||||
lcleaned = 0;
|
||||
|
||||
DPRINTF(BMAP,
|
||||
("%s: node %p i=%jx nlbn=%jx left=%jx ap=%p vblk %jx\n",
|
||||
__func__, node, i, nlbn, *left, ap, copy[i]));
|
||||
|
||||
if (copy[i] == 0) {
|
||||
tosub = blocks_inside(fsdev, level - 1, ap);
|
||||
if (tosub > *left)
|
||||
tosub = 0;
|
||||
|
||||
*left -= tosub;
|
||||
} else {
|
||||
if (level > SINGLE) {
|
||||
if (ap == fp)
|
||||
ap->in_lbn = nlbn;
|
||||
|
||||
error = bmap_truncate_indirect(node, level - 1,
|
||||
left, &lcleaned, ap, fp,
|
||||
copy + MNINDIR(fsdev));
|
||||
if (error)
|
||||
return (error);
|
||||
} else {
|
||||
error = nandfs_bdestroy(node, copy[i]);
|
||||
if (error)
|
||||
return (error);
|
||||
lcleaned = 1;
|
||||
*left -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lcleaned) {
|
||||
if (level > SINGLE) {
|
||||
error = nandfs_vblock_end(fsdev, copy[i]);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
copy[i] = 0;
|
||||
modified++;
|
||||
}
|
||||
|
||||
ap = fp;
|
||||
}
|
||||
|
||||
if (i == -1)
|
||||
*cleaned = 1;
|
||||
|
||||
error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
if (modified)
|
||||
bcopy(copy, bp->b_data, fsdev->nd_blocksize);
|
||||
|
||||
error = nandfs_dirty_buf_meta(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
bmap_truncate_mapping(struct nandfs_node *node, nandfs_lbn_t lastblk,
|
||||
nandfs_lbn_t todo)
|
||||
{
|
||||
struct nandfs_inode *ip;
|
||||
struct nandfs_indir a[NIADDR + 1], f[NIADDR], *ap;
|
||||
nandfs_daddr_t indir_lbn[NIADDR];
|
||||
nandfs_daddr_t *copy;
|
||||
int error, level;
|
||||
nandfs_lbn_t left, tosub;
|
||||
struct nandfs_device *fsdev;
|
||||
int cleaned, i;
|
||||
int num, *nump;
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p lastblk %jx truncating by %jx\n", __func__,
|
||||
node, lastblk, todo));
|
||||
|
||||
ip = &node->nn_inode;
|
||||
fsdev = node->nn_nandfsdev;
|
||||
|
||||
ap = a;
|
||||
nump = #
|
||||
|
||||
error = bmap_getlbns(node, lastblk, ap, nump);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
indir_lbn[SINGLE] = -NDADDR;
|
||||
indir_lbn[DOUBLE] = indir_lbn[SINGLE] - MNINDIR(fsdev) - 1;
|
||||
indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - MNINDIR(fsdev)
|
||||
* MNINDIR(fsdev) - 1;
|
||||
|
||||
for (i = 0; i < NIADDR; i++) {
|
||||
f[i].in_off = MNINDIR(fsdev) - 1;
|
||||
f[i].in_lbn = 0xdeadbeef;
|
||||
}
|
||||
|
||||
left = todo;
|
||||
|
||||
#ifdef DEBUG
|
||||
a[num].in_off = -1;
|
||||
#endif
|
||||
|
||||
ap++;
|
||||
num -= 2;
|
||||
|
||||
if (num < 0)
|
||||
goto direct;
|
||||
|
||||
copy = malloc(MNINDIR(fsdev) * sizeof(nandfs_daddr_t) * (num + 1),
|
||||
M_NANDFSTEMP, M_WAITOK);
|
||||
|
||||
for (level = num; level >= SINGLE && left > 0; level--) {
|
||||
cleaned = 0;
|
||||
|
||||
if (ip->i_ib[level] == 0) {
|
||||
tosub = blocks_inside(fsdev, level, ap);
|
||||
if (tosub > left)
|
||||
left = 0;
|
||||
else
|
||||
left -= tosub;
|
||||
} else {
|
||||
if (ap == f)
|
||||
ap->in_lbn = indir_lbn[level];
|
||||
error = bmap_truncate_indirect(node, level, &left,
|
||||
&cleaned, ap, f, copy);
|
||||
if (error) {
|
||||
nandfs_error("%s: error %d when truncate "
|
||||
"at level %d\n", __func__, error, level);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
if (cleaned) {
|
||||
nandfs_vblock_end(fsdev, ip->i_ib[level]);
|
||||
ip->i_ib[level] = 0;
|
||||
}
|
||||
|
||||
ap = f;
|
||||
}
|
||||
|
||||
free(copy, M_NANDFSTEMP);
|
||||
|
||||
direct:
|
||||
if (num < 0)
|
||||
i = lastblk;
|
||||
else
|
||||
i = NDADDR - 1;
|
||||
|
||||
for (; i >= 0 && left > 0; i--) {
|
||||
if (ip->i_db[i] != 0) {
|
||||
error = nandfs_bdestroy(node, ip->i_db[i]);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot destroy "
|
||||
"block %jx, error %d\n", __func__,
|
||||
(uintmax_t)ip->i_db[i], error);
|
||||
return (error);
|
||||
}
|
||||
ip->i_db[i] = 0;
|
||||
}
|
||||
|
||||
left--;
|
||||
}
|
||||
|
||||
KASSERT(left == 0,
|
||||
("truncated wrong number of blocks (%jd should be 0)", left));
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
nandfs_lbn_t
|
||||
get_maxfilesize(struct nandfs_device *fsdev)
|
||||
{
|
||||
struct nandfs_indir f[NIADDR];
|
||||
nandfs_lbn_t max;
|
||||
int i;
|
||||
|
||||
max = NDADDR;
|
||||
|
||||
for (i = 0; i < NIADDR; i++) {
|
||||
f[i].in_off = MNINDIR(fsdev) - 1;
|
||||
max += blocks_inside(fsdev, i, f);
|
||||
}
|
||||
|
||||
max *= fsdev->nd_blocksize;
|
||||
|
||||
return (max);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is ufs_getlbns with minor modifications.
|
||||
*/
|
||||
/*
|
||||
* Create an array of logical block number/offset pairs which represent the
|
||||
* path of indirect blocks required to access a data block. The first "pair"
|
||||
* contains the logical block number of the appropriate single, double or
|
||||
* triple indirect block and the offset into the inode indirect block array.
|
||||
* Note, the logical block number of the inode single/double/triple indirect
|
||||
* block appears twice in the array, once with the offset into the i_ib and
|
||||
* once with the offset into the page itself.
|
||||
*/
|
||||
static int
|
||||
bmap_getlbns(struct nandfs_node *node, nandfs_lbn_t bn, struct nandfs_indir *ap, int *nump)
|
||||
{
|
||||
nandfs_daddr_t blockcnt;
|
||||
nandfs_lbn_t metalbn, realbn;
|
||||
struct nandfs_device *fsdev;
|
||||
int i, numlevels, off;
|
||||
|
||||
fsdev = node->nn_nandfsdev;
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p bn=%jx mnindir=%zd enter\n", __func__,
|
||||
node, bn, MNINDIR(fsdev)));
|
||||
|
||||
*nump = 0;
|
||||
numlevels = 0;
|
||||
realbn = bn;
|
||||
|
||||
if (bn < 0)
|
||||
bn = -bn;
|
||||
|
||||
/* The first NDADDR blocks are direct blocks. */
|
||||
if (bn < NDADDR)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Determine the number of levels of indirection. After this loop
|
||||
* is done, blockcnt indicates the number of data blocks possible
|
||||
* at the previous level of indirection, and NIADDR - i is the number
|
||||
* of levels of indirection needed to locate the requested block.
|
||||
*/
|
||||
for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
|
||||
DPRINTF(BMAP, ("%s: blockcnt=%jd i=%d bn=%jd\n", __func__,
|
||||
blockcnt, i, bn));
|
||||
if (i == 0)
|
||||
return (EFBIG);
|
||||
blockcnt *= MNINDIR(fsdev);
|
||||
if (bn < blockcnt)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate the address of the first meta-block. */
|
||||
if (realbn >= 0)
|
||||
metalbn = -(realbn - bn + NIADDR - i);
|
||||
else
|
||||
metalbn = -(-realbn - bn + NIADDR - i);
|
||||
|
||||
/*
|
||||
* At each iteration, off is the offset into the bap array which is
|
||||
* an array of disk addresses at the current level of indirection.
|
||||
* The logical block number and the offset in that block are stored
|
||||
* into the argument array.
|
||||
*/
|
||||
ap->in_lbn = metalbn;
|
||||
ap->in_off = off = NIADDR - i;
|
||||
|
||||
DPRINTF(BMAP, ("%s: initial: ap->in_lbn=%jx ap->in_off=%d\n", __func__,
|
||||
metalbn, off));
|
||||
|
||||
ap++;
|
||||
for (++numlevels; i <= NIADDR; i++) {
|
||||
/* If searching for a meta-data block, quit when found. */
|
||||
if (metalbn == realbn)
|
||||
break;
|
||||
|
||||
blockcnt /= MNINDIR(fsdev);
|
||||
off = (bn / blockcnt) % MNINDIR(fsdev);
|
||||
|
||||
++numlevels;
|
||||
ap->in_lbn = metalbn;
|
||||
ap->in_off = off;
|
||||
|
||||
DPRINTF(BMAP, ("%s: in_lbn=%jx in_off=%d\n", __func__,
|
||||
ap->in_lbn, ap->in_off));
|
||||
++ap;
|
||||
|
||||
metalbn -= -1 + off * blockcnt;
|
||||
}
|
||||
if (nump)
|
||||
*nump = numlevels;
|
||||
|
||||
DPRINTF(BMAP, ("%s: numlevels=%d\n", __func__, numlevels));
|
||||
|
||||
return (0);
|
||||
}
|
40
sys/fs/nandfs/bmap.h
Normal file
40
sys/fs/nandfs/bmap.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _BMAP_H
|
||||
#define _BMAP_H
|
||||
|
||||
#include "nandfs_fs.h"
|
||||
|
||||
int bmap_lookup(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t *);
|
||||
int bmap_insert_block(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t);
|
||||
int bmap_truncate_mapping(struct nandfs_node *, nandfs_lbn_t, nandfs_lbn_t);
|
||||
int bmap_dirty_meta(struct nandfs_node *, nandfs_lbn_t, int);
|
||||
|
||||
nandfs_lbn_t get_maxfilesize(struct nandfs_device *);
|
||||
|
||||
#endif /* _BMAP_H */
|
310
sys/fs/nandfs/nandfs.h
Normal file
310
sys/fs/nandfs/nandfs.h
Normal file
@ -0,0 +1,310 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf
|
||||
* Copyright (c) 2008, 2009 Reinoud Zandijk
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* From: NetBSD: nilfs.h,v 1.1 2009/07/18 16:31:42 reinoud
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _FS_NANDFS_NANDFS_H_
|
||||
#define _FS_NANDFS_NANDFS_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <sys/disk.h>
|
||||
#include <sys/kthread.h>
|
||||
#include "nandfs_fs.h"
|
||||
|
||||
MALLOC_DECLARE(M_NANDFSTEMP);
|
||||
|
||||
/* Debug categories */
|
||||
#define NANDFS_DEBUG_VOLUMES 0x000001
|
||||
#define NANDFS_DEBUG_BLOCK 0x000004
|
||||
#define NANDFS_DEBUG_LOCKING 0x000008
|
||||
#define NANDFS_DEBUG_NODE 0x000010
|
||||
#define NANDFS_DEBUG_LOOKUP 0x000020
|
||||
#define NANDFS_DEBUG_READDIR 0x000040
|
||||
#define NANDFS_DEBUG_TRANSLATE 0x000080
|
||||
#define NANDFS_DEBUG_STRATEGY 0x000100
|
||||
#define NANDFS_DEBUG_READ 0x000200
|
||||
#define NANDFS_DEBUG_WRITE 0x000400
|
||||
#define NANDFS_DEBUG_IFILE 0x000800
|
||||
#define NANDFS_DEBUG_ATTR 0x001000
|
||||
#define NANDFS_DEBUG_EXTATTR 0x002000
|
||||
#define NANDFS_DEBUG_ALLOC 0x004000
|
||||
#define NANDFS_DEBUG_CPFILE 0x008000
|
||||
#define NANDFS_DEBUG_DIRHASH 0x010000
|
||||
#define NANDFS_DEBUG_NOTIMPL 0x020000
|
||||
#define NANDFS_DEBUG_SHEDULE 0x040000
|
||||
#define NANDFS_DEBUG_SEG 0x080000
|
||||
#define NANDFS_DEBUG_SYNC 0x100000
|
||||
#define NANDFS_DEBUG_PARANOIA 0x200000
|
||||
#define NANDFS_DEBUG_VNCALL 0x400000
|
||||
#define NANDFS_DEBUG_BUF 0x1000000
|
||||
#define NANDFS_DEBUG_BMAP 0x2000000
|
||||
#define NANDFS_DEBUG_DAT 0x4000000
|
||||
#define NANDFS_DEBUG_GENERIC 0x8000000
|
||||
#define NANDFS_DEBUG_CLEAN 0x10000000
|
||||
|
||||
extern int nandfs_verbose;
|
||||
|
||||
#define DPRINTF(name, arg) { \
|
||||
if (nandfs_verbose & NANDFS_DEBUG_##name) {\
|
||||
printf arg;\
|
||||
};\
|
||||
}
|
||||
#define DPRINTFIF(name, cond, arg) { \
|
||||
if (nandfs_verbose & NANDFS_DEBUG_##name) { \
|
||||
if (cond) printf arg;\
|
||||
};\
|
||||
}
|
||||
|
||||
#define VFSTONANDFS(mp) ((struct nandfsmount *)((mp)->mnt_data))
|
||||
#define VTON(vp) ((struct nandfs_node *)(vp)->v_data)
|
||||
#define NTOV(xp) ((xp)->nn_vnode)
|
||||
|
||||
int nandfs_init(struct vfsconf *);
|
||||
int nandfs_uninit(struct vfsconf *);
|
||||
|
||||
extern struct vop_vector nandfs_vnodeops;
|
||||
extern struct vop_vector nandfs_system_vnodeops;
|
||||
|
||||
struct nandfs_node;
|
||||
|
||||
/* Structure and derivatives */
|
||||
struct nandfs_mdt {
|
||||
uint32_t entries_per_block;
|
||||
uint32_t entries_per_group;
|
||||
uint32_t blocks_per_group;
|
||||
uint32_t groups_per_desc_block; /* desc is super group */
|
||||
uint32_t blocks_per_desc_block; /* desc is super group */
|
||||
};
|
||||
|
||||
struct nandfs_segment {
|
||||
LIST_ENTRY(nandfs_segment) seg_link;
|
||||
|
||||
struct nandfs_device *fsdev;
|
||||
|
||||
TAILQ_HEAD(, buf) segsum;
|
||||
TAILQ_HEAD(, buf) data;
|
||||
|
||||
uint64_t seg_num;
|
||||
uint64_t seg_next;
|
||||
uint64_t start_block;
|
||||
uint32_t num_blocks;
|
||||
|
||||
uint32_t nblocks;
|
||||
uint32_t nbinfos;
|
||||
uint32_t segsum_blocks;
|
||||
uint32_t segsum_bytes;
|
||||
uint32_t bytes_left;
|
||||
char *current_off;
|
||||
};
|
||||
|
||||
struct nandfs_seginfo {
|
||||
LIST_HEAD( ,nandfs_segment) seg_list;
|
||||
struct nandfs_segment *curseg;
|
||||
struct nandfs_device *fsdev;
|
||||
uint32_t blocks;
|
||||
uint8_t reiterate;
|
||||
};
|
||||
|
||||
#define NANDFS_FSSTOR_FAILED 1
|
||||
struct nandfs_fsarea {
|
||||
int offset;
|
||||
int flags;
|
||||
int last_used;
|
||||
};
|
||||
|
||||
extern int nandfs_cleaner_enable;
|
||||
extern int nandfs_cleaner_interval;
|
||||
extern int nandfs_cleaner_segments;
|
||||
|
||||
struct nandfs_device {
|
||||
struct vnode *nd_devvp;
|
||||
struct g_consumer *nd_gconsumer;
|
||||
|
||||
struct thread *nd_syncer;
|
||||
struct thread *nd_cleaner;
|
||||
int nd_syncer_exit;
|
||||
int nd_cleaner_exit;
|
||||
|
||||
int nd_is_nand;
|
||||
|
||||
struct nandfs_fsarea nd_fsarea[NANDFS_NFSAREAS];
|
||||
int nd_last_fsarea;
|
||||
|
||||
STAILQ_HEAD(nandfs_mnts, nandfsmount) nd_mounts;
|
||||
SLIST_ENTRY(nandfs_device) nd_next_device;
|
||||
|
||||
/* FS structures */
|
||||
struct nandfs_fsdata nd_fsdata;
|
||||
struct nandfs_super_block nd_super;
|
||||
struct nandfs_segment_summary nd_last_segsum;
|
||||
struct nandfs_super_root nd_super_root;
|
||||
struct nandfs_node *nd_dat_node;
|
||||
struct nandfs_node *nd_cp_node;
|
||||
struct nandfs_node *nd_su_node;
|
||||
struct nandfs_node *nd_gc_node;
|
||||
|
||||
struct nandfs_mdt nd_dat_mdt;
|
||||
struct nandfs_mdt nd_ifile_mdt;
|
||||
|
||||
struct timespec nd_ts;
|
||||
|
||||
/* Synchronization */
|
||||
struct mtx nd_mutex;
|
||||
struct mtx nd_sync_mtx;
|
||||
struct cv nd_sync_cv;
|
||||
struct mtx nd_clean_mtx;
|
||||
struct cv nd_clean_cv;
|
||||
struct lock nd_seg_const;
|
||||
|
||||
struct nandfs_seginfo *nd_seginfo;
|
||||
|
||||
/* FS geometry */
|
||||
uint64_t nd_devsize;
|
||||
uint64_t nd_maxfilesize;
|
||||
uint32_t nd_blocksize;
|
||||
uint32_t nd_erasesize;
|
||||
|
||||
uint32_t nd_devblocksize;
|
||||
|
||||
/* Segment usage */
|
||||
uint64_t nd_clean_segs;
|
||||
uint64_t *nd_free_base;
|
||||
uint64_t nd_free_count;
|
||||
uint64_t nd_dirty_bufs;
|
||||
|
||||
/* Running values */
|
||||
uint64_t nd_seg_sequence;
|
||||
uint64_t nd_seg_num;
|
||||
uint64_t nd_next_seg_num;
|
||||
uint64_t nd_last_pseg;
|
||||
uint64_t nd_last_cno;
|
||||
uint64_t nd_last_ino;
|
||||
uint64_t nd_fakevblk;
|
||||
|
||||
int nd_mount_state;
|
||||
int nd_refcnt;
|
||||
int nd_syncing;
|
||||
int nd_cleaning;
|
||||
};
|
||||
|
||||
extern SLIST_HEAD(_nandfs_devices, nandfs_device) nandfs_devices;
|
||||
|
||||
#define NANDFS_FORCE_SYNCER 0x1
|
||||
#define NANDFS_UMOUNT 0x2
|
||||
|
||||
#define SYNCER_UMOUNT 0x0
|
||||
#define SYNCER_VFS_SYNC 0x1
|
||||
#define SYNCER_BDFLUSH 0x2
|
||||
#define SYNCER_FFORCE 0x3
|
||||
#define SYNCER_FSYNC 0x4
|
||||
#define SYNCER_ROUPD 0x5
|
||||
|
||||
static __inline int
|
||||
nandfs_writelockflags(struct nandfs_device *fsdev, int flags)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (lockstatus(&fsdev->nd_seg_const) != LK_EXCLUSIVE)
|
||||
error = lockmgr(&fsdev->nd_seg_const, flags | LK_SHARED, NULL);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
nandfs_writeunlock(struct nandfs_device *fsdev)
|
||||
{
|
||||
|
||||
if (lockstatus(&fsdev->nd_seg_const) != LK_EXCLUSIVE)
|
||||
lockmgr(&(fsdev)->nd_seg_const, LK_RELEASE, NULL);
|
||||
}
|
||||
|
||||
#define NANDFS_WRITELOCKFLAGS(fsdev, flags) nandfs_writelockflags(fsdev, flags)
|
||||
|
||||
#define NANDFS_WRITELOCK(fsdev) NANDFS_WRITELOCKFLAGS(fsdev, 0)
|
||||
|
||||
#define NANDFS_WRITEUNLOCK(fsdev) nandfs_writeunlock(fsdev)
|
||||
|
||||
#define NANDFS_WRITEASSERT(fsdev) lockmgr_assert(&(fsdev)->nd_seg_const, KA_LOCKED)
|
||||
|
||||
/* Specific mountpoint; head or a checkpoint/snapshot */
|
||||
struct nandfsmount {
|
||||
STAILQ_ENTRY(nandfsmount) nm_next_mount;
|
||||
|
||||
struct mount *nm_vfs_mountp;
|
||||
struct nandfs_device *nm_nandfsdev;
|
||||
struct nandfs_args nm_mount_args;
|
||||
struct nandfs_node *nm_ifile_node;
|
||||
|
||||
uint8_t nm_flags;
|
||||
int8_t nm_ronly;
|
||||
};
|
||||
|
||||
struct nandfs_node {
|
||||
struct vnode *nn_vnode;
|
||||
struct nandfsmount *nn_nmp;
|
||||
struct nandfs_device *nn_nandfsdev;
|
||||
struct lockf *nn_lockf;
|
||||
|
||||
uint64_t nn_ino;
|
||||
struct nandfs_inode nn_inode;
|
||||
|
||||
uint64_t nn_diroff;
|
||||
uint32_t nn_flags;
|
||||
};
|
||||
|
||||
#define IN_ACCESS 0x0001 /* Inode access time update request */
|
||||
#define IN_CHANGE 0x0002 /* Inode change time update request */
|
||||
#define IN_UPDATE 0x0004 /* Inode was written to; update mtime*/
|
||||
#define IN_MODIFIED 0x0008 /* node has been modified */
|
||||
#define IN_RENAME 0x0010 /* node is being renamed. */
|
||||
|
||||
/* File permissions. */
|
||||
#define IEXEC 0000100 /* Executable. */
|
||||
#define IWRITE 0000200 /* Writeable. */
|
||||
#define IREAD 0000400 /* Readable. */
|
||||
#define ISVTX 0001000 /* Sticky bit. */
|
||||
#define ISGID 0002000 /* Set-gid. */
|
||||
#define ISUID 0004000 /* Set-uid. */
|
||||
|
||||
#define PRINT_NODE_FLAGS \
|
||||
"\10\1IN_ACCESS\2IN_CHANGE\3IN_UPDATE\4IN_MODIFIED\5IN_RENAME"
|
||||
|
||||
#define NANDFS_GATHER(x) ((x)->b_flags |= B_00800000)
|
||||
#define NANDFS_UNGATHER(x) ((x)->b_flags &= ~B_00800000)
|
||||
#define NANDFS_ISGATHERED(x) ((x)->b_flags & B_00800000)
|
||||
|
||||
#endif /* !_FS_NANDFS_NANDFS_H_ */
|
364
sys/fs/nandfs/nandfs_alloc.c
Normal file
364
sys/fs/nandfs/nandfs_alloc.c
Normal file
@ -0,0 +1,364 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_mount.h>
|
||||
#include <fs/nandfs/nandfs.h>
|
||||
#include <fs/nandfs/nandfs_subr.h>
|
||||
|
||||
static void
|
||||
nandfs_get_desc_block_nr(struct nandfs_mdt *mdt, uint64_t desc,
|
||||
uint64_t *desc_block)
|
||||
{
|
||||
|
||||
*desc_block = desc * mdt->blocks_per_desc_block;
|
||||
}
|
||||
|
||||
static void
|
||||
nandfs_get_group_block_nr(struct nandfs_mdt *mdt, uint64_t group,
|
||||
uint64_t *group_block)
|
||||
{
|
||||
uint64_t desc, group_off;
|
||||
|
||||
desc = group / mdt->groups_per_desc_block;
|
||||
group_off = group % mdt->groups_per_desc_block;
|
||||
*group_block = desc * mdt->blocks_per_desc_block +
|
||||
1 + group_off * mdt->blocks_per_group;
|
||||
}
|
||||
|
||||
static void
|
||||
init_desc_block(struct nandfs_mdt *mdt, uint8_t *block_data)
|
||||
{
|
||||
struct nandfs_block_group_desc *desc;
|
||||
uint32_t i;
|
||||
|
||||
desc = (struct nandfs_block_group_desc *) block_data;
|
||||
for (i = 0; i < mdt->groups_per_desc_block; i++)
|
||||
desc[i].bg_nfrees = mdt->entries_per_group;
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_find_free_entry(struct nandfs_mdt *mdt, struct nandfs_node *node,
|
||||
struct nandfs_alloc_request *req)
|
||||
{
|
||||
nandfs_daddr_t desc, group, maxgroup, maxdesc, pos = 0;
|
||||
nandfs_daddr_t start_group, start_desc;
|
||||
nandfs_daddr_t desc_block, group_block;
|
||||
nandfs_daddr_t file_blocks;
|
||||
struct nandfs_block_group_desc *descriptors;
|
||||
struct buf *bp, *bp2;
|
||||
uint32_t *mask, i, mcount, msize;
|
||||
int error;
|
||||
|
||||
file_blocks = node->nn_inode.i_blocks;
|
||||
maxgroup = 0x100000000ull / mdt->entries_per_group;
|
||||
maxdesc = maxgroup / mdt->groups_per_desc_block;
|
||||
start_group = req->entrynum / mdt->entries_per_group;
|
||||
start_desc = start_group / mdt->groups_per_desc_block;
|
||||
|
||||
bp = bp2 = NULL;
|
||||
restart:
|
||||
for (desc = start_desc; desc < maxdesc; desc++) {
|
||||
nandfs_get_desc_block_nr(mdt, desc, &desc_block);
|
||||
|
||||
if (bp)
|
||||
brelse(bp);
|
||||
if (desc_block < file_blocks) {
|
||||
error = nandfs_bread(node, desc_block, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
} else {
|
||||
error = nandfs_bcreate(node, desc_block, NOCRED, 0,
|
||||
&bp);
|
||||
if (error)
|
||||
return (error);
|
||||
file_blocks++;
|
||||
init_desc_block(mdt, bp->b_data);
|
||||
}
|
||||
|
||||
descriptors = (struct nandfs_block_group_desc *) bp->b_data;
|
||||
for (group = start_group; group < mdt->groups_per_desc_block;
|
||||
group++) {
|
||||
if (descriptors[group].bg_nfrees > 0) {
|
||||
nandfs_get_group_block_nr(mdt, group,
|
||||
&group_block);
|
||||
|
||||
if (bp2)
|
||||
brelse(bp2);
|
||||
if (group_block < file_blocks) {
|
||||
error = nandfs_bread(node, group_block,
|
||||
NOCRED, 0, &bp2);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
} else {
|
||||
error = nandfs_bcreate(node,
|
||||
group_block, NOCRED, 0, &bp2);
|
||||
if (error)
|
||||
return (error);
|
||||
file_blocks++;
|
||||
}
|
||||
mask = (uint32_t *)bp2->b_data;
|
||||
msize = (sizeof(uint32_t) * __CHAR_BIT);
|
||||
mcount = mdt->entries_per_group / msize;
|
||||
for (i = 0; i < mcount; i++) {
|
||||
if (mask[i] == UINT32_MAX)
|
||||
continue;
|
||||
|
||||
pos = ffs(~mask[i]) - 1;
|
||||
pos += (msize * i);
|
||||
pos += (group * mdt->entries_per_group);
|
||||
pos += desc * group *
|
||||
mdt->groups_per_desc_block *
|
||||
mdt->entries_per_group;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
start_group = 0;
|
||||
}
|
||||
|
||||
if (start_desc != 0) {
|
||||
maxdesc = start_desc;
|
||||
start_desc = 0;
|
||||
req->entrynum = 0;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
return (ENOENT);
|
||||
|
||||
found:
|
||||
req->entrynum = pos;
|
||||
req->bp_desc = bp;
|
||||
req->bp_bitmap = bp2;
|
||||
DPRINTF(ALLOC, ("%s: desc: %p bitmap: %p entry: %#jx\n",
|
||||
__func__, req->bp_desc, req->bp_bitmap, (uintmax_t)pos));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_find_entry(struct nandfs_mdt* mdt, struct nandfs_node *nnode,
|
||||
struct nandfs_alloc_request *req)
|
||||
{
|
||||
uint64_t dblock, bblock, eblock;
|
||||
uint32_t offset;
|
||||
int error;
|
||||
|
||||
nandfs_mdt_trans_blk(mdt, req->entrynum, &dblock, &bblock, &eblock,
|
||||
&offset);
|
||||
|
||||
error = nandfs_bread(nnode, dblock, NOCRED, 0, &req->bp_desc);
|
||||
if (error) {
|
||||
brelse(req->bp_desc);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = nandfs_bread(nnode, bblock, NOCRED, 0, &req->bp_bitmap);
|
||||
if (error) {
|
||||
brelse(req->bp_desc);
|
||||
brelse(req->bp_bitmap);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = nandfs_bread(nnode, eblock, NOCRED, 0, &req->bp_entry);
|
||||
if (error) {
|
||||
brelse(req->bp_desc);
|
||||
brelse(req->bp_bitmap);
|
||||
brelse(req->bp_entry);
|
||||
return (error);
|
||||
}
|
||||
|
||||
DPRINTF(ALLOC,
|
||||
("%s: desc_buf: %p bitmap_buf %p entry_buf %p offset %x\n",
|
||||
__func__, req->bp_desc, req->bp_bitmap, req->bp_entry, offset));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
nandfs_calc_idx_entry(struct nandfs_mdt* mdt, uint32_t entrynum,
|
||||
uint64_t *group, uint64_t *bitmap_idx, uint64_t *bitmap_off)
|
||||
{
|
||||
|
||||
/* Find group_desc index */
|
||||
entrynum = entrynum %
|
||||
(mdt->entries_per_group * mdt->groups_per_desc_block);
|
||||
*group = entrynum / mdt->entries_per_group;
|
||||
/* Find bitmap index and bit offset */
|
||||
entrynum = entrynum % mdt->entries_per_group;
|
||||
*bitmap_idx = entrynum / (sizeof(uint32_t) * __CHAR_BIT);
|
||||
*bitmap_off = entrynum % (sizeof(uint32_t) * __CHAR_BIT);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_free_entry(struct nandfs_mdt* mdt, struct nandfs_alloc_request *req)
|
||||
{
|
||||
struct nandfs_block_group_desc *descriptors;
|
||||
uint64_t bitmap_idx, bitmap_off;
|
||||
uint64_t group;
|
||||
uint32_t *mask, maskrw;
|
||||
|
||||
nandfs_calc_idx_entry(mdt, req->entrynum, &group, &bitmap_idx,
|
||||
&bitmap_off);
|
||||
|
||||
DPRINTF(ALLOC, ("nandfs_free_entry: req->entrynum=%jx bitmap_idx=%jx"
|
||||
" bitmap_off=%jx group=%jx\n", (uintmax_t)req->entrynum,
|
||||
(uintmax_t)bitmap_idx, (uintmax_t)bitmap_off, (uintmax_t)group));
|
||||
|
||||
/* Update counter of free entries for group */
|
||||
descriptors = (struct nandfs_block_group_desc *) req->bp_desc->b_data;
|
||||
descriptors[group].bg_nfrees++;
|
||||
|
||||
/* Set bit to indicate that entry is taken */
|
||||
mask = (uint32_t *)req->bp_bitmap->b_data;
|
||||
maskrw = mask[bitmap_idx];
|
||||
KASSERT(maskrw & (1 << bitmap_off), ("freeing unallocated vblock"));
|
||||
maskrw &= ~(1 << bitmap_off);
|
||||
mask[bitmap_idx] = maskrw;
|
||||
|
||||
/* Make descriptor, bitmap and entry buffer dirty */
|
||||
if (nandfs_dirty_buf(req->bp_desc, 0) == 0) {
|
||||
nandfs_dirty_buf(req->bp_bitmap, 1);
|
||||
nandfs_dirty_buf(req->bp_entry, 1);
|
||||
} else {
|
||||
brelse(req->bp_bitmap);
|
||||
brelse(req->bp_entry);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_alloc_entry(struct nandfs_mdt* mdt, struct nandfs_alloc_request *req)
|
||||
{
|
||||
struct nandfs_block_group_desc *descriptors;
|
||||
uint64_t bitmap_idx, bitmap_off;
|
||||
uint64_t group;
|
||||
uint32_t *mask, maskrw;
|
||||
|
||||
nandfs_calc_idx_entry(mdt, req->entrynum, &group, &bitmap_idx,
|
||||
&bitmap_off);
|
||||
|
||||
DPRINTF(ALLOC, ("nandfs_alloc_entry: req->entrynum=%jx bitmap_idx=%jx"
|
||||
" bitmap_off=%jx group=%jx\n", (uintmax_t)req->entrynum,
|
||||
(uintmax_t)bitmap_idx, (uintmax_t)bitmap_off, (uintmax_t)group));
|
||||
|
||||
/* Update counter of free entries for group */
|
||||
descriptors = (struct nandfs_block_group_desc *) req->bp_desc->b_data;
|
||||
descriptors[group].bg_nfrees--;
|
||||
|
||||
/* Clear bit to indicate that entry is free */
|
||||
mask = (uint32_t *)req->bp_bitmap->b_data;
|
||||
maskrw = mask[bitmap_idx];
|
||||
maskrw |= 1 << bitmap_off;
|
||||
mask[bitmap_idx] = maskrw;
|
||||
|
||||
/* Make descriptor, bitmap and entry buffer dirty */
|
||||
if (nandfs_dirty_buf(req->bp_desc, 0) == 0) {
|
||||
nandfs_dirty_buf(req->bp_bitmap, 1);
|
||||
nandfs_dirty_buf(req->bp_entry, 1);
|
||||
} else {
|
||||
brelse(req->bp_bitmap);
|
||||
brelse(req->bp_entry);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nandfs_abort_entry(struct nandfs_alloc_request *req)
|
||||
{
|
||||
|
||||
brelse(req->bp_desc);
|
||||
brelse(req->bp_bitmap);
|
||||
brelse(req->bp_entry);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_entry_block(struct nandfs_mdt *mdt, struct nandfs_node *node,
|
||||
struct nandfs_alloc_request *req, uint32_t *entry, int create)
|
||||
{
|
||||
struct buf *bp;
|
||||
nandfs_lbn_t blocknr;
|
||||
int error;
|
||||
|
||||
/* Find buffer number for given entry */
|
||||
nandfs_mdt_trans(mdt, req->entrynum, &blocknr, entry);
|
||||
DPRINTF(ALLOC, ("%s: ino %#jx entrynum:%#jx block:%#jx entry:%x\n",
|
||||
__func__, (uintmax_t)node->nn_ino, (uintmax_t)req->entrynum,
|
||||
(uintmax_t)blocknr, *entry));
|
||||
|
||||
/* Read entry block or create if 'create' parameter is not zero */
|
||||
bp = NULL;
|
||||
|
||||
if (blocknr < node->nn_inode.i_blocks)
|
||||
error = nandfs_bread(node, blocknr, NOCRED, 0, &bp);
|
||||
else if (create)
|
||||
error = nandfs_bcreate(node, blocknr, NOCRED, 0, &bp);
|
||||
else
|
||||
error = E2BIG;
|
||||
|
||||
if (error) {
|
||||
DPRINTF(ALLOC, ("%s: ino %#jx block %#jx entry %x error %d\n",
|
||||
__func__, (uintmax_t)node->nn_ino, (uintmax_t)blocknr,
|
||||
*entry, error));
|
||||
if (bp)
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
MPASS(nandfs_vblk_get(bp) != 0 || node->nn_ino == NANDFS_DAT_INO);
|
||||
|
||||
req->bp_entry = bp;
|
||||
return (0);
|
||||
}
|
230
sys/fs/nandfs/nandfs_bmap.c
Normal file
230
sys/fs/nandfs/nandfs_bmap.c
Normal file
@ -0,0 +1,230 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf
|
||||
* Copyright (c) 2008, 2009 Reinoud Zandijk
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/signalvar.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/lockf.h>
|
||||
#include <sys/ktr.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/vnode_pager.h>
|
||||
|
||||
#include <machine/_inttypes.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/vnode_pager.h>
|
||||
|
||||
#include "nandfs_mount.h"
|
||||
#include "nandfs.h"
|
||||
#include "nandfs_subr.h"
|
||||
#include "bmap.h"
|
||||
|
||||
nandfs_lbn_t
|
||||
nandfs_get_maxfilesize(struct nandfs_device *fsdev)
|
||||
{
|
||||
|
||||
return (get_maxfilesize(fsdev));
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_bmap_lookup(struct nandfs_node *node, nandfs_lbn_t lblk,
|
||||
nandfs_daddr_t *vblk)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (node->nn_ino == NANDFS_GC_INO && lblk >= 0)
|
||||
*vblk = lblk;
|
||||
else
|
||||
error = bmap_lookup(node, lblk, vblk);
|
||||
|
||||
DPRINTF(TRANSLATE, ("%s: error %d ino %#jx lblocknr %#jx -> %#jx\n",
|
||||
__func__, error, (uintmax_t)node->nn_ino, (uintmax_t)lblk,
|
||||
(uintmax_t)*vblk));
|
||||
|
||||
if (error)
|
||||
nandfs_error("%s: returned %d", __func__, error);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_bmap_insert_block(struct nandfs_node *node, nandfs_lbn_t lblk,
|
||||
struct buf *bp)
|
||||
{
|
||||
struct nandfs_device *fsdev;
|
||||
nandfs_daddr_t vblk;
|
||||
int error;
|
||||
|
||||
fsdev = node->nn_nandfsdev;
|
||||
|
||||
vblk = 0;
|
||||
if (node->nn_ino != NANDFS_DAT_INO) {
|
||||
error = nandfs_vblock_alloc(fsdev, &vblk);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
nandfs_buf_set(bp, NANDFS_VBLK_ASSIGNED);
|
||||
nandfs_vblk_set(bp, vblk);
|
||||
|
||||
error = bmap_insert_block(node, lblk, vblk);
|
||||
if (error) {
|
||||
nandfs_vblock_free(fsdev, vblk);
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_bmap_dirty_blocks(struct nandfs_node *node, struct buf *bp, int force)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = bmap_dirty_meta(node, bp->b_lblkno, force);
|
||||
if (error)
|
||||
nandfs_error("%s: cannot dirty buffer %p\n",
|
||||
__func__, bp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_bmap_update_mapping(struct nandfs_node *node, nandfs_lbn_t lblk,
|
||||
nandfs_daddr_t blknr)
|
||||
{
|
||||
int error;
|
||||
|
||||
DPRINTF(BMAP,
|
||||
("%s: node: %p ino: %#jx lblk: %#jx vblk: %#jx\n",
|
||||
__func__, node, (uintmax_t)node->nn_ino, (uintmax_t)lblk,
|
||||
(uintmax_t)blknr));
|
||||
|
||||
error = bmap_insert_block(node, lblk, blknr);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_bmap_update_block(struct nandfs_node *node, struct buf *bp,
|
||||
nandfs_lbn_t blknr)
|
||||
{
|
||||
nandfs_lbn_t lblk;
|
||||
int error;
|
||||
|
||||
lblk = bp->b_lblkno;
|
||||
nandfs_vblk_set(bp, blknr);
|
||||
|
||||
DPRINTF(BMAP, ("%s: node: %p ino: %#jx bp: %p lblk: %#jx blk: %#jx\n",
|
||||
__func__, node, (uintmax_t)node->nn_ino, bp,
|
||||
(uintmax_t)lblk, (uintmax_t)blknr));
|
||||
|
||||
error = nandfs_bmap_update_mapping(node, lblk, blknr);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot update lblk:%jx to blk:%jx for "
|
||||
"node:%p, error:%d\n", __func__, (uintmax_t)lblk,
|
||||
(uintmax_t)blknr, node, error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_bmap_update_dat(struct nandfs_node *node, nandfs_daddr_t oldblk,
|
||||
struct buf *bp)
|
||||
{
|
||||
struct nandfs_device *fsdev;
|
||||
nandfs_daddr_t vblk = 0;
|
||||
int error;
|
||||
|
||||
if (node->nn_ino == NANDFS_DAT_INO)
|
||||
return (0);
|
||||
|
||||
if (nandfs_buf_check(bp, NANDFS_VBLK_ASSIGNED)) {
|
||||
nandfs_buf_clear(bp, NANDFS_VBLK_ASSIGNED);
|
||||
return (0);
|
||||
}
|
||||
|
||||
fsdev = node->nn_nandfsdev;
|
||||
|
||||
/* First alloc new virtual block.... */
|
||||
error = nandfs_vblock_alloc(fsdev, &vblk);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
error = nandfs_bmap_update_block(node, bp, vblk);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Then we can end up with old one */
|
||||
nandfs_vblock_end(fsdev, oldblk);
|
||||
|
||||
DPRINTF(BMAP,
|
||||
("%s: ino %#jx block %#jx: update vblk %#jx to %#jx\n",
|
||||
__func__, (uintmax_t)node->nn_ino, (uintmax_t)bp->b_lblkno,
|
||||
(uintmax_t)oldblk, (uintmax_t)vblk));
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_bmap_truncate_mapping(struct nandfs_node *node, nandfs_lbn_t oblk,
|
||||
nandfs_lbn_t nblk)
|
||||
{
|
||||
nandfs_lbn_t todo;
|
||||
int error;
|
||||
|
||||
todo = oblk - nblk;
|
||||
|
||||
DPRINTF(BMAP, ("%s: node %p oblk %jx nblk %jx truncate by %jx\n",
|
||||
__func__, node, oblk, nblk, todo));
|
||||
|
||||
error = bmap_truncate_mapping(node, oblk, todo);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (error);
|
||||
}
|
83
sys/fs/nandfs/nandfs_buffer.c
Normal file
83
sys/fs/nandfs/nandfs_buffer.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_mount.h>
|
||||
#include <fs/nandfs/nandfs.h>
|
||||
#include <fs/nandfs/nandfs_subr.h>
|
||||
|
||||
struct buf *
|
||||
nandfs_geteblk(int size, int flags)
|
||||
{
|
||||
struct buf *bp;
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* Right now we can call geteblk with GB_NOWAIT_BD flag, which means
|
||||
* it can return NULL. But we cannot afford to get NULL, hence this panic.
|
||||
*/
|
||||
bp = geteblk(size, flags);
|
||||
if (bp == NULL)
|
||||
panic("geteblk returned NULL");
|
||||
|
||||
return (bp);
|
||||
}
|
||||
|
||||
void
|
||||
nandfs_dirty_bufs_increment(struct nandfs_device *fsdev)
|
||||
{
|
||||
|
||||
mtx_lock(&fsdev->nd_mutex);
|
||||
KASSERT(fsdev->nd_dirty_bufs >= 0, ("negative nd_dirty_bufs"));
|
||||
fsdev->nd_dirty_bufs++;
|
||||
mtx_unlock(&fsdev->nd_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
nandfs_dirty_bufs_decrement(struct nandfs_device *fsdev)
|
||||
{
|
||||
|
||||
mtx_lock(&fsdev->nd_mutex);
|
||||
KASSERT(fsdev->nd_dirty_bufs > 0,
|
||||
("decrementing not-positive nd_dirty_bufs"));
|
||||
fsdev->nd_dirty_bufs--;
|
||||
mtx_unlock(&fsdev->nd_mutex);
|
||||
}
|
621
sys/fs/nandfs/nandfs_cleaner.c
Normal file
621
sys/fs/nandfs/nandfs_cleaner.c
Normal file
@ -0,0 +1,621 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_mount.h>
|
||||
#include <fs/nandfs/nandfs.h>
|
||||
#include <fs/nandfs/nandfs_subr.h>
|
||||
|
||||
#define NANDFS_CLEANER_KILL 1
|
||||
|
||||
static void nandfs_cleaner(struct nandfs_device *);
|
||||
static int nandfs_cleaner_clean_segments(struct nandfs_device *,
|
||||
struct nandfs_vinfo *, uint32_t, struct nandfs_period *, uint32_t,
|
||||
struct nandfs_bdesc *, uint32_t, uint64_t *, uint32_t);
|
||||
|
||||
static int
|
||||
nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
|
||||
uint64_t nmembs);
|
||||
|
||||
static void
|
||||
nandfs_wakeup_wait_cleaner(struct nandfs_device *fsdev, int reason)
|
||||
{
|
||||
|
||||
mtx_lock(&fsdev->nd_clean_mtx);
|
||||
if (reason == NANDFS_CLEANER_KILL)
|
||||
fsdev->nd_cleaner_exit = 1;
|
||||
if (fsdev->nd_cleaning == 0) {
|
||||
fsdev->nd_cleaning = 1;
|
||||
wakeup(&fsdev->nd_cleaning);
|
||||
}
|
||||
cv_wait(&fsdev->nd_clean_cv, &fsdev->nd_clean_mtx);
|
||||
mtx_unlock(&fsdev->nd_clean_mtx);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_start_cleaner(struct nandfs_device *fsdev)
|
||||
{
|
||||
int error;
|
||||
|
||||
MPASS(fsdev->nd_cleaner == NULL);
|
||||
|
||||
fsdev->nd_cleaner_exit = 0;
|
||||
|
||||
error = kthread_add((void(*)(void *))nandfs_cleaner, fsdev, NULL,
|
||||
&fsdev->nd_cleaner, 0, 0, "nandfs_cleaner");
|
||||
if (error)
|
||||
printf("nandfs: could not start cleaner: %d\n", error);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_stop_cleaner(struct nandfs_device *fsdev)
|
||||
{
|
||||
|
||||
MPASS(fsdev->nd_cleaner != NULL);
|
||||
nandfs_wakeup_wait_cleaner(fsdev, NANDFS_CLEANER_KILL);
|
||||
fsdev->nd_cleaner = NULL;
|
||||
|
||||
DPRINTF(CLEAN, ("cleaner stopped\n"));
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cleaner_finished(struct nandfs_device *fsdev)
|
||||
{
|
||||
int exit;
|
||||
|
||||
mtx_lock(&fsdev->nd_clean_mtx);
|
||||
fsdev->nd_cleaning = 0;
|
||||
if (!fsdev->nd_cleaner_exit) {
|
||||
DPRINTF(CLEAN, ("%s: sleep\n", __func__));
|
||||
msleep(&fsdev->nd_cleaning, &fsdev->nd_clean_mtx, PRIBIO, "-",
|
||||
hz * nandfs_cleaner_interval);
|
||||
}
|
||||
exit = fsdev->nd_cleaner_exit;
|
||||
cv_broadcast(&fsdev->nd_clean_cv);
|
||||
mtx_unlock(&fsdev->nd_clean_mtx);
|
||||
if (exit) {
|
||||
DPRINTF(CLEAN, ("%s: no longer active\n", __func__));
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
print_suinfo(struct nandfs_suinfo *suinfo, int nsegs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nsegs; i++) {
|
||||
DPRINTF(CLEAN, ("%jx %jd %c%c%c %10u\n",
|
||||
suinfo[i].nsi_num, suinfo[i].nsi_lastmod,
|
||||
(suinfo[i].nsi_flags &
|
||||
(NANDFS_SEGMENT_USAGE_ACTIVE) ? 'a' : '-'),
|
||||
(suinfo[i].nsi_flags &
|
||||
(NANDFS_SEGMENT_USAGE_DIRTY) ? 'd' : '-'),
|
||||
(suinfo[i].nsi_flags &
|
||||
(NANDFS_SEGMENT_USAGE_ERROR) ? 'e' : '-'),
|
||||
suinfo[i].nsi_blocks));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cleaner_vblock_is_alive(struct nandfs_device *fsdev,
|
||||
struct nandfs_vinfo *vinfo, struct nandfs_cpinfo *cp, uint32_t ncps)
|
||||
{
|
||||
int64_t idx, min, max;
|
||||
|
||||
if (vinfo->nvi_end >= fsdev->nd_last_cno)
|
||||
return (1);
|
||||
|
||||
if (ncps == 0)
|
||||
return (0);
|
||||
|
||||
if (vinfo->nvi_end < cp[0].nci_cno ||
|
||||
vinfo->nvi_start > cp[ncps - 1].nci_cno)
|
||||
return (0);
|
||||
|
||||
idx = min = 0;
|
||||
max = ncps - 1;
|
||||
while (min <= max) {
|
||||
idx = (min + max) / 2;
|
||||
if (vinfo->nvi_start == cp[idx].nci_cno)
|
||||
return (1);
|
||||
if (vinfo->nvi_start < cp[idx].nci_cno)
|
||||
max = idx - 1;
|
||||
else
|
||||
min = idx + 1;
|
||||
}
|
||||
|
||||
return (vinfo->nvi_end >= cp[idx].nci_cno);
|
||||
}
|
||||
|
||||
static void
|
||||
nandfs_cleaner_vinfo_mark_alive(struct nandfs_device *fsdev,
|
||||
struct nandfs_vinfo *vinfo, uint32_t nmembs, struct nandfs_cpinfo *cp,
|
||||
uint32_t ncps)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < nmembs; i++)
|
||||
vinfo[i].nvi_alive =
|
||||
nandfs_cleaner_vblock_is_alive(fsdev, &vinfo[i], cp, ncps);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cleaner_bdesc_is_alive(struct nandfs_device *fsdev,
|
||||
struct nandfs_bdesc *bdesc)
|
||||
{
|
||||
int alive;
|
||||
|
||||
alive = bdesc->bd_oblocknr == bdesc->bd_blocknr;
|
||||
if (!alive)
|
||||
MPASS(abs(bdesc->bd_oblocknr - bdesc->bd_blocknr) > 2);
|
||||
|
||||
return (alive);
|
||||
}
|
||||
|
||||
static void
|
||||
nandfs_cleaner_bdesc_mark_alive(struct nandfs_device *fsdev,
|
||||
struct nandfs_bdesc *bdesc, uint32_t nmembs)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < nmembs; i++)
|
||||
bdesc[i].bd_alive = nandfs_cleaner_bdesc_is_alive(fsdev,
|
||||
&bdesc[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
nandfs_cleaner_iterate_psegment(struct nandfs_device *fsdev,
|
||||
struct nandfs_segment_summary *segsum, union nandfs_binfo *binfo,
|
||||
nandfs_daddr_t blk, struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp)
|
||||
{
|
||||
int i;
|
||||
|
||||
DPRINTF(CLEAN, ("%s nbinfos %x\n", __func__, segsum->ss_nbinfos));
|
||||
for (i = 0; i < segsum->ss_nbinfos; i++) {
|
||||
if (binfo[i].bi_v.bi_ino == NANDFS_DAT_INO) {
|
||||
(*bdpp)->bd_oblocknr = blk + segsum->ss_nblocks -
|
||||
segsum->ss_nbinfos + i;
|
||||
/*
|
||||
* XXX Hack
|
||||
*/
|
||||
if (segsum->ss_flags & NANDFS_SS_SR)
|
||||
(*bdpp)->bd_oblocknr--;
|
||||
(*bdpp)->bd_level = binfo[i].bi_dat.bi_level;
|
||||
(*bdpp)->bd_offset = binfo[i].bi_dat.bi_blkoff;
|
||||
(*bdpp)++;
|
||||
} else {
|
||||
(*vipp)->nvi_ino = binfo[i].bi_v.bi_ino;
|
||||
(*vipp)->nvi_vblocknr = binfo[i].bi_v.bi_vblocknr;
|
||||
(*vipp)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cleaner_iterate_segment(struct nandfs_device *fsdev, uint64_t segno,
|
||||
struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp, int *select)
|
||||
{
|
||||
struct nandfs_segment_summary *segsum;
|
||||
union nandfs_binfo *binfo;
|
||||
struct buf *bp;
|
||||
uint32_t nblocks;
|
||||
nandfs_daddr_t curr, start, end;
|
||||
int error = 0;
|
||||
|
||||
nandfs_get_segment_range(fsdev, segno, &start, &end);
|
||||
|
||||
DPRINTF(CLEAN, ("%s: segno %jx start %jx end %jx\n", __func__, segno,
|
||||
start, end));
|
||||
|
||||
*select = 0;
|
||||
|
||||
for (curr = start; curr < end; curr += nblocks) {
|
||||
error = nandfs_dev_bread(fsdev, curr, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
nandfs_error("%s: couldn't load segment summary of %jx: %d\n",
|
||||
__func__, segno, error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
segsum = (struct nandfs_segment_summary *)bp->b_data;
|
||||
binfo = (union nandfs_binfo *)(bp->b_data + segsum->ss_bytes);
|
||||
|
||||
if (!nandfs_segsum_valid(segsum)) {
|
||||
brelse(bp);
|
||||
nandfs_error("nandfs: invalid summary of segment %jx\n", segno);
|
||||
return (error);
|
||||
}
|
||||
|
||||
DPRINTF(CLEAN, ("%s: %jx magic %x bytes %x nblocks %x nbinfos "
|
||||
"%x\n", __func__, segno, segsum->ss_magic, segsum->ss_bytes,
|
||||
segsum->ss_nblocks, segsum->ss_nbinfos));
|
||||
|
||||
nandfs_cleaner_iterate_psegment(fsdev, segsum, binfo, curr,
|
||||
vipp, bdpp);
|
||||
nblocks = segsum->ss_nblocks;
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
if (error == 0)
|
||||
*select = 1;
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cleaner_choose_segment(struct nandfs_device *fsdev, uint64_t **segpp,
|
||||
uint64_t nsegs, uint64_t *rseg)
|
||||
{
|
||||
struct nandfs_suinfo *suinfo;
|
||||
uint64_t i, ssegs;
|
||||
int error;
|
||||
|
||||
suinfo = malloc(sizeof(*suinfo) * nsegs, M_NANDFSTEMP,
|
||||
M_ZERO | M_WAITOK);
|
||||
|
||||
if (*rseg >= fsdev->nd_fsdata.f_nsegments)
|
||||
*rseg = 0;
|
||||
|
||||
retry:
|
||||
error = nandfs_get_segment_info_filter(fsdev, suinfo, nsegs, *rseg,
|
||||
&ssegs, NANDFS_SEGMENT_USAGE_DIRTY,
|
||||
NANDFS_SEGMENT_USAGE_ACTIVE | NANDFS_SEGMENT_USAGE_ERROR |
|
||||
NANDFS_SEGMENT_USAGE_GC);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d", __FILE__, __LINE__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ssegs == 0 && *rseg != 0) {
|
||||
*rseg = 0;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
print_suinfo(suinfo, ssegs);
|
||||
|
||||
for (i = 0; i < ssegs; i++) {
|
||||
(**segpp) = suinfo[i].nsi_num;
|
||||
(*segpp)++;
|
||||
}
|
||||
|
||||
*rseg = suinfo[i - 1].nsi_num + 1;
|
||||
out:
|
||||
free(suinfo, M_NANDFSTEMP);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cleaner_body(struct nandfs_device *fsdev, uint64_t *rseg)
|
||||
{
|
||||
struct nandfs_vinfo *vinfo, *vip, *vipi;
|
||||
struct nandfs_bdesc *bdesc, *bdp, *bdpi;
|
||||
struct nandfs_cpstat cpstat;
|
||||
struct nandfs_cpinfo *cpinfo = NULL;
|
||||
uint64_t *segnums, *segp;
|
||||
int select, selected;
|
||||
int error = 0;
|
||||
int nsegs;
|
||||
int i;
|
||||
|
||||
nsegs = nandfs_cleaner_segments;
|
||||
|
||||
vip = vinfo = malloc(sizeof(*vinfo) *
|
||||
fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP,
|
||||
M_ZERO | M_WAITOK);
|
||||
bdp = bdesc = malloc(sizeof(*bdesc) *
|
||||
fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP,
|
||||
M_ZERO | M_WAITOK);
|
||||
segp = segnums = malloc(sizeof(*segnums) * nsegs, M_NANDFSTEMP,
|
||||
M_WAITOK);
|
||||
|
||||
error = nandfs_cleaner_choose_segment(fsdev, &segp, nsegs, rseg);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d", __FILE__, __LINE__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (segnums == segp)
|
||||
goto out;
|
||||
|
||||
selected = 0;
|
||||
for (i = 0; i < segp - segnums; i++) {
|
||||
error = nandfs_cleaner_iterate_segment(fsdev, segnums[i], &vip,
|
||||
&bdp, &select);
|
||||
if (error) {
|
||||
/*
|
||||
* XXX deselect (see below)?
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
if (!select)
|
||||
segnums[i] = NANDFS_NOSEGMENT;
|
||||
else {
|
||||
error = nandfs_markgc_segment(fsdev, segnums[i]);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d\n", __FILE__, __LINE__);
|
||||
goto out;
|
||||
}
|
||||
selected++;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected == 0) {
|
||||
MPASS(vinfo == vip);
|
||||
MPASS(bdesc == bdp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = nandfs_get_cpstat(fsdev->nd_cp_node, &cpstat);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d\n", __FILE__, __LINE__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cpstat.ncp_nss != 0) {
|
||||
cpinfo = malloc(sizeof(struct nandfs_cpinfo) * cpstat.ncp_nss,
|
||||
M_NANDFSTEMP, M_WAITOK);
|
||||
error = nandfs_get_cpinfo(fsdev->nd_cp_node, 1, NANDFS_SNAPSHOT,
|
||||
cpinfo, cpstat.ncp_nss, NULL);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d\n", __FILE__, __LINE__);
|
||||
goto out_locked;
|
||||
}
|
||||
}
|
||||
|
||||
NANDFS_WRITELOCK(fsdev);
|
||||
DPRINTF(CLEAN, ("%s: got lock\n", __func__));
|
||||
|
||||
error = nandfs_get_dat_vinfo(fsdev, vinfo, vip - vinfo);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d\n", __FILE__, __LINE__);
|
||||
goto out_locked;
|
||||
}
|
||||
|
||||
nandfs_cleaner_vinfo_mark_alive(fsdev, vinfo, vip - vinfo, cpinfo,
|
||||
cpstat.ncp_nss);
|
||||
|
||||
error = nandfs_get_dat_bdescs(fsdev, bdesc, bdp - bdesc);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d\n", __FILE__, __LINE__);
|
||||
goto out_locked;
|
||||
}
|
||||
|
||||
nandfs_cleaner_bdesc_mark_alive(fsdev, bdesc, bdp - bdesc);
|
||||
|
||||
DPRINTF(CLEAN, ("got:\n"));
|
||||
for (vipi = vinfo; vipi < vip; vipi++) {
|
||||
DPRINTF(CLEAN, ("v ino %jx vblocknr %jx start %jx end %jx "
|
||||
"alive %d\n", vipi->nvi_ino, vipi->nvi_vblocknr,
|
||||
vipi->nvi_start, vipi->nvi_end, vipi->nvi_alive));
|
||||
}
|
||||
for (bdpi = bdesc; bdpi < bdp; bdpi++) {
|
||||
DPRINTF(CLEAN, ("b oblocknr %jx blocknr %jx offset %jx "
|
||||
"alive %d\n", bdpi->bd_oblocknr, bdpi->bd_blocknr,
|
||||
bdpi->bd_offset, bdpi->bd_alive));
|
||||
}
|
||||
DPRINTF(CLEAN, ("end list\n"));
|
||||
|
||||
error = nandfs_cleaner_clean_segments(fsdev, vinfo, vip - vinfo, NULL,
|
||||
0, bdesc, bdp - bdesc, segnums, segp - segnums);
|
||||
if (error)
|
||||
nandfs_error("%s:%d\n", __FILE__, __LINE__);
|
||||
|
||||
out_locked:
|
||||
NANDFS_WRITEUNLOCK(fsdev);
|
||||
out:
|
||||
free(cpinfo, M_NANDFSTEMP);
|
||||
free(segnums, M_NANDFSTEMP);
|
||||
free(bdesc, M_NANDFSTEMP);
|
||||
free(vinfo, M_NANDFSTEMP);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
nandfs_cleaner(struct nandfs_device *fsdev)
|
||||
{
|
||||
uint64_t checked_seg = 0;
|
||||
int error;
|
||||
|
||||
while (!nandfs_cleaner_finished(fsdev)) {
|
||||
if (!nandfs_cleaner_enable || rebooting)
|
||||
continue;
|
||||
|
||||
DPRINTF(CLEAN, ("%s: run started\n", __func__));
|
||||
|
||||
fsdev->nd_cleaning = 1;
|
||||
|
||||
error = nandfs_cleaner_body(fsdev, &checked_seg);
|
||||
|
||||
DPRINTF(CLEAN, ("%s: run finished error %d\n", __func__,
|
||||
error));
|
||||
}
|
||||
|
||||
DPRINTF(CLEAN, ("%s: exiting\n", __func__));
|
||||
kthread_exit();
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cleaner_clean_segments(struct nandfs_device *nffsdev,
|
||||
struct nandfs_vinfo *vinfo, uint32_t nvinfo,
|
||||
struct nandfs_period *pd, uint32_t npd,
|
||||
struct nandfs_bdesc *bdesc, uint32_t nbdesc,
|
||||
uint64_t *segments, uint32_t nsegs)
|
||||
{
|
||||
struct nandfs_node *gc;
|
||||
struct buf *bp;
|
||||
uint32_t i;
|
||||
int error = 0;
|
||||
|
||||
gc = nffsdev->nd_gc_node;
|
||||
|
||||
DPRINTF(CLEAN, ("%s: enter\n", __func__));
|
||||
|
||||
VOP_LOCK(NTOV(gc), LK_EXCLUSIVE);
|
||||
for (i = 0; i < nvinfo; i++) {
|
||||
if (!vinfo[i].nvi_alive)
|
||||
continue;
|
||||
DPRINTF(CLEAN, ("%s: read vblknr:%#jx blk:%#jx\n",
|
||||
__func__, (uintmax_t)vinfo[i].nvi_vblocknr,
|
||||
(uintmax_t)vinfo[i].nvi_blocknr));
|
||||
error = nandfs_bread(nffsdev->nd_gc_node, vinfo[i].nvi_blocknr,
|
||||
NULL, 0, &bp);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d", __FILE__, __LINE__);
|
||||
VOP_UNLOCK(NTOV(gc), 0);
|
||||
goto out;
|
||||
}
|
||||
nandfs_vblk_set(bp, vinfo[i].nvi_vblocknr);
|
||||
nandfs_buf_set(bp, NANDFS_VBLK_ASSIGNED);
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
}
|
||||
VOP_UNLOCK(NTOV(gc), 0);
|
||||
|
||||
/* Delete checkpoints */
|
||||
for (i = 0; i < npd; i++) {
|
||||
DPRINTF(CLEAN, ("delete checkpoint: %jx\n",
|
||||
(uintmax_t)pd[i].p_start));
|
||||
error = nandfs_delete_cp(nffsdev->nd_cp_node, pd[i].p_start,
|
||||
pd[i].p_end);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d", __FILE__, __LINE__);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update vblocks */
|
||||
for (i = 0; i < nvinfo; i++) {
|
||||
if (vinfo[i].nvi_alive)
|
||||
continue;
|
||||
DPRINTF(CLEAN, ("freeing vblknr: %jx\n", vinfo[i].nvi_vblocknr));
|
||||
error = nandfs_vblock_free(nffsdev, vinfo[i].nvi_vblocknr);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d", __FILE__, __LINE__);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
error = nandfs_process_bdesc(nffsdev, bdesc, nbdesc);
|
||||
if (error) {
|
||||
nandfs_error("%s:%d", __FILE__, __LINE__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add segments to clean */
|
||||
if (nffsdev->nd_free_count) {
|
||||
nffsdev->nd_free_base = realloc(nffsdev->nd_free_base,
|
||||
(nffsdev->nd_free_count + nsegs) * sizeof(uint64_t),
|
||||
M_NANDFSTEMP, M_WAITOK | M_ZERO);
|
||||
memcpy(&nffsdev->nd_free_base[nffsdev->nd_free_count], segments,
|
||||
nsegs * sizeof(uint64_t));
|
||||
nffsdev->nd_free_count += nsegs;
|
||||
} else {
|
||||
nffsdev->nd_free_base = malloc(nsegs * sizeof(uint64_t),
|
||||
M_NANDFSTEMP, M_WAITOK|M_ZERO);
|
||||
memcpy(nffsdev->nd_free_base, segments,
|
||||
nsegs * sizeof(uint64_t));
|
||||
nffsdev->nd_free_count = nsegs;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
DPRINTF(CLEAN, ("%s: exit error %d\n", __func__, error));
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
|
||||
uint64_t nmembs)
|
||||
{
|
||||
struct nandfs_node *dat_node;
|
||||
struct buf *bp;
|
||||
uint64_t i;
|
||||
int error;
|
||||
|
||||
dat_node = nffsdev->nd_dat_node;
|
||||
|
||||
VOP_LOCK(NTOV(dat_node), LK_EXCLUSIVE);
|
||||
|
||||
for (i = 0; i < nmembs; i++) {
|
||||
if (!bd[i].bd_alive)
|
||||
continue;
|
||||
DPRINTF(CLEAN, ("%s: idx %jx offset %jx\n",
|
||||
__func__, i, bd[i].bd_offset));
|
||||
if (bd[i].bd_level) {
|
||||
error = nandfs_bread_meta(dat_node, bd[i].bd_offset,
|
||||
NULL, 0, &bp);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot read dat node "
|
||||
"level:%d\n", __func__, bd[i].bd_level);
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(dat_node), 0);
|
||||
return (error);
|
||||
}
|
||||
nandfs_dirty_buf_meta(bp, 1);
|
||||
nandfs_bmap_dirty_blocks(VTON(bp->b_vp), bp, 1);
|
||||
} else {
|
||||
error = nandfs_bread(dat_node, bd[i].bd_offset, NULL,
|
||||
0, &bp);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot read dat node\n",
|
||||
__func__);
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(dat_node), 0);
|
||||
return (error);
|
||||
}
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
}
|
||||
DPRINTF(CLEAN, ("%s: bp: %p\n", __func__, bp));
|
||||
}
|
||||
|
||||
VOP_UNLOCK(NTOV(dat_node), 0);
|
||||
|
||||
return (0);
|
||||
}
|
776
sys/fs/nandfs/nandfs_cpfile.c
Normal file
776
sys/fs/nandfs/nandfs_cpfile.c
Normal file
@ -0,0 +1,776 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include "nandfs_mount.h"
|
||||
#include "nandfs.h"
|
||||
#include "nandfs_subr.h"
|
||||
|
||||
|
||||
static int
|
||||
nandfs_checkpoint_size(struct nandfs_device *fsdev)
|
||||
{
|
||||
|
||||
return (fsdev->nd_fsdata.f_checkpoint_size);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_checkpoint_blk_offset(struct nandfs_device *fsdev, uint64_t cn,
|
||||
uint64_t *blk, uint64_t *offset)
|
||||
{
|
||||
uint64_t off;
|
||||
uint16_t cp_size, cp_per_blk;
|
||||
|
||||
KASSERT((cn), ("checkpoing cannot be zero"));
|
||||
|
||||
cp_size = fsdev->nd_fsdata.f_checkpoint_size;
|
||||
cp_per_blk = fsdev->nd_blocksize / cp_size;
|
||||
off = roundup(sizeof(struct nandfs_cpfile_header), cp_size) / cp_size;
|
||||
off += (cn - 1);
|
||||
|
||||
*blk = off / cp_per_blk;
|
||||
*offset = (off % cp_per_blk) * cp_size;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_checkpoint_blk_remaining(struct nandfs_device *fsdev, uint64_t cn,
|
||||
uint64_t blk, uint64_t offset)
|
||||
{
|
||||
uint16_t cp_size, cp_remaining;
|
||||
|
||||
cp_size = fsdev->nd_fsdata.f_checkpoint_size;
|
||||
cp_remaining = (fsdev->nd_blocksize - offset) / cp_size;
|
||||
|
||||
return (cp_remaining);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
|
||||
uint64_t cn)
|
||||
{
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset;
|
||||
int error;
|
||||
|
||||
if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (-1);
|
||||
|
||||
|
||||
nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
|
||||
|
||||
if (blk != 0) {
|
||||
if (blk < cp_node->nn_inode.i_blocks)
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
else
|
||||
error = nandfs_bcreate(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
if (bp)
|
||||
brelse(bp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
}
|
||||
|
||||
DPRINTF(CPFILE, ("%s: cn:%#jx entry block:%#jx offset:%#jx\n",
|
||||
__func__, (uintmax_t)cn, (uintmax_t)blk, (uintmax_t)offset));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_set_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
|
||||
uint64_t cn, struct nandfs_inode *ifile_inode, uint64_t nblocks)
|
||||
{
|
||||
struct nandfs_cpfile_header *cnh;
|
||||
struct nandfs_checkpoint *cnp;
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset;
|
||||
int error;
|
||||
|
||||
if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
|
||||
nandfs_error("%s: trying to set invalid chekpoint %jx - %jx\n",
|
||||
__func__, cn, fsdev->nd_last_cno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return error;
|
||||
}
|
||||
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
cnh->ch_ncheckpoints++;
|
||||
|
||||
nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
|
||||
|
||||
if(blk != 0) {
|
||||
brelse(bp);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
cnp = (struct nandfs_checkpoint *)((uint8_t *)bp->b_data + offset);
|
||||
cnp->cp_flags = 0;
|
||||
cnp->cp_checkpoints_count = 1;
|
||||
memset(&cnp->cp_snapshot_list, 0, sizeof(struct nandfs_snapshot_list));
|
||||
cnp->cp_cno = cn;
|
||||
cnp->cp_create = fsdev->nd_ts.tv_sec;
|
||||
cnp->cp_nblk_inc = nblocks;
|
||||
cnp->cp_blocks_count = 0;
|
||||
memcpy (&cnp->cp_ifile_inode, ifile_inode, sizeof(cnp->cp_ifile_inode));
|
||||
|
||||
DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx nblk:%#jx\n",
|
||||
__func__, (uintmax_t)cn, (uintmax_t)cnp->cp_create,
|
||||
(uintmax_t)nblocks));
|
||||
|
||||
brelse(bp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cp_mounted(struct nandfs_device *nandfsdev, uint64_t cno)
|
||||
{
|
||||
struct nandfsmount *nmp;
|
||||
int mounted = 0;
|
||||
|
||||
mtx_lock(&nandfsdev->nd_mutex);
|
||||
/* No double-mounting of the same checkpoint */
|
||||
STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
|
||||
if (nmp->nm_mount_args.cpno == cno) {
|
||||
mounted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&nandfsdev->nd_mutex);
|
||||
|
||||
return (mounted);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cp_set_snapshot(struct nandfs_node *cp_node, uint64_t cno)
|
||||
{
|
||||
struct nandfs_device *fsdev;
|
||||
struct nandfs_cpfile_header *cnh;
|
||||
struct nandfs_checkpoint *cnp;
|
||||
struct nandfs_snapshot_list *list;
|
||||
struct buf *bp;
|
||||
uint64_t blk, prev_blk, offset;
|
||||
uint64_t curr, prev;
|
||||
int error;
|
||||
|
||||
fsdev = cp_node->nn_nandfsdev;
|
||||
|
||||
/* Get snapshot data */
|
||||
nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
|
||||
brelse(bp);
|
||||
return (ENOENT);
|
||||
}
|
||||
if ((cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
|
||||
brelse(bp);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
/* Get list from header */
|
||||
error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
list = &cnh->ch_snapshot_list;
|
||||
prev = list->ssl_prev;
|
||||
brelse(bp);
|
||||
prev_blk = ~(0);
|
||||
curr = 0;
|
||||
while (prev > cno) {
|
||||
curr = prev;
|
||||
nandfs_checkpoint_blk_offset(fsdev, prev, &prev_blk, &offset);
|
||||
error = nandfs_bread(cp_node, prev_blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
list = &cnp->cp_snapshot_list;
|
||||
prev = list->ssl_prev;
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
if (curr == 0) {
|
||||
nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
list = &cnh->ch_snapshot_list;
|
||||
} else {
|
||||
nandfs_checkpoint_blk_offset(fsdev, curr, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
list = &cnp->cp_snapshot_list;
|
||||
}
|
||||
|
||||
list->ssl_prev = cno;
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
|
||||
/* Update snapshot for cno */
|
||||
nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
list = &cnp->cp_snapshot_list;
|
||||
list->ssl_prev = prev;
|
||||
list->ssl_next = curr;
|
||||
cnp->cp_flags |= NANDFS_CHECKPOINT_SNAPSHOT;
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
if (prev == 0) {
|
||||
nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
list = &cnh->ch_snapshot_list;
|
||||
} else {
|
||||
/* Update snapshot list for prev */
|
||||
nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
list = &cnp->cp_snapshot_list;
|
||||
}
|
||||
list->ssl_next = cno;
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
/* Update header */
|
||||
error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
cnh->ch_nsnapshots++;
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_cp_clr_snapshot(struct nandfs_node *cp_node, uint64_t cno)
|
||||
{
|
||||
struct nandfs_device *fsdev;
|
||||
struct nandfs_cpfile_header *cnh;
|
||||
struct nandfs_checkpoint *cnp;
|
||||
struct nandfs_snapshot_list *list;
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset, snapshot_cnt;
|
||||
uint64_t next, prev;
|
||||
int error;
|
||||
|
||||
fsdev = cp_node->nn_nandfsdev;
|
||||
|
||||
/* Get snapshot data */
|
||||
nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
|
||||
brelse(bp);
|
||||
return (ENOENT);
|
||||
}
|
||||
if (!(cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
|
||||
brelse(bp);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
list = &cnp->cp_snapshot_list;
|
||||
next = list->ssl_next;
|
||||
prev = list->ssl_prev;
|
||||
brelse(bp);
|
||||
|
||||
/* Get previous snapshot */
|
||||
if (prev != 0) {
|
||||
nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
list = &cnp->cp_snapshot_list;
|
||||
} else {
|
||||
nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
list = &cnh->ch_snapshot_list;
|
||||
}
|
||||
|
||||
list->ssl_next = next;
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Get next snapshot */
|
||||
if (next != 0) {
|
||||
nandfs_checkpoint_blk_offset(fsdev, next, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
list = &cnp->cp_snapshot_list;
|
||||
} else {
|
||||
nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
list = &cnh->ch_snapshot_list;
|
||||
}
|
||||
list->ssl_prev = prev;
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
/* Update snapshot list for cno */
|
||||
nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
|
||||
error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
list = &cnp->cp_snapshot_list;
|
||||
list->ssl_prev = 0;
|
||||
list->ssl_next = 0;
|
||||
cnp->cp_flags &= !NANDFS_CHECKPOINT_SNAPSHOT;
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
/* Update header */
|
||||
error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
snapshot_cnt = cnh->ch_nsnapshots;
|
||||
snapshot_cnt--;
|
||||
cnh->ch_nsnapshots = snapshot_cnt;
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_chng_cpmode(struct nandfs_node *node, struct nandfs_cpmode *ncpm)
|
||||
{
|
||||
struct nandfs_device *fsdev;
|
||||
uint64_t cno = ncpm->ncpm_cno;
|
||||
int mode = ncpm->ncpm_mode;
|
||||
int ret;
|
||||
|
||||
fsdev = node->nn_nandfsdev;
|
||||
VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
|
||||
switch (mode) {
|
||||
case NANDFS_CHECKPOINT:
|
||||
if (nandfs_cp_mounted(fsdev, cno)) {
|
||||
ret = EBUSY;
|
||||
} else
|
||||
ret = nandfs_cp_clr_snapshot(node, cno);
|
||||
break;
|
||||
case NANDFS_SNAPSHOT:
|
||||
ret = nandfs_cp_set_snapshot(node, cno);
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
break;
|
||||
}
|
||||
VOP_UNLOCK(NTOV(node), 0);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
nandfs_cpinfo_fill(struct nandfs_checkpoint *cnp, struct nandfs_cpinfo *nci)
|
||||
{
|
||||
|
||||
nci->nci_flags = cnp->cp_flags;
|
||||
nci->nci_pad = 0;
|
||||
nci->nci_cno = cnp->cp_cno;
|
||||
nci->nci_create = cnp->cp_create;
|
||||
nci->nci_nblk_inc = cnp->cp_nblk_inc;
|
||||
nci->nci_blocks_count = cnp->cp_blocks_count;
|
||||
nci->nci_next = cnp->cp_snapshot_list.ssl_next;
|
||||
DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx\n",
|
||||
__func__, (uintmax_t)cnp->cp_cno,
|
||||
(uintmax_t)cnp->cp_create));
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_get_cpinfo_cp(struct nandfs_node *node, uint64_t cno,
|
||||
struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
|
||||
{
|
||||
struct nandfs_device *fsdev;
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset, last_cno, i;
|
||||
uint16_t remaining;
|
||||
int error;
|
||||
#ifdef INVARIANTS
|
||||
uint64_t testblk, testoffset;
|
||||
#endif
|
||||
|
||||
if (cno == 0) {
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
if (mnmembs < 1) {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
fsdev = node->nn_nandfsdev;
|
||||
last_cno = fsdev->nd_last_cno;
|
||||
DPRINTF(CPFILE, ("%s: cno:%#jx mnmembs: %#jx last:%#jx\n", __func__,
|
||||
(uintmax_t)cno, (uintmax_t)mnmembs,
|
||||
(uintmax_t)fsdev->nd_last_cno));
|
||||
|
||||
/*
|
||||
* do {
|
||||
* get block
|
||||
* read checkpoints until we hit last checkpoint, end of block or
|
||||
* requested number
|
||||
* } while (last read checkpoint <= last checkpoint on fs &&
|
||||
* read checkpoints < request number);
|
||||
*/
|
||||
*nmembs = i = 0;
|
||||
do {
|
||||
nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
|
||||
remaining = nandfs_checkpoint_blk_remaining(fsdev, cno,
|
||||
blk, offset);
|
||||
error = nandfs_bread(node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
while (cno <= last_cno && i < mnmembs && remaining) {
|
||||
#ifdef INVARIANTS
|
||||
nandfs_checkpoint_blk_offset(fsdev, cno, &testblk,
|
||||
&testoffset);
|
||||
KASSERT(testblk == blk, ("testblk != blk"));
|
||||
KASSERT(testoffset == offset, ("testoffset != offset"));
|
||||
#endif
|
||||
DPRINTF(CPFILE, ("%s: cno %#jx\n", __func__,
|
||||
(uintmax_t)cno));
|
||||
|
||||
nandfs_cpinfo_fill((struct nandfs_checkpoint *)
|
||||
(bp->b_data + offset), nci);
|
||||
offset += nandfs_checkpoint_size(fsdev);
|
||||
i++;
|
||||
nci++;
|
||||
cno++;
|
||||
(*nmembs)++;
|
||||
remaining--;
|
||||
}
|
||||
brelse(bp);
|
||||
} while (cno <= last_cno && i < mnmembs);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_get_cpinfo_sp(struct nandfs_node *node, uint64_t cno,
|
||||
struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
|
||||
{
|
||||
struct nandfs_checkpoint *cnp;
|
||||
struct nandfs_cpfile_header *cnh;
|
||||
struct nandfs_device *fsdev;
|
||||
struct buf *bp = NULL;
|
||||
uint64_t curr = 0;
|
||||
uint64_t blk, offset, curr_cno;
|
||||
uint32_t flag;
|
||||
int i, error;
|
||||
|
||||
if (cno == 0 || cno == ~(0))
|
||||
return (ENOENT);
|
||||
|
||||
fsdev = node->nn_nandfsdev;
|
||||
curr_cno = cno;
|
||||
|
||||
if (nmembs)
|
||||
*nmembs = 0;
|
||||
if (curr_cno == 1) {
|
||||
/* Get list from header */
|
||||
error = nandfs_bread(node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
curr_cno = cnh->ch_snapshot_list.ssl_next;
|
||||
brelse(bp);
|
||||
bp = NULL;
|
||||
|
||||
/* No snapshots */
|
||||
if (curr_cno == 0)
|
||||
return (0);
|
||||
}
|
||||
|
||||
for (i = 0; i < mnmembs; i++, nci++) {
|
||||
nandfs_checkpoint_blk_offset(fsdev, curr_cno, &blk, &offset);
|
||||
if (i == 0 || curr != blk) {
|
||||
if (bp)
|
||||
brelse(bp);
|
||||
error = nandfs_bread(node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (ENOENT);
|
||||
}
|
||||
curr = blk;
|
||||
}
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
flag = cnp->cp_flags;
|
||||
if (!(flag & NANDFS_CHECKPOINT_SNAPSHOT) ||
|
||||
(flag & NANDFS_CHECKPOINT_INVALID))
|
||||
break;
|
||||
|
||||
nci->nci_flags = flag;
|
||||
nci->nci_pad = 0;
|
||||
nci->nci_cno = cnp->cp_cno;
|
||||
nci->nci_create = cnp->cp_create;
|
||||
nci->nci_nblk_inc = cnp->cp_nblk_inc;
|
||||
nci->nci_blocks_count = cnp->cp_blocks_count;
|
||||
nci->nci_next = cnp->cp_snapshot_list.ssl_next;
|
||||
if (nmembs)
|
||||
(*nmembs)++;
|
||||
|
||||
curr_cno = nci->nci_next;
|
||||
if (!curr_cno)
|
||||
break;
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_cpinfo(struct nandfs_node *node, uint64_t cno, uint16_t flags,
|
||||
struct nandfs_cpinfo *nci, uint32_t nmembs, uint32_t *nnmembs)
|
||||
{
|
||||
int error;
|
||||
|
||||
VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
|
||||
switch (flags) {
|
||||
case NANDFS_CHECKPOINT:
|
||||
error = nandfs_get_cpinfo_cp(node, cno, nci, nmembs, nnmembs);
|
||||
break;
|
||||
case NANDFS_SNAPSHOT:
|
||||
error = nandfs_get_cpinfo_sp(node, cno, nci, nmembs, nnmembs);
|
||||
break;
|
||||
default:
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
VOP_UNLOCK(NTOV(node), 0);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_cpinfo_ioctl(struct nandfs_node *node, struct nandfs_argv *nargv)
|
||||
{
|
||||
struct nandfs_cpinfo *nci;
|
||||
uint64_t cno = nargv->nv_index;
|
||||
void *buf = (void *)((uintptr_t)nargv->nv_base);
|
||||
uint16_t flags = nargv->nv_flags;
|
||||
uint32_t nmembs = 0;
|
||||
int error;
|
||||
|
||||
if (nargv->nv_nmembs > NANDFS_CPINFO_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
nci = malloc(sizeof(struct nandfs_cpinfo) * nargv->nv_nmembs,
|
||||
M_NANDFSTEMP, M_WAITOK | M_ZERO);
|
||||
|
||||
error = nandfs_get_cpinfo(node, cno, flags, nci, nargv->nv_nmembs, &nmembs);
|
||||
|
||||
if (error == 0) {
|
||||
nargv->nv_nmembs = nmembs;
|
||||
error = copyout(nci, buf,
|
||||
sizeof(struct nandfs_cpinfo) * nmembs);
|
||||
}
|
||||
|
||||
free(nci, M_NANDFSTEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_delete_cp(struct nandfs_node *node, uint64_t start, uint64_t end)
|
||||
{
|
||||
struct nandfs_checkpoint *cnp;
|
||||
struct nandfs_device *fsdev;
|
||||
struct buf *bp;
|
||||
uint64_t cno = start, blk, offset;
|
||||
int error;
|
||||
|
||||
DPRINTF(CPFILE, ("%s: delete cno %jx-%jx\n", __func__, start, end));
|
||||
VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
|
||||
fsdev = node->nn_nandfsdev;
|
||||
for (cno = start; cno <= end; cno++) {
|
||||
if (!cno)
|
||||
continue;
|
||||
|
||||
nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
|
||||
error = nandfs_bread(node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
VOP_UNLOCK(NTOV(node), 0);
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
|
||||
if (cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) {
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(node), 0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
cnp->cp_flags |= NANDFS_CHECKPOINT_INVALID;
|
||||
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
VOP_UNLOCK(NTOV(node), 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_make_snap(struct nandfs_device *fsdev, uint64_t *cno)
|
||||
{
|
||||
struct nandfs_cpmode cpm;
|
||||
int error;
|
||||
|
||||
*cno = cpm.ncpm_cno = fsdev->nd_last_cno;
|
||||
cpm.ncpm_mode = NANDFS_SNAPSHOT;
|
||||
error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_delete_snap(struct nandfs_device *fsdev, uint64_t cno)
|
||||
{
|
||||
struct nandfs_cpmode cpm;
|
||||
int error;
|
||||
|
||||
cpm.ncpm_cno = cno;
|
||||
cpm.ncpm_mode = NANDFS_CHECKPOINT;
|
||||
error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int nandfs_get_cpstat(struct nandfs_node *cp_node, struct nandfs_cpstat *ncp)
|
||||
{
|
||||
struct nandfs_device *fsdev;
|
||||
struct nandfs_cpfile_header *cnh;
|
||||
struct buf *bp;
|
||||
int error;
|
||||
|
||||
VOP_LOCK(NTOV(cp_node), LK_EXCLUSIVE);
|
||||
fsdev = cp_node->nn_nandfsdev;
|
||||
|
||||
/* Get header */
|
||||
error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(cp_node), 0);
|
||||
return (error);
|
||||
}
|
||||
cnh = (struct nandfs_cpfile_header *) bp->b_data;
|
||||
ncp->ncp_cno = fsdev->nd_last_cno;
|
||||
ncp->ncp_ncps = cnh->ch_ncheckpoints;
|
||||
ncp->ncp_nss = cnh->ch_nsnapshots;
|
||||
DPRINTF(CPFILE, ("%s: cno:%#jx ncps:%#jx nss:%#jx\n",
|
||||
__func__, ncp->ncp_cno, ncp->ncp_ncps, ncp->ncp_nss));
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(cp_node), 0);
|
||||
|
||||
return (0);
|
||||
}
|
344
sys/fs/nandfs/nandfs_dat.c
Normal file
344
sys/fs/nandfs/nandfs_dat.c
Normal file
@ -0,0 +1,344 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_mount.h>
|
||||
#include <fs/nandfs/nandfs.h>
|
||||
#include <fs/nandfs/nandfs_subr.h>
|
||||
|
||||
int
|
||||
nandfs_vblock_alloc(struct nandfs_device *nandfsdev, nandfs_daddr_t *vblock)
|
||||
{
|
||||
struct nandfs_node *dat;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfs_dat_entry *dat_entry;
|
||||
uint64_t start;
|
||||
uint32_t entry;
|
||||
int locked, error;
|
||||
|
||||
dat = nandfsdev->nd_dat_node;
|
||||
mdt = &nandfsdev->nd_dat_mdt;
|
||||
start = nandfsdev->nd_last_cno + 1;
|
||||
|
||||
locked = NANDFS_VOP_ISLOCKED(NTOV(dat));
|
||||
if (!locked)
|
||||
VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
|
||||
req.entrynum = 0;
|
||||
|
||||
/* Alloc vblock number */
|
||||
error = nandfs_find_free_entry(mdt, dat, &req);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot find free vblk entry\n",
|
||||
__func__);
|
||||
if (!locked)
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Read/create buffer */
|
||||
error = nandfs_get_entry_block(mdt, dat, &req, &entry, 1);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot get free vblk entry\n",
|
||||
__func__);
|
||||
nandfs_abort_entry(&req);
|
||||
if (!locked)
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Fill out vblock data */
|
||||
dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data;
|
||||
dat_entry[entry].de_start = start;
|
||||
dat_entry[entry].de_end = UINTMAX_MAX;
|
||||
dat_entry[entry].de_blocknr = 0;
|
||||
|
||||
/* Commit allocation */
|
||||
error = nandfs_alloc_entry(mdt, &req);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot get free vblk entry\n",
|
||||
__func__);
|
||||
if (!locked)
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Return allocated vblock */
|
||||
*vblock = req.entrynum;
|
||||
DPRINTF(DAT, ("%s: allocated vblock %#jx\n",
|
||||
__func__, (uintmax_t)*vblock));
|
||||
|
||||
if (!locked)
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_vblock_assign(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock,
|
||||
nandfs_lbn_t block)
|
||||
{
|
||||
struct nandfs_node *dat;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfs_dat_entry *dat_entry;
|
||||
uint32_t entry;
|
||||
int locked, error;
|
||||
|
||||
dat = nandfsdev->nd_dat_node;
|
||||
mdt = &nandfsdev->nd_dat_mdt;
|
||||
|
||||
locked = NANDFS_VOP_ISLOCKED(NTOV(dat));
|
||||
if (!locked)
|
||||
VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
|
||||
req.entrynum = vblock;
|
||||
|
||||
error = nandfs_get_entry_block(mdt, dat, &req, &entry, 0);
|
||||
if (!error) {
|
||||
dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data;
|
||||
dat_entry[entry].de_blocknr = block;
|
||||
|
||||
DPRINTF(DAT, ("%s: assing vblock %jx->%jx\n",
|
||||
__func__, (uintmax_t)vblock, (uintmax_t)block));
|
||||
|
||||
/*
|
||||
* It is mostly called from syncer() so
|
||||
* we want to force making buf dirty
|
||||
*/
|
||||
error = nandfs_dirty_buf(req.bp_entry, 1);
|
||||
}
|
||||
|
||||
if (!locked)
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_vblock_end(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock)
|
||||
{
|
||||
struct nandfs_node *dat;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfs_dat_entry *dat_entry;
|
||||
uint64_t end;
|
||||
uint32_t entry;
|
||||
int locked, error;
|
||||
|
||||
dat = nandfsdev->nd_dat_node;
|
||||
mdt = &nandfsdev->nd_dat_mdt;
|
||||
end = nandfsdev->nd_last_cno;
|
||||
|
||||
locked = NANDFS_VOP_ISLOCKED(NTOV(dat));
|
||||
if (!locked)
|
||||
VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
|
||||
req.entrynum = vblock;
|
||||
|
||||
error = nandfs_get_entry_block(mdt, dat, &req, &entry, 0);
|
||||
if (!error) {
|
||||
dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data;
|
||||
dat_entry[entry].de_end = end;
|
||||
DPRINTF(DAT, ("%s: end vblock %#jx at checkpoint %#jx\n",
|
||||
__func__, (uintmax_t)vblock, (uintmax_t)end));
|
||||
|
||||
/*
|
||||
* It is mostly called from syncer() so
|
||||
* we want to force making buf dirty
|
||||
*/
|
||||
error = nandfs_dirty_buf(req.bp_entry, 1);
|
||||
}
|
||||
|
||||
if (!locked)
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_vblock_free(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock)
|
||||
{
|
||||
struct nandfs_node *dat;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_alloc_request req;
|
||||
int error;
|
||||
|
||||
dat = nandfsdev->nd_dat_node;
|
||||
mdt = &nandfsdev->nd_dat_mdt;
|
||||
|
||||
VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
|
||||
req.entrynum = vblock;
|
||||
|
||||
error = nandfs_find_entry(mdt, dat, &req);
|
||||
if (!error) {
|
||||
DPRINTF(DAT, ("%s: vblk %#jx\n", __func__, (uintmax_t)vblock));
|
||||
nandfs_free_entry(mdt, &req);
|
||||
}
|
||||
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_dat_vinfo_ioctl(struct nandfs_device *nandfsdev, struct nandfs_argv *nargv)
|
||||
{
|
||||
struct nandfs_vinfo *vinfo;
|
||||
size_t size;
|
||||
int error;
|
||||
|
||||
if (nargv->nv_nmembs > NANDFS_VINFO_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
size = sizeof(struct nandfs_vinfo) * nargv->nv_nmembs;
|
||||
vinfo = malloc(size, M_NANDFSTEMP, M_WAITOK|M_ZERO);
|
||||
|
||||
error = copyin((void *)(uintptr_t)nargv->nv_base, vinfo, size);
|
||||
if (error) {
|
||||
free(vinfo, M_NANDFSTEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = nandfs_get_dat_vinfo(nandfsdev, vinfo, nargv->nv_nmembs);
|
||||
if (error == 0)
|
||||
error = copyout(vinfo, (void *)(uintptr_t)nargv->nv_base, size);
|
||||
free(vinfo, M_NANDFSTEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_dat_vinfo(struct nandfs_device *nandfsdev, struct nandfs_vinfo *vinfo,
|
||||
uint32_t nmembs)
|
||||
{
|
||||
struct nandfs_node *dat;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfs_dat_entry *dat_entry;
|
||||
uint32_t i, idx;
|
||||
int error = 0;
|
||||
|
||||
dat = nandfsdev->nd_dat_node;
|
||||
mdt = &nandfsdev->nd_dat_mdt;
|
||||
|
||||
DPRINTF(DAT, ("%s: nmembs %#x\n", __func__, nmembs));
|
||||
|
||||
VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
|
||||
|
||||
for (i = 0; i < nmembs; i++) {
|
||||
req.entrynum = vinfo[i].nvi_vblocknr;
|
||||
|
||||
error = nandfs_get_entry_block(mdt, dat,&req, &idx, 0);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
dat_entry = ((struct nandfs_dat_entry *) req.bp_entry->b_data);
|
||||
vinfo[i].nvi_start = dat_entry[idx].de_start;
|
||||
vinfo[i].nvi_end = dat_entry[idx].de_end;
|
||||
vinfo[i].nvi_blocknr = dat_entry[idx].de_blocknr;
|
||||
|
||||
DPRINTF(DAT, ("%s: vinfo: %jx[%jx-%jx]->%jx\n",
|
||||
__func__, vinfo[i].nvi_vblocknr, vinfo[i].nvi_start,
|
||||
vinfo[i].nvi_end, vinfo[i].nvi_blocknr));
|
||||
|
||||
brelse(req.bp_entry);
|
||||
}
|
||||
|
||||
VOP_UNLOCK(NTOV(dat), 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_dat_bdescs_ioctl(struct nandfs_device *nffsdev,
|
||||
struct nandfs_argv *nargv)
|
||||
{
|
||||
struct nandfs_bdesc *bd;
|
||||
size_t size;
|
||||
int error;
|
||||
|
||||
size = nargv->nv_nmembs * sizeof(struct nandfs_bdesc);
|
||||
bd = malloc(size, M_NANDFSTEMP, M_WAITOK);
|
||||
error = copyin((void *)(uintptr_t)nargv->nv_base, bd, size);
|
||||
if (error) {
|
||||
free(bd, M_NANDFSTEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = nandfs_get_dat_bdescs(nffsdev, bd, nargv->nv_nmembs);
|
||||
|
||||
if (error == 0)
|
||||
error = copyout(bd, (void *)(uintptr_t)nargv->nv_base, size);
|
||||
|
||||
free(bd, M_NANDFSTEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_dat_bdescs(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
|
||||
uint32_t nmembs)
|
||||
{
|
||||
struct nandfs_node *dat_node;
|
||||
uint64_t map;
|
||||
uint32_t i;
|
||||
int error = 0;
|
||||
|
||||
dat_node = nffsdev->nd_dat_node;
|
||||
|
||||
VOP_LOCK(NTOV(dat_node), LK_EXCLUSIVE);
|
||||
|
||||
for (i = 0; i < nmembs; i++) {
|
||||
DPRINTF(CLEAN,
|
||||
("%s: bd ino:%#jx oblk:%#jx blocknr:%#jx off:%#jx\n",
|
||||
__func__, (uintmax_t)bd[i].bd_ino,
|
||||
(uintmax_t)bd[i].bd_oblocknr, (uintmax_t)bd[i].bd_blocknr,
|
||||
(uintmax_t)bd[i].bd_offset));
|
||||
|
||||
error = nandfs_bmap_lookup(dat_node, bd[i].bd_offset, &map);
|
||||
if (error)
|
||||
break;
|
||||
bd[i].bd_blocknr = map;
|
||||
}
|
||||
|
||||
VOP_UNLOCK(NTOV(dat_node), 0);
|
||||
return (error);
|
||||
}
|
314
sys/fs/nandfs/nandfs_dir.c
Normal file
314
sys/fs/nandfs/nandfs_dir.c
Normal file
@ -0,0 +1,314 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf
|
||||
* Copyright (c) 2008, 2009 Reinoud Zandijk
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/signalvar.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/lockf.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
|
||||
#include "nandfs_mount.h"
|
||||
#include "nandfs.h"
|
||||
#include "nandfs_subr.h"
|
||||
|
||||
int
|
||||
nandfs_add_dirent(struct vnode *dvp, uint64_t ino, char *nameptr, long namelen,
|
||||
uint8_t type)
|
||||
{
|
||||
struct nandfs_node *dir_node = VTON(dvp);
|
||||
struct nandfs_dir_entry *dirent, *pdirent;
|
||||
uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize;
|
||||
uint64_t filesize = dir_node->nn_inode.i_size;
|
||||
uint64_t inode_blks = dir_node->nn_inode.i_blocks;
|
||||
uint32_t off, rest;
|
||||
uint8_t *pos;
|
||||
struct buf *bp;
|
||||
int error;
|
||||
|
||||
pdirent = NULL;
|
||||
bp = NULL;
|
||||
if (inode_blks) {
|
||||
error = nandfs_bread(dir_node, inode_blks - 1, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
pos = bp->b_data;
|
||||
off = 0;
|
||||
while (off < blocksize) {
|
||||
pdirent = (struct nandfs_dir_entry *) (pos + off);
|
||||
if (!pdirent->rec_len) {
|
||||
pdirent = NULL;
|
||||
break;
|
||||
}
|
||||
off += pdirent->rec_len;
|
||||
}
|
||||
|
||||
if (pdirent)
|
||||
rest = pdirent->rec_len -
|
||||
NANDFS_DIR_REC_LEN(pdirent->name_len);
|
||||
else
|
||||
rest = blocksize;
|
||||
|
||||
if (rest < NANDFS_DIR_REC_LEN(namelen)) {
|
||||
/* Do not update pdirent as new block is created */
|
||||
pdirent = NULL;
|
||||
brelse(bp);
|
||||
/* Set to NULL to create new */
|
||||
bp = NULL;
|
||||
filesize += rest;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no bp found create new */
|
||||
if (!bp) {
|
||||
error = nandfs_bcreate(dir_node, inode_blks, NOCRED, 0, &bp);
|
||||
if (error)
|
||||
return (error);
|
||||
off = 0;
|
||||
pos = bp->b_data;
|
||||
}
|
||||
|
||||
/* Modify pdirent if exists */
|
||||
if (pdirent) {
|
||||
DPRINTF(LOOKUP, ("modify pdirent %p\n", pdirent));
|
||||
/* modify last de */
|
||||
off -= pdirent->rec_len;
|
||||
pdirent->rec_len =
|
||||
NANDFS_DIR_REC_LEN(pdirent->name_len);
|
||||
off += pdirent->rec_len;
|
||||
}
|
||||
|
||||
/* Create new dirent */
|
||||
dirent = (struct nandfs_dir_entry *) (pos + off);
|
||||
dirent->rec_len = blocksize - off;
|
||||
dirent->inode = ino;
|
||||
dirent->name_len = namelen;
|
||||
memset(dirent->name, 0, NANDFS_DIR_NAME_LEN(namelen));
|
||||
memcpy(dirent->name, nameptr, namelen);
|
||||
dirent->file_type = type;
|
||||
|
||||
filesize += NANDFS_DIR_REC_LEN(dirent->name_len);
|
||||
|
||||
DPRINTF(LOOKUP, ("create dir_entry '%.*s' at %p with size %x "
|
||||
"new filesize: %jx\n",
|
||||
(int)namelen, dirent->name, dirent, dirent->rec_len,
|
||||
(uintmax_t)filesize));
|
||||
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
dir_node->nn_inode.i_size = filesize;
|
||||
dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
|
||||
vnode_pager_setsize(dvp, filesize);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_remove_dirent(struct vnode *dvp, struct nandfs_node *node,
|
||||
struct componentname *cnp)
|
||||
{
|
||||
struct nandfs_node *dir_node;
|
||||
struct nandfs_dir_entry *dirent, *pdirent;
|
||||
struct buf *bp;
|
||||
uint64_t filesize, blocknr, ino, offset;
|
||||
uint32_t blocksize, limit, off;
|
||||
uint16_t newsize;
|
||||
uint8_t *pos;
|
||||
int error, found;
|
||||
|
||||
dir_node = VTON(dvp);
|
||||
filesize = dir_node->nn_inode.i_size;
|
||||
if (!filesize)
|
||||
return (0);
|
||||
|
||||
if (node) {
|
||||
offset = node->nn_diroff;
|
||||
ino = node->nn_ino;
|
||||
} else {
|
||||
offset = dir_node->nn_diroff;
|
||||
ino = NANDFS_WHT_INO;
|
||||
}
|
||||
|
||||
dirent = pdirent = NULL;
|
||||
blocksize = dir_node->nn_nandfsdev->nd_blocksize;
|
||||
blocknr = offset / blocksize;
|
||||
|
||||
DPRINTF(LOOKUP, ("rm direntry dvp %p node %p ino %#jx at off %#jx\n",
|
||||
dvp, node, (uintmax_t)ino, (uintmax_t)offset));
|
||||
|
||||
error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
pos = bp->b_data;
|
||||
off = 0;
|
||||
found = 0;
|
||||
limit = offset % blocksize;
|
||||
pdirent = (struct nandfs_dir_entry *) bp->b_data;
|
||||
while (off <= limit) {
|
||||
dirent = (struct nandfs_dir_entry *) (pos + off);
|
||||
|
||||
if ((off == limit) &&
|
||||
(dirent->inode == ino)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (dirent->inode != 0)
|
||||
pdirent = dirent;
|
||||
off += dirent->rec_len;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
nandfs_error("cannot find entry to remove");
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
DPRINTF(LOOKUP,
|
||||
("rm dirent ino %#jx at %#x with size %#x\n",
|
||||
(uintmax_t)dirent->inode, off, dirent->rec_len));
|
||||
|
||||
newsize = (uintptr_t)dirent - (uintptr_t)pdirent;
|
||||
newsize += dirent->rec_len;
|
||||
pdirent->rec_len = newsize;
|
||||
dirent->inode = 0;
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
|
||||
/* If last one modify filesize */
|
||||
if ((offset + NANDFS_DIR_REC_LEN(dirent->name_len)) == filesize) {
|
||||
filesize = blocknr * blocksize +
|
||||
((uintptr_t)pdirent - (uintptr_t)pos) +
|
||||
NANDFS_DIR_REC_LEN(pdirent->name_len);
|
||||
dir_node->nn_inode.i_size = filesize;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_update_parent_dir(struct vnode *dvp, uint64_t newparent)
|
||||
{
|
||||
struct nandfs_dir_entry *dirent;
|
||||
struct nandfs_node *dir_node;
|
||||
struct buf *bp;
|
||||
int error;
|
||||
|
||||
dir_node = VTON(dvp);
|
||||
error = nandfs_bread(dir_node, 0, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
dirent = (struct nandfs_dir_entry *)bp->b_data;
|
||||
dirent->inode = newparent;
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_update_dirent(struct vnode *dvp, struct nandfs_node *fnode,
|
||||
struct nandfs_node *tnode)
|
||||
{
|
||||
struct nandfs_node *dir_node;
|
||||
struct nandfs_dir_entry *dirent;
|
||||
struct buf *bp;
|
||||
uint64_t file_size, blocknr;
|
||||
uint32_t blocksize, off;
|
||||
uint8_t *pos;
|
||||
int error;
|
||||
|
||||
dir_node = VTON(dvp);
|
||||
file_size = dir_node->nn_inode.i_size;
|
||||
if (!file_size)
|
||||
return (0);
|
||||
|
||||
DPRINTF(LOOKUP,
|
||||
("chg direntry dvp %p ino %#jx to in %#jx at off %#jx\n",
|
||||
dvp, (uintmax_t)tnode->nn_ino, (uintmax_t)fnode->nn_ino,
|
||||
(uintmax_t)tnode->nn_diroff));
|
||||
|
||||
blocksize = dir_node->nn_nandfsdev->nd_blocksize;
|
||||
blocknr = tnode->nn_diroff / blocksize;
|
||||
off = tnode->nn_diroff % blocksize;
|
||||
error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
pos = bp->b_data;
|
||||
dirent = (struct nandfs_dir_entry *) (pos + off);
|
||||
KASSERT((dirent->inode == tnode->nn_ino),
|
||||
("direntry mismatch"));
|
||||
|
||||
dirent->inode = fnode->nn_ino;
|
||||
error = nandfs_dirty_buf(bp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_init_dir(struct vnode *dvp, uint64_t ino, uint64_t parent_ino)
|
||||
{
|
||||
|
||||
if (nandfs_add_dirent(dvp, parent_ino, "..", 2, DT_DIR) ||
|
||||
nandfs_add_dirent(dvp, ino, ".", 1, DT_DIR)) {
|
||||
nandfs_error("%s: cannot initialize dir ino:%jd(pino:%jd)\n",
|
||||
__func__, ino, parent_ino);
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
565
sys/fs/nandfs/nandfs_fs.h
Normal file
565
sys/fs/nandfs/nandfs_fs.h
Normal file
@ -0,0 +1,565 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf
|
||||
* Copyright (c) 2008, 2009 Reinoud Zandijk
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Original definitions written by Koji Sato <koji@osrg.net>
|
||||
* and Ryusuke Konishi <ryusuke@osrg.net>
|
||||
* From: NetBSD: nandfs_fs.h,v 1.1 2009/07/18 16:31:42 reinoud
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NANDFS_FS_H
|
||||
#define _NANDFS_FS_H
|
||||
|
||||
#include <sys/uuid.h>
|
||||
|
||||
#define MNINDIR(fsdev) ((fsdev)->nd_blocksize / sizeof(nandfs_daddr_t))
|
||||
|
||||
/*
|
||||
* Inode structure. There are a few dedicated inode numbers that are
|
||||
* defined here first.
|
||||
*/
|
||||
#define NANDFS_WHT_INO 1 /* Whiteout ino */
|
||||
#define NANDFS_ROOT_INO 2 /* Root file inode */
|
||||
#define NANDFS_DAT_INO 3 /* DAT file */
|
||||
#define NANDFS_CPFILE_INO 4 /* checkpoint file */
|
||||
#define NANDFS_SUFILE_INO 5 /* segment usage file */
|
||||
#define NANDFS_IFILE_INO 6 /* ifile */
|
||||
#define NANDFS_GC_INO 7 /* Cleanerd node */
|
||||
#define NANDFS_ATIME_INO 8 /* Atime file (reserved) */
|
||||
#define NANDFS_XATTR_INO 9 /* Xattribute file (reserved) */
|
||||
#define NANDFS_SKETCH_INO 10 /* Sketch file (obsolete) */
|
||||
#define NANDFS_USER_INO 11 /* First user's file inode number */
|
||||
|
||||
#define NANDFS_SYS_NODE(ino) \
|
||||
(((ino) >= NANDFS_DAT_INO) && ((ino) <= NANDFS_GC_INO))
|
||||
|
||||
#define NDADDR 12 /* Direct addresses in inode. */
|
||||
#define NIADDR 3 /* Indirect addresses in inode. */
|
||||
|
||||
typedef int64_t nandfs_daddr_t;
|
||||
typedef int64_t nandfs_lbn_t;
|
||||
|
||||
struct nandfs_inode {
|
||||
uint64_t i_blocks; /* 0: size in device blocks */
|
||||
uint64_t i_size; /* 8: size in bytes */
|
||||
uint64_t i_ctime; /* 16: creation time in seconds */
|
||||
uint64_t i_mtime; /* 24: modification time in seconds part*/
|
||||
uint32_t i_ctime_nsec; /* 32: creation time nanoseconds part */
|
||||
uint32_t i_mtime_nsec; /* 36: modification time in nanoseconds */
|
||||
uint32_t i_uid; /* 40: user id */
|
||||
uint32_t i_gid; /* 44: group id */
|
||||
uint16_t i_mode; /* 48: file mode */
|
||||
uint16_t i_links_count; /* 50: number of references to the inode*/
|
||||
uint32_t i_flags; /* 52: NANDFS_*_FL flags */
|
||||
nandfs_daddr_t i_special; /* 56: special */
|
||||
nandfs_daddr_t i_db[NDADDR]; /* 64: Direct disk blocks. */
|
||||
nandfs_daddr_t i_ib[NIADDR]; /* 160: Indirect disk blocks. */
|
||||
uint64_t i_xattr; /* 184: reserved for extended attributes*/
|
||||
uint32_t i_generation; /* 192: file generation for NFS */
|
||||
uint32_t i_pad[15]; /* 196: make it 64 bits aligned */
|
||||
};
|
||||
|
||||
#ifdef _KERNEL
|
||||
CTASSERT(sizeof(struct nandfs_inode) == 256);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Each checkpoint/snapshot has a super root.
|
||||
*
|
||||
* The super root holds the inodes of the three system files: `dat', `cp' and
|
||||
* 'su' files. All other FS state is defined by those.
|
||||
*
|
||||
* It is CRC checksum'ed and time stamped.
|
||||
*/
|
||||
|
||||
struct nandfs_super_root {
|
||||
uint32_t sr_sum; /* check-sum */
|
||||
uint16_t sr_bytes; /* byte count of this structure */
|
||||
uint16_t sr_flags; /* reserved for flags */
|
||||
uint64_t sr_nongc_ctime; /* timestamp, not for cleaner(?) */
|
||||
struct nandfs_inode sr_dat; /* DAT, virt->phys translation inode */
|
||||
struct nandfs_inode sr_cpfile; /* CP, checkpoints inode */
|
||||
struct nandfs_inode sr_sufile; /* SU, segment usage inode */
|
||||
};
|
||||
|
||||
#define NANDFS_SR_MDT_OFFSET(inode_size, i) \
|
||||
((uint32_t)&((struct nandfs_super_root *)0)->sr_dat + \
|
||||
(inode_size) * (i))
|
||||
|
||||
#define NANDFS_SR_DAT_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 0)
|
||||
#define NANDFS_SR_CPFILE_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 1)
|
||||
#define NANDFS_SR_SUFILE_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 2)
|
||||
#define NANDFS_SR_BYTES (sizeof(struct nandfs_super_root))
|
||||
|
||||
/*
|
||||
* The superblock describes the basic structure and mount history. It also
|
||||
* records some sizes of structures found on the disc for sanity checks.
|
||||
*
|
||||
* The superblock is stored at two places: NANDFS_SB_OFFSET_BYTES and
|
||||
* NANDFS_SB2_OFFSET_BYTES.
|
||||
*/
|
||||
|
||||
/* File system states stored on media in superblock's sbp->s_state */
|
||||
#define NANDFS_VALID_FS 0x0001 /* cleanly unmounted and all is ok */
|
||||
#define NANDFS_ERROR_FS 0x0002 /* there were errors detected, fsck */
|
||||
#define NANDFS_RESIZE_FS 0x0004 /* resize required, XXX unknown flag*/
|
||||
#define NANDFS_MOUNT_STATE_BITS "\20\1VALID_FS\2ERROR_FS\3RESIZE_FS"
|
||||
|
||||
/*
|
||||
* Brief description of control structures:
|
||||
*
|
||||
* NANDFS_NFSAREAS first blocks contain fsdata and some amount of super blocks.
|
||||
* Simple round-robin policy is used in order to choose which block will
|
||||
* contain new super block.
|
||||
*
|
||||
* Simple case with 2 blocks:
|
||||
* 1: fsdata sblock1 [sblock3 [sblock5 ..]]
|
||||
* 2: fsdata sblock2 [sblock4 [sblock6 ..]]
|
||||
*/
|
||||
struct nandfs_fsdata {
|
||||
uint16_t f_magic;
|
||||
uint16_t f_bytes;
|
||||
|
||||
uint32_t f_sum; /* checksum of fsdata */
|
||||
uint32_t f_rev_level; /* major disk format revision */
|
||||
|
||||
uint64_t f_ctime; /* creation time (execution time
|
||||
of newfs) */
|
||||
/* Block size represented as: blocksize = 1 << (f_log_block_size + 10) */
|
||||
uint32_t f_log_block_size;
|
||||
|
||||
uint16_t f_inode_size; /* size of an inode */
|
||||
uint16_t f_dat_entry_size; /* size of a dat entry */
|
||||
uint16_t f_checkpoint_size; /* size of a checkpoint */
|
||||
uint16_t f_segment_usage_size; /* size of a segment usage */
|
||||
|
||||
uint16_t f_sbbytes; /* byte count of CRC calculation
|
||||
for super blocks. s_reserved
|
||||
is excluded! */
|
||||
|
||||
uint16_t f_errors; /* behaviour on detecting errors */
|
||||
|
||||
uint32_t f_erasesize;
|
||||
uint64_t f_nsegments; /* number of segm. in filesystem */
|
||||
nandfs_daddr_t f_first_data_block; /* 1st seg disk block number */
|
||||
uint32_t f_blocks_per_segment; /* number of blocks per segment */
|
||||
uint32_t f_r_segments_percentage; /* reserved segments percentage */
|
||||
|
||||
struct uuid f_uuid; /* 128-bit uuid for volume */
|
||||
char f_volume_name[16]; /* volume name */
|
||||
uint32_t f_pad[104];
|
||||
} __packed;
|
||||
|
||||
#ifdef _KERNEL
|
||||
CTASSERT(sizeof(struct nandfs_fsdata) == 512);
|
||||
#endif
|
||||
|
||||
struct nandfs_super_block {
|
||||
uint16_t s_magic; /* magic value for identification */
|
||||
|
||||
uint32_t s_sum; /* check sum of super block */
|
||||
|
||||
uint64_t s_last_cno; /* last checkpoint number */
|
||||
uint64_t s_last_pseg; /* addr part. segm. written last */
|
||||
uint64_t s_last_seq; /* seq.number of seg written last */
|
||||
uint64_t s_free_blocks_count; /* free blocks count */
|
||||
|
||||
uint64_t s_mtime; /* mount time */
|
||||
uint64_t s_wtime; /* write time */
|
||||
uint16_t s_state; /* file system state */
|
||||
|
||||
char s_last_mounted[64]; /* directory where last mounted */
|
||||
|
||||
uint32_t s_c_interval; /* commit interval of segment */
|
||||
uint32_t s_c_block_max; /* threshold of data amount for
|
||||
the segment construction */
|
||||
uint32_t s_reserved[32]; /* padding to end of the block */
|
||||
} __packed;
|
||||
|
||||
#ifdef _KERNEL
|
||||
CTASSERT(sizeof(struct nandfs_super_block) == 256);
|
||||
#endif
|
||||
|
||||
#define NANDFS_FSDATA_MAGIC 0xf8da
|
||||
#define NANDFS_SUPER_MAGIC 0x8008
|
||||
|
||||
#define NANDFS_NFSAREAS 4
|
||||
#define NANDFS_DATA_OFFSET_BYTES(esize) (NANDFS_NFSAREAS * (esize))
|
||||
|
||||
#define NANDFS_SBLOCK_OFFSET_BYTES (sizeof(struct nandfs_fsdata))
|
||||
|
||||
#define NANDFS_DEF_BLOCKSIZE 4096
|
||||
#define NANDFS_MIN_BLOCKSIZE 512
|
||||
|
||||
#define NANDFS_DEF_ERASESIZE (2 << 16)
|
||||
|
||||
#define NANDFS_MIN_SEGSIZE NANDFS_DEF_ERASESIZE
|
||||
|
||||
#define NANDFS_CURRENT_REV 9 /* current major revision */
|
||||
|
||||
#define NANDFS_FSDATA_CRC_BYTES offsetof(struct nandfs_fsdata, f_pad)
|
||||
/* Bytes count of super_block for CRC-calculation */
|
||||
#define NANDFS_SB_BYTES offsetof(struct nandfs_super_block, s_reserved)
|
||||
|
||||
/* Maximal count of links to a file */
|
||||
#define NANDFS_LINK_MAX 32000
|
||||
|
||||
/*
|
||||
* Structure of a directory entry.
|
||||
*
|
||||
* Note that they can't span blocks; the rec_len fills out.
|
||||
*/
|
||||
|
||||
#define NANDFS_NAME_LEN 255
|
||||
struct nandfs_dir_entry {
|
||||
uint64_t inode; /* inode number */
|
||||
uint16_t rec_len; /* directory entry length */
|
||||
uint8_t name_len; /* name length */
|
||||
uint8_t file_type;
|
||||
char name[NANDFS_NAME_LEN]; /* file name */
|
||||
char pad;
|
||||
};
|
||||
|
||||
/*
|
||||
* NANDFS_DIR_PAD defines the directory entries boundaries
|
||||
*
|
||||
* NOTE: It must be a multiple of 8
|
||||
*/
|
||||
#define NANDFS_DIR_PAD 8
|
||||
#define NANDFS_DIR_ROUND (NANDFS_DIR_PAD - 1)
|
||||
#define NANDFS_DIR_NAME_OFFSET (offsetof(struct nandfs_dir_entry, name))
|
||||
#define NANDFS_DIR_REC_LEN(name_len) \
|
||||
(((name_len) + NANDFS_DIR_NAME_OFFSET + NANDFS_DIR_ROUND) \
|
||||
& ~NANDFS_DIR_ROUND)
|
||||
#define NANDFS_DIR_NAME_LEN(name_len) \
|
||||
(NANDFS_DIR_REC_LEN(name_len) - NANDFS_DIR_NAME_OFFSET)
|
||||
|
||||
/*
|
||||
* NiLFS/NANDFS devides the disc into fixed length segments. Each segment is
|
||||
* filled with one or more partial segments of variable lengths.
|
||||
*
|
||||
* Each partial segment has a segment summary header followed by updates of
|
||||
* files and optionally a super root.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Virtual to physical block translation information. For data blocks it maps
|
||||
* logical block number bi_blkoff to virtual block nr bi_vblocknr. For non
|
||||
* datablocks it is the virtual block number assigned to an indirect block
|
||||
* and has no bi_blkoff. The physical block number is the next
|
||||
* available data block in the partial segment after all the binfo's.
|
||||
*/
|
||||
struct nandfs_binfo_v {
|
||||
uint64_t bi_ino; /* file's inode */
|
||||
uint64_t bi_vblocknr; /* assigned virtual block number */
|
||||
uint64_t bi_blkoff; /* for file's logical block number */
|
||||
};
|
||||
|
||||
/*
|
||||
* DAT allocation. For data blocks just the logical block number that maps on
|
||||
* the next available data block in the partial segment after the binfo's.
|
||||
*/
|
||||
struct nandfs_binfo_dat {
|
||||
uint64_t bi_ino;
|
||||
uint64_t bi_blkoff; /* DAT file's logical block number */
|
||||
uint8_t bi_level; /* whether this is meta block */
|
||||
uint8_t bi_pad[7];
|
||||
};
|
||||
|
||||
#ifdef _KERNEL
|
||||
CTASSERT(sizeof(struct nandfs_binfo_v) == sizeof(struct nandfs_binfo_dat));
|
||||
#endif
|
||||
|
||||
/* Convenience union for both types of binfo's */
|
||||
union nandfs_binfo {
|
||||
struct nandfs_binfo_v bi_v;
|
||||
struct nandfs_binfo_dat bi_dat;
|
||||
};
|
||||
|
||||
/* Indirect buffers path */
|
||||
struct nandfs_indir {
|
||||
nandfs_daddr_t in_lbn;
|
||||
int in_off;
|
||||
};
|
||||
|
||||
/* The (partial) segment summary */
|
||||
struct nandfs_segment_summary {
|
||||
uint32_t ss_datasum; /* CRC of complete data block */
|
||||
uint32_t ss_sumsum; /* CRC of segment summary only */
|
||||
uint32_t ss_magic; /* magic to identify segment summary */
|
||||
uint16_t ss_bytes; /* size of segment summary structure */
|
||||
uint16_t ss_flags; /* NANDFS_SS_* flags */
|
||||
uint64_t ss_seq; /* sequence number of this segm. sum */
|
||||
uint64_t ss_create; /* creation timestamp in seconds */
|
||||
uint64_t ss_next; /* blocknumber of next segment */
|
||||
uint32_t ss_nblocks; /* number of blocks used by summary */
|
||||
uint32_t ss_nbinfos; /* number of binfo structures */
|
||||
uint32_t ss_sumbytes; /* total size of segment summary */
|
||||
uint32_t ss_pad;
|
||||
/* stream of binfo structures */
|
||||
};
|
||||
|
||||
#define NANDFS_SEGSUM_MAGIC 0x8e680011 /* segment summary magic number */
|
||||
|
||||
/* Segment summary flags */
|
||||
#define NANDFS_SS_LOGBGN 0x0001 /* begins a logical segment */
|
||||
#define NANDFS_SS_LOGEND 0x0002 /* ends a logical segment */
|
||||
#define NANDFS_SS_SR 0x0004 /* has super root */
|
||||
#define NANDFS_SS_SYNDT 0x0008 /* includes data only updates */
|
||||
#define NANDFS_SS_GC 0x0010 /* segment written for cleaner operation */
|
||||
#define NANDFS_SS_FLAG_BITS "\20\1LOGBGN\2LOGEND\3SR\4SYNDT\5GC"
|
||||
|
||||
/* Segment summary constrains */
|
||||
#define NANDFS_SEG_MIN_BLOCKS 16 /* minimum number of blocks in a
|
||||
full segment */
|
||||
#define NANDFS_PSEG_MIN_BLOCKS 2 /* minimum number of blocks in a
|
||||
partial segment */
|
||||
#define NANDFS_MIN_NRSVSEGS 8 /* minimum number of reserved
|
||||
segments */
|
||||
|
||||
/*
|
||||
* Structure of DAT/inode file.
|
||||
*
|
||||
* A DAT file is devided into groups. The maximum number of groups is the
|
||||
* number of block group descriptors that fit into one block; this descriptor
|
||||
* only gives the number of free entries in the associated group.
|
||||
*
|
||||
* Each group has a block sized bitmap indicating if an entry is taken or
|
||||
* empty. Each bit stands for a DAT entry.
|
||||
*
|
||||
* The inode file has exactly the same format only the entries are inode
|
||||
* entries.
|
||||
*/
|
||||
|
||||
struct nandfs_block_group_desc {
|
||||
uint32_t bg_nfrees; /* num. free entries in block group */
|
||||
};
|
||||
|
||||
/* DAT entry in a super root's DAT file */
|
||||
struct nandfs_dat_entry {
|
||||
uint64_t de_blocknr; /* block number */
|
||||
uint64_t de_start; /* valid from checkpoint */
|
||||
uint64_t de_end; /* valid till checkpoint */
|
||||
uint64_t de_rsv; /* reserved for future use */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure of CP file.
|
||||
*
|
||||
* A snapshot is just a checkpoint only it's protected against removal by the
|
||||
* cleaner. The snapshots are kept on a double linked list of checkpoints.
|
||||
*/
|
||||
struct nandfs_snapshot_list {
|
||||
uint64_t ssl_next; /* checkpoint nr. forward */
|
||||
uint64_t ssl_prev; /* checkpoint nr. back */
|
||||
};
|
||||
|
||||
/* Checkpoint entry structure */
|
||||
struct nandfs_checkpoint {
|
||||
uint32_t cp_flags; /* NANDFS_CHECKPOINT_* flags */
|
||||
uint32_t cp_checkpoints_count; /* ZERO, not used anymore? */
|
||||
struct nandfs_snapshot_list cp_snapshot_list; /* list of snapshots */
|
||||
uint64_t cp_cno; /* checkpoint number */
|
||||
uint64_t cp_create; /* creation timestamp */
|
||||
uint64_t cp_nblk_inc; /* number of blocks incremented */
|
||||
uint64_t cp_blocks_count; /* reserved (might be deleted) */
|
||||
struct nandfs_inode cp_ifile_inode; /* inode file inode */
|
||||
};
|
||||
|
||||
/* Checkpoint flags */
|
||||
#define NANDFS_CHECKPOINT_SNAPSHOT 1
|
||||
#define NANDFS_CHECKPOINT_INVALID 2
|
||||
#define NANDFS_CHECKPOINT_SKETCH 4
|
||||
#define NANDFS_CHECKPOINT_MINOR 8
|
||||
#define NANDFS_CHECKPOINT_BITS "\20\1SNAPSHOT\2INVALID\3SKETCH\4MINOR"
|
||||
|
||||
/* Header of the checkpoint file */
|
||||
struct nandfs_cpfile_header {
|
||||
uint64_t ch_ncheckpoints; /* number of checkpoints */
|
||||
uint64_t ch_nsnapshots; /* number of snapshots */
|
||||
struct nandfs_snapshot_list ch_snapshot_list; /* snapshot list */
|
||||
};
|
||||
|
||||
#define NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET \
|
||||
((sizeof(struct nandfs_cpfile_header) + \
|
||||
sizeof(struct nandfs_checkpoint) - 1) / \
|
||||
sizeof(struct nandfs_checkpoint))
|
||||
|
||||
|
||||
#define NANDFS_NOSEGMENT 0xffffffff
|
||||
|
||||
/*
|
||||
* Structure of SU file.
|
||||
*
|
||||
* The segment usage file sums up how each of the segments are used. They are
|
||||
* indexed by their segment number.
|
||||
*/
|
||||
|
||||
/* Segment usage entry */
|
||||
struct nandfs_segment_usage {
|
||||
uint64_t su_lastmod; /* last modified timestamp */
|
||||
uint32_t su_nblocks; /* number of blocks in segment */
|
||||
uint32_t su_flags; /* NANDFS_SEGMENT_USAGE_* flags */
|
||||
};
|
||||
|
||||
/* Segment usage flag */
|
||||
#define NANDFS_SEGMENT_USAGE_ACTIVE 1
|
||||
#define NANDFS_SEGMENT_USAGE_DIRTY 2
|
||||
#define NANDFS_SEGMENT_USAGE_ERROR 4
|
||||
#define NANDFS_SEGMENT_USAGE_GC 8
|
||||
#define NANDFS_SEGMENT_USAGE_BITS "\20\1ACTIVE\2DIRTY\3ERROR"
|
||||
|
||||
/* Header of the segment usage file */
|
||||
struct nandfs_sufile_header {
|
||||
uint64_t sh_ncleansegs; /* number of segments marked clean */
|
||||
uint64_t sh_ndirtysegs; /* number of segments marked dirty */
|
||||
uint64_t sh_last_alloc; /* last allocated segment number */
|
||||
};
|
||||
|
||||
#define NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET \
|
||||
((sizeof(struct nandfs_sufile_header) + \
|
||||
sizeof(struct nandfs_segment_usage) - 1) / \
|
||||
sizeof(struct nandfs_segment_usage))
|
||||
|
||||
struct nandfs_seg_stat {
|
||||
uint64_t nss_nsegs;
|
||||
uint64_t nss_ncleansegs;
|
||||
uint64_t nss_ndirtysegs;
|
||||
uint64_t nss_ctime;
|
||||
uint64_t nss_nongc_ctime;
|
||||
uint64_t nss_prot_seq;
|
||||
};
|
||||
|
||||
enum {
|
||||
NANDFS_CHECKPOINT,
|
||||
NANDFS_SNAPSHOT
|
||||
};
|
||||
|
||||
#define NANDFS_CPINFO_MAX 512
|
||||
|
||||
struct nandfs_cpinfo {
|
||||
uint32_t nci_flags;
|
||||
uint32_t nci_pad;
|
||||
uint64_t nci_cno;
|
||||
uint64_t nci_create;
|
||||
uint64_t nci_nblk_inc;
|
||||
uint64_t nci_blocks_count;
|
||||
uint64_t nci_next;
|
||||
};
|
||||
|
||||
#define NANDFS_SEGMENTS_MAX 512
|
||||
|
||||
struct nandfs_suinfo {
|
||||
uint64_t nsi_num;
|
||||
uint64_t nsi_lastmod;
|
||||
uint32_t nsi_blocks;
|
||||
uint32_t nsi_flags;
|
||||
};
|
||||
|
||||
#define NANDFS_VINFO_MAX 512
|
||||
|
||||
struct nandfs_vinfo {
|
||||
uint64_t nvi_ino;
|
||||
uint64_t nvi_vblocknr;
|
||||
uint64_t nvi_start;
|
||||
uint64_t nvi_end;
|
||||
uint64_t nvi_blocknr;
|
||||
int nvi_alive;
|
||||
};
|
||||
|
||||
struct nandfs_cpmode {
|
||||
uint64_t ncpm_cno;
|
||||
uint32_t ncpm_mode;
|
||||
uint32_t ncpm_pad;
|
||||
};
|
||||
|
||||
struct nandfs_argv {
|
||||
uint64_t nv_base;
|
||||
uint32_t nv_nmembs;
|
||||
uint16_t nv_size;
|
||||
uint16_t nv_flags;
|
||||
uint64_t nv_index;
|
||||
};
|
||||
|
||||
struct nandfs_cpstat {
|
||||
uint64_t ncp_cno;
|
||||
uint64_t ncp_ncps;
|
||||
uint64_t ncp_nss;
|
||||
};
|
||||
|
||||
struct nandfs_period {
|
||||
uint64_t p_start;
|
||||
uint64_t p_end;
|
||||
};
|
||||
|
||||
struct nandfs_vdesc {
|
||||
uint64_t vd_ino;
|
||||
uint64_t vd_cno;
|
||||
uint64_t vd_vblocknr;
|
||||
struct nandfs_period vd_period;
|
||||
uint64_t vd_blocknr;
|
||||
uint64_t vd_offset;
|
||||
uint32_t vd_flags;
|
||||
uint32_t vd_pad;
|
||||
};
|
||||
|
||||
struct nandfs_bdesc {
|
||||
uint64_t bd_ino;
|
||||
uint64_t bd_oblocknr;
|
||||
uint64_t bd_blocknr;
|
||||
uint64_t bd_offset;
|
||||
uint32_t bd_level;
|
||||
uint32_t bd_alive;
|
||||
};
|
||||
|
||||
#ifndef _KERNEL
|
||||
#ifndef MNAMELEN
|
||||
#define MNAMELEN 88
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct nandfs_fsinfo {
|
||||
struct nandfs_fsdata fs_fsdata;
|
||||
struct nandfs_super_block fs_super;
|
||||
char fs_dev[MNAMELEN];
|
||||
};
|
||||
|
||||
#define NANDFS_MAX_MOUNTS 65535
|
||||
|
||||
#define NANDFS_IOCTL_GET_SUSTAT _IOR('N', 100, struct nandfs_seg_stat)
|
||||
#define NANDFS_IOCTL_CHANGE_CPMODE _IOWR('N', 101, struct nandfs_cpmode)
|
||||
#define NANDFS_IOCTL_GET_CPINFO _IOWR('N', 102, struct nandfs_argv)
|
||||
#define NANDFS_IOCTL_DELETE_CP _IOWR('N', 103, uint64_t[2])
|
||||
#define NANDFS_IOCTL_GET_CPSTAT _IOR('N', 104, struct nandfs_cpstat)
|
||||
#define NANDFS_IOCTL_GET_SUINFO _IOWR('N', 105, struct nandfs_argv)
|
||||
#define NANDFS_IOCTL_GET_VINFO _IOWR('N', 106, struct nandfs_argv)
|
||||
#define NANDFS_IOCTL_GET_BDESCS _IOWR('N', 107, struct nandfs_argv)
|
||||
#define NANDFS_IOCTL_GET_FSINFO _IOR('N', 108, struct nandfs_fsinfo)
|
||||
#define NANDFS_IOCTL_MAKE_SNAP _IOWR('N', 109, uint64_t)
|
||||
#define NANDFS_IOCTL_DELETE_SNAP _IOWR('N', 110, uint64_t)
|
||||
#define NANDFS_IOCTL_SYNC _IOWR('N', 111, uint64_t)
|
||||
|
||||
#endif /* _NANDFS_FS_H */
|
213
sys/fs/nandfs/nandfs_ifile.c
Normal file
213
sys/fs/nandfs/nandfs_ifile.c
Normal file
@ -0,0 +1,213 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_mount.h>
|
||||
#include <fs/nandfs/nandfs.h>
|
||||
#include <fs/nandfs/nandfs_subr.h>
|
||||
|
||||
int
|
||||
nandfs_node_create(struct nandfsmount *nmp, struct nandfs_node **node,
|
||||
uint16_t mode)
|
||||
{
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfs_device *nandfsdev;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_node *ifile;
|
||||
struct nandfs_inode *inode;
|
||||
struct vnode *vp;
|
||||
uint32_t entry;
|
||||
int error = 0;
|
||||
|
||||
nandfsdev = nmp->nm_nandfsdev;
|
||||
mdt = &nandfsdev->nd_ifile_mdt;
|
||||
ifile = nmp->nm_ifile_node;
|
||||
vp = NTOV(ifile);
|
||||
|
||||
VOP_LOCK(vp, LK_EXCLUSIVE);
|
||||
/* Allocate new inode in ifile */
|
||||
req.entrynum = nandfsdev->nd_last_ino + 1;
|
||||
error = nandfs_find_free_entry(mdt, ifile, &req);
|
||||
if (error) {
|
||||
VOP_UNLOCK(vp, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = nandfs_get_entry_block(mdt, ifile, &req, &entry, 1);
|
||||
if (error) {
|
||||
VOP_UNLOCK(vp, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Inode initialization */
|
||||
inode = ((struct nandfs_inode *) req.bp_entry->b_data) + entry;
|
||||
nandfs_inode_init(inode, mode);
|
||||
|
||||
error = nandfs_alloc_entry(mdt, &req);
|
||||
if (error) {
|
||||
VOP_UNLOCK(vp, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
VOP_UNLOCK(vp, 0);
|
||||
|
||||
nandfsdev->nd_last_ino = req.entrynum;
|
||||
error = nandfs_get_node(nmp, req.entrynum, node);
|
||||
DPRINTF(IFILE, ("%s: node: %p ino: %#jx\n",
|
||||
__func__, node, (uintmax_t)((*node)->nn_ino)));
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_node_destroy(struct nandfs_node *node)
|
||||
{
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfsmount *nmp;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_node *ifile;
|
||||
struct vnode *vp;
|
||||
int error = 0;
|
||||
|
||||
nmp = node->nn_nmp;
|
||||
req.entrynum = node->nn_ino;
|
||||
mdt = &nmp->nm_nandfsdev->nd_ifile_mdt;
|
||||
ifile = nmp->nm_ifile_node;
|
||||
vp = NTOV(ifile);
|
||||
|
||||
DPRINTF(IFILE, ("%s: destroy node: %p ino: %#jx\n",
|
||||
__func__, node, (uintmax_t)node->nn_ino));
|
||||
VOP_LOCK(vp, LK_EXCLUSIVE);
|
||||
|
||||
error = nandfs_find_entry(mdt, ifile, &req);
|
||||
if (error) {
|
||||
nandfs_error("%s: finding entry error:%d node %p(%jx)",
|
||||
__func__, error, node, node->nn_ino);
|
||||
VOP_UNLOCK(vp, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
nandfs_inode_destroy(&node->nn_inode);
|
||||
|
||||
error = nandfs_free_entry(mdt, &req);
|
||||
if (error) {
|
||||
nandfs_error("%s: freing entry error:%d node %p(%jx)",
|
||||
__func__, error, node, node->nn_ino);
|
||||
VOP_UNLOCK(vp, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
VOP_UNLOCK(vp, 0);
|
||||
DPRINTF(IFILE, ("%s: freed node %p ino %#jx\n",
|
||||
__func__, node, (uintmax_t)node->nn_ino));
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_node_update(struct nandfs_node *node)
|
||||
{
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfsmount *nmp;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_node *ifile;
|
||||
struct nandfs_inode *inode;
|
||||
uint32_t index;
|
||||
int error = 0;
|
||||
|
||||
nmp = node->nn_nmp;
|
||||
ifile = nmp->nm_ifile_node;
|
||||
ASSERT_VOP_LOCKED(NTOV(ifile), __func__);
|
||||
|
||||
req.entrynum = node->nn_ino;
|
||||
mdt = &nmp->nm_nandfsdev->nd_ifile_mdt;
|
||||
|
||||
DPRINTF(IFILE, ("%s: node:%p ino:%#jx\n",
|
||||
__func__, &node->nn_inode, (uintmax_t)node->nn_ino));
|
||||
|
||||
error = nandfs_get_entry_block(mdt, ifile, &req, &index, 0);
|
||||
if (error) {
|
||||
printf("nandfs_get_entry_block returned with ERROR=%d\n",
|
||||
error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
inode = ((struct nandfs_inode *) req.bp_entry->b_data) + index;
|
||||
memcpy(inode, &node->nn_inode, sizeof(*inode));
|
||||
error = nandfs_dirty_buf(req.bp_entry, 0);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_node_entry(struct nandfsmount *nmp, struct nandfs_inode **inode,
|
||||
uint64_t ino, struct buf **bp)
|
||||
{
|
||||
struct nandfs_alloc_request req;
|
||||
struct nandfs_mdt *mdt;
|
||||
struct nandfs_node *ifile;
|
||||
struct vnode *vp;
|
||||
uint32_t index;
|
||||
int error = 0;
|
||||
|
||||
req.entrynum = ino;
|
||||
mdt = &nmp->nm_nandfsdev->nd_ifile_mdt;
|
||||
ifile = nmp->nm_ifile_node;
|
||||
vp = NTOV(ifile);
|
||||
|
||||
VOP_LOCK(vp, LK_EXCLUSIVE);
|
||||
error = nandfs_get_entry_block(mdt, ifile, &req, &index, 0);
|
||||
if (error) {
|
||||
VOP_UNLOCK(vp, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
*inode = ((struct nandfs_inode *) req.bp_entry->b_data) + index;
|
||||
*bp = req.bp_entry;
|
||||
VOP_UNLOCK(vp, 0);
|
||||
return (0);
|
||||
}
|
||||
|
50
sys/fs/nandfs/nandfs_mount.h
Normal file
50
sys/fs/nandfs/nandfs_mount.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*-
|
||||
* Copyright (c) 2008, 2009 Reinoud Zandijk
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the
|
||||
* NetBSD Project. See http://www.NetBSD.org/ for
|
||||
* information about NetBSD.
|
||||
* 4. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* From: NetBSD: nilfs_mount.h,v 1.1 2009/07/18 16:31:42 reinoud
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _FS_NANDFS_NANDFS_MOUNT_H_
|
||||
#define _FS_NANDFS_NANDFS_MOUNT_H_
|
||||
|
||||
/*
|
||||
* Arguments to mount NANDFS filingsystem.
|
||||
*/
|
||||
|
||||
struct nandfs_args {
|
||||
char *fspec; /* mount specifier */
|
||||
int64_t cpno; /* checkpoint number */
|
||||
};
|
||||
|
||||
#endif /* !_FS_NANDFS_NANDFS_MOUNT_H_ */
|
||||
|
1329
sys/fs/nandfs/nandfs_segment.c
Normal file
1329
sys/fs/nandfs/nandfs_segment.c
Normal file
File diff suppressed because it is too large
Load Diff
1120
sys/fs/nandfs/nandfs_subr.c
Normal file
1120
sys/fs/nandfs/nandfs_subr.c
Normal file
File diff suppressed because it is too large
Load Diff
238
sys/fs/nandfs/nandfs_subr.h
Normal file
238
sys/fs/nandfs/nandfs_subr.h
Normal file
@ -0,0 +1,238 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf
|
||||
* Copyright (c) 2008, 2009 Reinoud Zandijk
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* From: NetBSD: nilfs_subr.h,v 1.1 2009/07/18 16:31:42 reinoud
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _FS_NANDFS_NANDFS_SUBR_H_
|
||||
#define _FS_NANDFS_NANDFS_SUBR_H_
|
||||
|
||||
struct nandfs_mdt;
|
||||
|
||||
struct nandfs_alloc_request
|
||||
{
|
||||
uint64_t entrynum;
|
||||
struct buf *bp_desc;
|
||||
struct buf *bp_bitmap;
|
||||
struct buf *bp_entry;
|
||||
};
|
||||
|
||||
/* Segment creation */
|
||||
void nandfs_wakeup_wait_sync(struct nandfs_device *, int);
|
||||
int nandfs_segment_constructor(struct nandfsmount *, int);
|
||||
int nandfs_sync_file(struct vnode *);
|
||||
|
||||
/* Basic calculators */
|
||||
uint64_t nandfs_get_segnum_of_block(struct nandfs_device *, nandfs_daddr_t);
|
||||
void nandfs_get_segment_range(struct nandfs_device *, uint64_t, uint64_t *,
|
||||
uint64_t *);
|
||||
void nandfs_calc_mdt_consts(struct nandfs_device *, struct nandfs_mdt *, int);
|
||||
|
||||
/* Log reading / volume helpers */
|
||||
int nandfs_search_super_root(struct nandfs_device *);
|
||||
|
||||
/* Reading */
|
||||
int nandfs_dev_bread(struct nandfs_device *, nandfs_daddr_t, struct ucred *,
|
||||
int, struct buf **);
|
||||
int nandfs_bread(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int,
|
||||
struct buf **);
|
||||
int nandfs_bread_meta(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int,
|
||||
struct buf **);
|
||||
int nandfs_bdestroy(struct nandfs_node *, nandfs_daddr_t);
|
||||
int nandfs_bcreate(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int,
|
||||
struct buf **);
|
||||
int nandfs_bcreate_meta(struct nandfs_node *, nandfs_lbn_t, struct ucred *,
|
||||
int, struct buf **);
|
||||
int nandfs_bread_create(struct nandfs_node *, nandfs_lbn_t, struct ucred *,
|
||||
int, struct buf **);
|
||||
|
||||
/* vtop operations */
|
||||
int nandfs_vtop(struct nandfs_node *, nandfs_daddr_t, nandfs_daddr_t *);
|
||||
|
||||
/* Node action implementators */
|
||||
int nandfs_vinit(struct vnode *, uint64_t);
|
||||
int nandfs_get_node(struct nandfsmount *, uint64_t, struct nandfs_node **);
|
||||
int nandfs_get_node_raw(struct nandfs_device *, struct nandfsmount *, uint64_t,
|
||||
struct nandfs_inode *, struct nandfs_node **);
|
||||
void nandfs_dispose_node(struct nandfs_node **);
|
||||
|
||||
void nandfs_itimes(struct vnode *);
|
||||
int nandfs_lookup_name_in_dir(struct vnode *, const char *, int, uint64_t *,
|
||||
int *, uint64_t *);
|
||||
int nandfs_create_node(struct vnode *, struct vnode **, struct vattr *,
|
||||
struct componentname *);
|
||||
void nandfs_delete_node(struct nandfs_node *);
|
||||
|
||||
int nandfs_chsize(struct vnode *, u_quad_t, struct ucred *);
|
||||
int nandfs_dir_detach(struct nandfsmount *, struct nandfs_node *,
|
||||
struct nandfs_node *, struct componentname *);
|
||||
int nandfs_dir_attach(struct nandfsmount *, struct nandfs_node *,
|
||||
struct nandfs_node *, struct vattr *, struct componentname *);
|
||||
|
||||
int nandfs_dirty_buf(struct buf *, int);
|
||||
int nandfs_dirty_buf_meta(struct buf *, int);
|
||||
int nandfs_fs_full(struct nandfs_device *);
|
||||
void nandfs_undirty_buf_fsdev(struct nandfs_device *, struct buf *);
|
||||
void nandfs_undirty_buf(struct buf *);
|
||||
|
||||
void nandfs_clear_buf(struct buf *);
|
||||
void nandfs_buf_set(struct buf *, uint32_t);
|
||||
void nandfs_buf_clear(struct buf *, uint32_t);
|
||||
int nandfs_buf_check(struct buf *, uint32_t);
|
||||
|
||||
int nandfs_find_free_entry(struct nandfs_mdt *, struct nandfs_node *,
|
||||
struct nandfs_alloc_request *);
|
||||
int nandfs_find_entry(struct nandfs_mdt *, struct nandfs_node *,
|
||||
struct nandfs_alloc_request *);
|
||||
int nandfs_alloc_entry(struct nandfs_mdt *, struct nandfs_alloc_request *);
|
||||
void nandfs_abort_entry(struct nandfs_alloc_request *);
|
||||
int nandfs_free_entry(struct nandfs_mdt *, struct nandfs_alloc_request *);
|
||||
int nandfs_get_entry_block(struct nandfs_mdt *, struct nandfs_node *,
|
||||
struct nandfs_alloc_request *, uint32_t *, int);
|
||||
|
||||
/* inode managment */
|
||||
int nandfs_node_create(struct nandfsmount *, struct nandfs_node **, uint16_t);
|
||||
int nandfs_node_destroy(struct nandfs_node *);
|
||||
int nandfs_node_update(struct nandfs_node *);
|
||||
int nandfs_get_node_entry(struct nandfsmount *, struct nandfs_inode **,
|
||||
uint64_t, struct buf **);
|
||||
void nandfs_mdt_trans_blk(struct nandfs_mdt *, uint64_t, uint64_t *,
|
||||
uint64_t *, nandfs_lbn_t *, uint32_t *);
|
||||
|
||||
/* vblock management */
|
||||
void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t, nandfs_lbn_t *, uint32_t *);
|
||||
int nandfs_vblock_alloc(struct nandfs_device *, nandfs_daddr_t *);
|
||||
int nandfs_vblock_end(struct nandfs_device *, nandfs_daddr_t);
|
||||
int nandfs_vblock_assign(struct nandfs_device *, nandfs_daddr_t,
|
||||
nandfs_lbn_t);
|
||||
int nandfs_vblock_free(struct nandfs_device *, nandfs_daddr_t);
|
||||
|
||||
/* Checkpoint management */
|
||||
int nandfs_get_checkpoint(struct nandfs_device *, struct nandfs_node *,
|
||||
uint64_t);
|
||||
int nandfs_set_checkpoint(struct nandfs_device *, struct nandfs_node *,
|
||||
uint64_t, struct nandfs_inode *, uint64_t);
|
||||
|
||||
/* Segment management */
|
||||
int nandfs_alloc_segment(struct nandfs_device *, uint64_t *);
|
||||
int nandfs_update_segment(struct nandfs_device *, uint64_t, uint32_t);
|
||||
int nandfs_free_segment(struct nandfs_device *, uint64_t);
|
||||
int nandfs_clear_segment(struct nandfs_device *, uint64_t);
|
||||
int nandfs_touch_segment(struct nandfs_device *, uint64_t);
|
||||
int nandfs_markgc_segment(struct nandfs_device *, uint64_t);
|
||||
|
||||
int nandfs_bmap_insert_block(struct nandfs_node *, nandfs_lbn_t, struct buf *);
|
||||
int nandfs_bmap_update_block(struct nandfs_node *, struct buf *, nandfs_lbn_t);
|
||||
int nandfs_bmap_update_dat(struct nandfs_node *, nandfs_daddr_t, struct buf *);
|
||||
int nandfs_bmap_dirty_blocks(struct nandfs_node *, struct buf *, int);
|
||||
int nandfs_bmap_truncate_mapping(struct nandfs_node *, nandfs_lbn_t,
|
||||
nandfs_lbn_t);
|
||||
int nandfs_bmap_lookup(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t *);
|
||||
|
||||
/* dirent */
|
||||
int nandfs_add_dirent(struct vnode *, uint64_t, char *, long, uint8_t);
|
||||
int nandfs_remove_dirent(struct vnode *, struct nandfs_node *,
|
||||
struct componentname *);
|
||||
int nandfs_update_dirent(struct vnode *, struct nandfs_node *,
|
||||
struct nandfs_node *);
|
||||
int nandfs_init_dir(struct vnode *, uint64_t, uint64_t);
|
||||
int nandfs_update_parent_dir(struct vnode *, uint64_t);
|
||||
|
||||
void nandfs_vblk_set(struct buf *, nandfs_daddr_t);
|
||||
nandfs_daddr_t nandfs_vblk_get(struct buf *);
|
||||
|
||||
void nandfs_inode_init(struct nandfs_inode *, uint16_t);
|
||||
void nandfs_inode_destroy(struct nandfs_inode *);
|
||||
|
||||
/* ioctl */
|
||||
int nandfs_get_seg_stat(struct nandfs_device *, struct nandfs_seg_stat *);
|
||||
int nandfs_chng_cpmode(struct nandfs_node *, struct nandfs_cpmode *);
|
||||
int nandfs_get_cpinfo_ioctl(struct nandfs_node *, struct nandfs_argv *);
|
||||
int nandfs_delete_cp(struct nandfs_node *, uint64_t start, uint64_t);
|
||||
int nandfs_make_snap(struct nandfs_device *, uint64_t *);
|
||||
int nandfs_delete_snap(struct nandfs_device *, uint64_t);
|
||||
int nandfs_get_cpstat(struct nandfs_node *, struct nandfs_cpstat *);
|
||||
int nandfs_get_segment_info_ioctl(struct nandfs_device *, struct nandfs_argv *);
|
||||
int nandfs_get_dat_vinfo_ioctl(struct nandfs_device *, struct nandfs_argv *);
|
||||
int nandfs_get_dat_bdescs_ioctl(struct nandfs_device *, struct nandfs_argv *);
|
||||
int nandfs_get_fsinfo(struct nandfsmount *, struct nandfs_fsinfo *);
|
||||
|
||||
int nandfs_get_cpinfo(struct nandfs_node *, uint64_t, uint16_t,
|
||||
struct nandfs_cpinfo *, uint32_t, uint32_t *);
|
||||
|
||||
nandfs_lbn_t nandfs_get_maxfilesize(struct nandfs_device *);
|
||||
|
||||
int nandfs_write_superblock(struct nandfs_device *);
|
||||
|
||||
extern int nandfs_sync_interval;
|
||||
extern int nandfs_max_dirty_segs;
|
||||
extern int nandfs_cps_between_sblocks;
|
||||
|
||||
struct buf *nandfs_geteblk(int, int);
|
||||
|
||||
void nandfs_dirty_bufs_increment(struct nandfs_device *);
|
||||
void nandfs_dirty_bufs_decrement(struct nandfs_device *);
|
||||
|
||||
int nandfs_start_cleaner(struct nandfs_device *);
|
||||
int nandfs_stop_cleaner(struct nandfs_device *);
|
||||
|
||||
int nandfs_segsum_valid(struct nandfs_segment_summary *);
|
||||
int nandfs_load_segsum(struct nandfs_device *, nandfs_daddr_t,
|
||||
struct nandfs_segment_summary *);
|
||||
int nandfs_get_segment_info(struct nandfs_device *, struct nandfs_suinfo *,
|
||||
uint32_t, uint64_t);
|
||||
int nandfs_get_segment_info_filter(struct nandfs_device *,
|
||||
struct nandfs_suinfo *, uint32_t, uint64_t, uint64_t *, uint32_t, uint32_t);
|
||||
int nandfs_get_dat_vinfo(struct nandfs_device *, struct nandfs_vinfo *,
|
||||
uint32_t);
|
||||
int nandfs_get_dat_bdescs(struct nandfs_device *, struct nandfs_bdesc *,
|
||||
uint32_t);
|
||||
|
||||
#define NANDFS_VBLK_ASSIGNED 1
|
||||
|
||||
#define NANDFS_IS_INDIRECT(bp) ((bp)->b_lblkno < 0)
|
||||
|
||||
int nandfs_erase(struct nandfs_device *, off_t, size_t);
|
||||
|
||||
#define NANDFS_VOP_ISLOCKED(vp) nandfs_vop_islocked((vp))
|
||||
int nandfs_vop_islocked(struct vnode *vp);
|
||||
|
||||
nandfs_daddr_t nandfs_block_to_dblock(struct nandfs_device *, nandfs_lbn_t);
|
||||
|
||||
#define DEBUG_MODE
|
||||
#if defined(DEBUG_MODE)
|
||||
#define nandfs_error panic
|
||||
#define nandfs_warning printf
|
||||
#elif defined(TEST_MODE)
|
||||
#define nandfs_error printf
|
||||
#define nandfs_warning printf
|
||||
#else
|
||||
#define nandfs_error(...)
|
||||
#define nandfs_warning(...)
|
||||
#endif
|
||||
|
||||
#endif /* !_FS_NANDFS_NANDFS_SUBR_H_ */
|
569
sys/fs/nandfs/nandfs_sufile.c
Normal file
569
sys/fs/nandfs/nandfs_sufile.c
Normal file
@ -0,0 +1,569 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bio.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include <geom/geom.h>
|
||||
#include <geom/geom_vfs.h>
|
||||
|
||||
#include <fs/nandfs/nandfs_mount.h>
|
||||
#include <fs/nandfs/nandfs.h>
|
||||
#include <fs/nandfs/nandfs_subr.h>
|
||||
|
||||
#define SU_USAGE_OFF(bp, offset) \
|
||||
((struct nandfs_segment_usage *)((bp)->b_data + offset))
|
||||
|
||||
static int
|
||||
nandfs_seg_usage_blk_offset(struct nandfs_device *fsdev, uint64_t seg,
|
||||
uint64_t *blk, uint64_t *offset)
|
||||
{
|
||||
uint64_t off;
|
||||
uint16_t seg_size;
|
||||
|
||||
seg_size = fsdev->nd_fsdata.f_segment_usage_size;
|
||||
|
||||
off = roundup(sizeof(struct nandfs_sufile_header), seg_size);
|
||||
off += (seg * seg_size);
|
||||
|
||||
*blk = off / fsdev->nd_blocksize;
|
||||
*offset = off % fsdev->nd_blocksize;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Alloc new segment */
|
||||
int
|
||||
nandfs_alloc_segment(struct nandfs_device *fsdev, uint64_t *seg)
|
||||
{
|
||||
struct nandfs_node *su_node;
|
||||
struct nandfs_sufile_header *su_header;
|
||||
struct nandfs_segment_usage *su_usage;
|
||||
struct buf *bp_header, *bp;
|
||||
uint64_t blk, vblk, offset, i, rest, nsegments;
|
||||
uint16_t seg_size;
|
||||
int error, found;
|
||||
|
||||
seg_size = fsdev->nd_fsdata.f_segment_usage_size;
|
||||
nsegments = fsdev->nd_fsdata.f_nsegments;
|
||||
|
||||
su_node = fsdev->nd_su_node;
|
||||
ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
|
||||
|
||||
/* Read header buffer */
|
||||
error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
|
||||
if (error) {
|
||||
brelse(bp_header);
|
||||
return (error);
|
||||
}
|
||||
|
||||
su_header = (struct nandfs_sufile_header *)bp_header->b_data;
|
||||
|
||||
/* Get last allocated segment */
|
||||
i = su_header->sh_last_alloc + 1;
|
||||
|
||||
found = 0;
|
||||
bp = NULL;
|
||||
while (!found) {
|
||||
nandfs_seg_usage_blk_offset(fsdev, i, &blk, &offset);
|
||||
if(blk != 0) {
|
||||
error = nandfs_bmap_lookup(su_node, blk, &vblk);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot find vblk for blk "
|
||||
"blk:%jx\n", __func__, blk);
|
||||
return (error);
|
||||
}
|
||||
if (vblk)
|
||||
error = nandfs_bread(su_node, blk, NOCRED, 0,
|
||||
&bp);
|
||||
else
|
||||
error = nandfs_bcreate(su_node, blk, NOCRED, 0,
|
||||
&bp);
|
||||
if (error) {
|
||||
nandfs_error("%s: cannot create/read "
|
||||
"vblk:%jx\n", __func__, vblk);
|
||||
if (bp)
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
su_usage = SU_USAGE_OFF(bp, offset);
|
||||
} else {
|
||||
su_usage = SU_USAGE_OFF(bp_header, offset);
|
||||
bp = bp_header;
|
||||
}
|
||||
|
||||
rest = (fsdev->nd_blocksize - offset) / seg_size;
|
||||
/* Go through all su usage in block */
|
||||
while (rest) {
|
||||
/* When last check start from beggining */
|
||||
if (i == nsegments)
|
||||
break;
|
||||
|
||||
if (!su_usage->su_flags) {
|
||||
su_usage->su_flags = 1;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
su_usage++;
|
||||
i++;
|
||||
|
||||
/* If all checked return error */
|
||||
if (i == su_header->sh_last_alloc) {
|
||||
DPRINTF(SEG, ("%s: cannot allocate segment \n",
|
||||
__func__));
|
||||
brelse(bp_header);
|
||||
if (blk != 0)
|
||||
brelse(bp);
|
||||
return (1);
|
||||
}
|
||||
rest--;
|
||||
}
|
||||
if (!found) {
|
||||
/* Otherwise read another block */
|
||||
if (blk != 0)
|
||||
brelse(bp);
|
||||
if (i == nsegments) {
|
||||
blk = 0;
|
||||
i = 0;
|
||||
} else
|
||||
blk++;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
*seg = i;
|
||||
su_header->sh_last_alloc = i;
|
||||
su_header->sh_ncleansegs--;
|
||||
su_header->sh_ndirtysegs++;
|
||||
|
||||
fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
|
||||
fsdev->nd_fsdata.f_blocks_per_segment;
|
||||
fsdev->nd_clean_segs--;
|
||||
|
||||
/*
|
||||
* It is mostly called from syncer() so we want to force
|
||||
* making buf dirty.
|
||||
*/
|
||||
error = nandfs_dirty_buf(bp_header, 1);
|
||||
if (error) {
|
||||
if (bp && bp != bp_header)
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
if (bp && bp != bp_header)
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)i));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
DPRINTF(SEG, ("%s: failed\n", __func__));
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make buffer dirty, it will be updated soon but first it need to be
|
||||
* gathered by syncer.
|
||||
*/
|
||||
int
|
||||
nandfs_touch_segment(struct nandfs_device *fsdev, uint64_t seg)
|
||||
{
|
||||
struct nandfs_node *su_node;
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset;
|
||||
int error;
|
||||
|
||||
su_node = fsdev->nd_su_node;
|
||||
ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
|
||||
|
||||
nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
|
||||
|
||||
error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
nandfs_error("%s: cannot preallocate new segment\n", __func__);
|
||||
return (error);
|
||||
} else
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Update block count of segment */
|
||||
int
|
||||
nandfs_update_segment(struct nandfs_device *fsdev, uint64_t seg, uint32_t nblks)
|
||||
{
|
||||
struct nandfs_node *su_node;
|
||||
struct nandfs_segment_usage *su_usage;
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset;
|
||||
int error;
|
||||
|
||||
su_node = fsdev->nd_su_node;
|
||||
ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
|
||||
|
||||
nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
|
||||
|
||||
error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
nandfs_error("%s: read block:%jx to update\n",
|
||||
__func__, blk);
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
su_usage = SU_USAGE_OFF(bp, offset);
|
||||
su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
|
||||
su_usage->su_flags = NANDFS_SEGMENT_USAGE_DIRTY;
|
||||
su_usage->su_nblocks += nblks;
|
||||
|
||||
DPRINTF(SEG, ("%s: seg:%#jx inc:%#x cur:%#x\n", __func__,
|
||||
(uintmax_t)seg, nblks, su_usage->su_nblocks));
|
||||
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Make segment free */
|
||||
int
|
||||
nandfs_free_segment(struct nandfs_device *fsdev, uint64_t seg)
|
||||
{
|
||||
struct nandfs_node *su_node;
|
||||
struct nandfs_sufile_header *su_header;
|
||||
struct nandfs_segment_usage *su_usage;
|
||||
struct buf *bp_header, *bp;
|
||||
uint64_t blk, offset;
|
||||
int error;
|
||||
|
||||
su_node = fsdev->nd_su_node;
|
||||
ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
|
||||
|
||||
/* Read su header */
|
||||
error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
|
||||
if (error) {
|
||||
brelse(bp_header);
|
||||
return (error);
|
||||
}
|
||||
|
||||
su_header = (struct nandfs_sufile_header *)bp_header->b_data;
|
||||
nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
|
||||
|
||||
/* Read su usage block if other than su header block */
|
||||
if (blk != 0) {
|
||||
error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
brelse(bp_header);
|
||||
return (error);
|
||||
}
|
||||
} else
|
||||
bp = bp_header;
|
||||
|
||||
/* Reset su usage data */
|
||||
su_usage = SU_USAGE_OFF(bp, offset);
|
||||
su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
|
||||
su_usage->su_nblocks = 0;
|
||||
su_usage->su_flags = 0;
|
||||
|
||||
/* Update clean/dirty counter in header */
|
||||
su_header->sh_ncleansegs++;
|
||||
su_header->sh_ndirtysegs--;
|
||||
|
||||
/*
|
||||
* Make buffers dirty, called by cleaner
|
||||
* so force dirty even if no much space left
|
||||
* on device
|
||||
*/
|
||||
nandfs_dirty_buf(bp_header, 1);
|
||||
if (bp != bp_header)
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
/* Update free block count */
|
||||
fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
|
||||
fsdev->nd_fsdata.f_blocks_per_segment;
|
||||
fsdev->nd_clean_segs++;
|
||||
|
||||
DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nandfs_bad_segment(struct nandfs_device *fsdev, uint64_t seg)
|
||||
{
|
||||
struct nandfs_node *su_node;
|
||||
struct nandfs_segment_usage *su_usage;
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset;
|
||||
int error;
|
||||
|
||||
su_node = fsdev->nd_su_node;
|
||||
ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
|
||||
|
||||
nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
|
||||
|
||||
error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
su_usage = SU_USAGE_OFF(bp, offset);
|
||||
su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
|
||||
su_usage->su_flags = NANDFS_SEGMENT_USAGE_ERROR;
|
||||
|
||||
DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
|
||||
|
||||
nandfs_dirty_buf(bp, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_markgc_segment(struct nandfs_device *fsdev, uint64_t seg)
|
||||
{
|
||||
struct nandfs_node *su_node;
|
||||
struct nandfs_segment_usage *su_usage;
|
||||
struct buf *bp;
|
||||
uint64_t blk, offset;
|
||||
int error;
|
||||
|
||||
su_node = fsdev->nd_su_node;
|
||||
|
||||
VOP_LOCK(NTOV(su_node), LK_EXCLUSIVE);
|
||||
|
||||
nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
|
||||
|
||||
error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(su_node), 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
su_usage = SU_USAGE_OFF(bp, offset);
|
||||
MPASS((su_usage->su_flags & NANDFS_SEGMENT_USAGE_GC) == 0);
|
||||
su_usage->su_flags |= NANDFS_SEGMENT_USAGE_GC;
|
||||
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(su_node), 0);
|
||||
|
||||
DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_clear_segment(struct nandfs_device *fsdev, uint64_t seg)
|
||||
{
|
||||
uint64_t offset, segsize;
|
||||
uint32_t bps, bsize;
|
||||
int error = 0;
|
||||
|
||||
bps = fsdev->nd_fsdata.f_blocks_per_segment;
|
||||
bsize = fsdev->nd_blocksize;
|
||||
segsize = bsize * bps;
|
||||
nandfs_get_segment_range(fsdev, seg, &offset, NULL);
|
||||
offset *= bsize;
|
||||
|
||||
DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
|
||||
|
||||
/* Erase it and mark it bad when fail */
|
||||
if (nandfs_erase(fsdev, offset, segsize))
|
||||
error = nandfs_bad_segment(fsdev, seg);
|
||||
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Mark it free */
|
||||
error = nandfs_free_segment(fsdev, seg);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_seg_stat(struct nandfs_device *nandfsdev,
|
||||
struct nandfs_seg_stat *nss)
|
||||
{
|
||||
struct nandfs_sufile_header *suhdr;
|
||||
struct nandfs_node *su_node;
|
||||
struct buf *bp;
|
||||
int err;
|
||||
|
||||
su_node = nandfsdev->nd_su_node;
|
||||
|
||||
NANDFS_WRITELOCK(nandfsdev);
|
||||
VOP_LOCK(NTOV(su_node), LK_SHARED);
|
||||
err = nandfs_bread(nandfsdev->nd_su_node, 0, NOCRED, 0, &bp);
|
||||
if (err) {
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(su_node), 0);
|
||||
NANDFS_WRITEUNLOCK(nandfsdev);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
suhdr = (struct nandfs_sufile_header *)bp->b_data;
|
||||
nss->nss_nsegs = nandfsdev->nd_fsdata.f_nsegments;
|
||||
nss->nss_ncleansegs = suhdr->sh_ncleansegs;
|
||||
nss->nss_ndirtysegs = suhdr->sh_ndirtysegs;
|
||||
nss->nss_ctime = 0;
|
||||
nss->nss_nongc_ctime = nandfsdev->nd_ts.tv_sec;
|
||||
nss->nss_prot_seq = nandfsdev->nd_seg_sequence;
|
||||
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(su_node), 0);
|
||||
|
||||
NANDFS_WRITEUNLOCK(nandfsdev);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_segment_info_ioctl(struct nandfs_device *fsdev,
|
||||
struct nandfs_argv *nargv)
|
||||
{
|
||||
struct nandfs_suinfo *nsi;
|
||||
int error;
|
||||
|
||||
if (nargv->nv_nmembs > NANDFS_SEGMENTS_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
nsi = malloc(sizeof(struct nandfs_suinfo) * nargv->nv_nmembs,
|
||||
M_NANDFSTEMP, M_WAITOK | M_ZERO);
|
||||
|
||||
error = nandfs_get_segment_info(fsdev, nsi, nargv->nv_nmembs,
|
||||
nargv->nv_index);
|
||||
|
||||
if (error == 0)
|
||||
error = copyout(nsi, (void *)(uintptr_t)nargv->nv_base,
|
||||
sizeof(struct nandfs_suinfo) * nargv->nv_nmembs);
|
||||
|
||||
free(nsi, M_NANDFSTEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_segment_info(struct nandfs_device *fsdev, struct nandfs_suinfo *nsi,
|
||||
uint32_t nmembs, uint64_t segment)
|
||||
{
|
||||
|
||||
return (nandfs_get_segment_info_filter(fsdev, nsi, nmembs, segment,
|
||||
NULL, 0, 0));
|
||||
}
|
||||
|
||||
int
|
||||
nandfs_get_segment_info_filter(struct nandfs_device *fsdev,
|
||||
struct nandfs_suinfo *nsi, uint32_t nmembs, uint64_t segment,
|
||||
uint64_t *nsegs, uint32_t filter, uint32_t nfilter)
|
||||
{
|
||||
struct nandfs_segment_usage *su;
|
||||
struct nandfs_node *su_node;
|
||||
struct buf *bp;
|
||||
uint64_t curr, blocknr, blockoff, i;
|
||||
uint32_t flags;
|
||||
int err = 0;
|
||||
|
||||
curr = ~(0);
|
||||
|
||||
lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL);
|
||||
su_node = fsdev->nd_su_node;
|
||||
|
||||
VOP_LOCK(NTOV(su_node), LK_SHARED);
|
||||
|
||||
bp = NULL;
|
||||
if (nsegs != NULL)
|
||||
*nsegs = 0;
|
||||
for (i = 0; i < nmembs; segment++) {
|
||||
if (segment == fsdev->nd_fsdata.f_nsegments)
|
||||
break;
|
||||
|
||||
nandfs_seg_usage_blk_offset(fsdev, segment, &blocknr,
|
||||
&blockoff);
|
||||
|
||||
if (i == 0 || curr != blocknr) {
|
||||
if (bp != NULL)
|
||||
brelse(bp);
|
||||
err = nandfs_bread(su_node, blocknr, NOCRED,
|
||||
0, &bp);
|
||||
if (err) {
|
||||
goto out;
|
||||
}
|
||||
curr = blocknr;
|
||||
}
|
||||
|
||||
su = SU_USAGE_OFF(bp, blockoff);
|
||||
flags = su->su_flags;
|
||||
if (segment == fsdev->nd_seg_num ||
|
||||
segment == fsdev->nd_next_seg_num)
|
||||
flags |= NANDFS_SEGMENT_USAGE_ACTIVE;
|
||||
|
||||
if (nfilter != 0 && (flags & nfilter) != 0)
|
||||
continue;
|
||||
if (filter != 0 && (flags & filter) == 0)
|
||||
continue;
|
||||
|
||||
nsi->nsi_num = segment;
|
||||
nsi->nsi_lastmod = su->su_lastmod;
|
||||
nsi->nsi_blocks = su->su_nblocks;
|
||||
nsi->nsi_flags = flags;
|
||||
nsi++;
|
||||
i++;
|
||||
if (nsegs != NULL)
|
||||
(*nsegs)++;
|
||||
}
|
||||
|
||||
out:
|
||||
if (bp != NULL)
|
||||
brelse(bp);
|
||||
VOP_UNLOCK(NTOV(su_node), 0);
|
||||
lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);
|
||||
|
||||
return (err);
|
||||
}
|
1590
sys/fs/nandfs/nandfs_vfsops.c
Normal file
1590
sys/fs/nandfs/nandfs_vfsops.c
Normal file
File diff suppressed because it is too large
Load Diff
2455
sys/fs/nandfs/nandfs_vnops.c
Normal file
2455
sys/fs/nandfs/nandfs_vnops.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -217,6 +217,8 @@ SUBDIR= ${_3dfx} \
|
||||
${_mwlfw} \
|
||||
mxge \
|
||||
my \
|
||||
${_nandfs} \
|
||||
${_nandsim} \
|
||||
${_ncp} \
|
||||
${_ncv} \
|
||||
${_ndis} \
|
||||
@ -335,7 +337,6 @@ SUBDIR= ${_3dfx} \
|
||||
vx \
|
||||
${_vxge} \
|
||||
wb \
|
||||
${_wbwd} \
|
||||
${_wi} \
|
||||
wlan \
|
||||
wlan_acl \
|
||||
@ -398,6 +399,11 @@ _ipdivert= ipdivert
|
||||
_ipfw= ipfw
|
||||
.endif
|
||||
|
||||
.if ${MK_NAND} != "no" || defined(ALL_MODULES)
|
||||
_nandfs= nandfs
|
||||
_nandsim= nandsim
|
||||
.endif
|
||||
|
||||
.if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES)
|
||||
_netgraph= netgraph
|
||||
.endif
|
||||
@ -513,7 +519,6 @@ _stg= stg
|
||||
_streams= streams
|
||||
_svr4= svr4
|
||||
_vxge= vxge
|
||||
_wbwd= wbwd
|
||||
_wi= wi
|
||||
_xe= xe
|
||||
.if ${MK_ZFS} != "no" || defined(ALL_MODULES)
|
||||
@ -708,7 +713,6 @@ _viawd= viawd
|
||||
_virtio= virtio
|
||||
_vxge= vxge
|
||||
_x86bios= x86bios
|
||||
_wbwd= wbwd
|
||||
_wi= wi
|
||||
_wpi= wpi
|
||||
.if ${MK_SOURCELESS_UCODE} != "no"
|
||||
|
12
sys/modules/nandfs/Makefile
Normal file
12
sys/modules/nandfs/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../fs/nandfs
|
||||
|
||||
KMOD= nandfs
|
||||
SRCS= vnode_if.h \
|
||||
bmap.c nandfs_bmap.c nandfs_dir.c nandfs_subr.c nandfs_vfsops.c \
|
||||
nandfs_vnops.c nandfs_alloc.c nandfs_cpfile.c nandfs_dat.c \
|
||||
nandfs_ifile.c nandfs_segment.c nandfs_sufile.c nandfs_buffer.c \
|
||||
nandfs_cleaner.c
|
||||
|
||||
.include <bsd.kmod.mk>
|
11
sys/modules/nandsim/Makefile
Normal file
11
sys/modules/nandsim/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../dev/nand
|
||||
|
||||
KMOD= nandsim
|
||||
SRCS= nandsim.c nandsim_chip.c nandsim_swap.c nandsim_ctrl.c nandsim_log.c\
|
||||
bus_if.h device_if.h vnode_if.h nfc_if.h nand_if.h
|
||||
MFILES= kern/bus_if.m kern/device_if.m\
|
||||
dev/nand/nfc_if.m dev/nand/nand_if.m
|
||||
|
||||
.include <bsd.kmod.mk>
|
2
tools/build/options/WITHOUT_NAND
Normal file
2
tools/build/options/WITHOUT_NAND
Normal file
@ -0,0 +1,2 @@
|
||||
.\" $FreeBSD$
|
||||
Set to not build the NAND Flash components.
|
2
tools/build/options/WITH_NAND
Normal file
2
tools/build/options/WITH_NAND
Normal file
@ -0,0 +1,2 @@
|
||||
.\" $FreeBSD$
|
||||
Set to build the NAND Flash components.
|
@ -223,6 +223,11 @@ SUBDIR+= lpr
|
||||
SUBDIR+= manctl
|
||||
.endif
|
||||
|
||||
.if ${MK_NAND} != "no"
|
||||
SUBDIR+= nandsim
|
||||
SUBDIR+= nandtool
|
||||
.endif
|
||||
|
||||
.if ${MK_NETGRAPH} != "no"
|
||||
SUBDIR+= flowctl
|
||||
SUBDIR+= lmcconfig
|
||||
|
8
usr.sbin/nandsim/Makefile
Normal file
8
usr.sbin/nandsim/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= nandsim
|
||||
SRCS= nandsim.c nandsim_rcfile.c nandsim_cfgparse.c
|
||||
BINDIR= /usr/sbin
|
||||
MAN= nandsim.8
|
||||
|
||||
.include <bsd.prog.mk>
|
230
usr.sbin/nandsim/nandsim.8
Normal file
230
usr.sbin/nandsim/nandsim.8
Normal file
@ -0,0 +1,230 @@
|
||||
.\" Copyright (c) 2010 Semihalf
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd August 10, 2010
|
||||
.Dt NANDSIM 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm nandsim
|
||||
.Nd NAND simulator control program
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Ic status
|
||||
.Aq ctrl_no | Fl -all | Fl a
|
||||
.Op Fl v
|
||||
.Nm
|
||||
.Ic conf
|
||||
.Aq filename
|
||||
.Nm
|
||||
.Ic start
|
||||
.Aq ctrl_no
|
||||
.Nm
|
||||
.Ic mod
|
||||
.Aq ctrl_no:cs_no | Fl l Aq loglevel
|
||||
.Op Fl p Aq prog_time
|
||||
.Op Fl e Aq erase_time
|
||||
.Op Fl r Aq read_time
|
||||
.Op Fl E Aq error_ratio
|
||||
.Op Fl h
|
||||
.Nm
|
||||
.Ic stop
|
||||
.Aq ctrl_no
|
||||
.Nm
|
||||
.Ic error
|
||||
.Aq ctrl_no:cs_no
|
||||
.Aq page_num
|
||||
.Aq column
|
||||
.Aq length
|
||||
.Aq pattern
|
||||
.Nm
|
||||
.Ic bb
|
||||
.Aq ctrl_no:cs_no
|
||||
.Op blk_num,blk_num2,...
|
||||
.Op Fl U
|
||||
.Op Fl L
|
||||
.Nm
|
||||
.Ic freeze
|
||||
.Op ctrl_no
|
||||
.Nm
|
||||
.Ic log
|
||||
.Aq ctrl_no | Fl -all | Fl a
|
||||
.Nm
|
||||
.Ic stats
|
||||
.Aq ctrl_no:cs_no
|
||||
.Aq page_num
|
||||
.Nm
|
||||
.Ic dump
|
||||
.Aq ctrl_no:cs_no
|
||||
.Aq filename
|
||||
.Nm
|
||||
.Ic restore
|
||||
.Aq ctrl_no:chip_no
|
||||
.Aq filename
|
||||
.Nm
|
||||
.Ic destroy
|
||||
.Aq ctrl_no[:cs_no] | Fl -all | Fl a
|
||||
.Nm
|
||||
.Ic help
|
||||
.Op Fl v
|
||||
.Sh COMMAND DESCRIPTION
|
||||
Controllers and chips are arranged into a simple hierarchy.
|
||||
There can be up to 4 controllers configured, each with 4 chip select (CS) lines.
|
||||
A given chip is connected to one of the chip selects.
|
||||
.Pp
|
||||
Controllers are specified as
|
||||
.Aq ctrl_no ;
|
||||
chip selects are specified as
|
||||
.Aq cs_no .
|
||||
.Bl -tag -width periphlist
|
||||
.It Ic status
|
||||
Gets controller(s) status. If
|
||||
.Fl a
|
||||
or
|
||||
.Fl -all
|
||||
flag is specified - command will print status of every controller
|
||||
currently available.
|
||||
Optional flag
|
||||
.Fl v
|
||||
causes printing complete information about the controller, and all
|
||||
chips attached to it.
|
||||
.It Ic conf
|
||||
Reads simulator configuration from a specified file (this includes
|
||||
the simulation "layout" i.e. controllers-chips assignments).
|
||||
Configuration changes for an already started simulation require a
|
||||
full stop-start cycle in order to take effect i.e.:
|
||||
.Pp
|
||||
.Bl -column
|
||||
.It nandsim stop ...
|
||||
.It nandsim destroy ...
|
||||
.Pp
|
||||
.It << edit config file >>
|
||||
.Pp
|
||||
.It nandsim conf ...
|
||||
.It nandsim start ...
|
||||
.El
|
||||
.It Ic mod
|
||||
Alters simulator parameters on-the-fly.
|
||||
If controller number and CS pair is not specified, the general
|
||||
simulator parameters (not specific to a controller or a chip) will be modified.
|
||||
Changing chip's parameters requires specifying both controller number and CS
|
||||
to which the given chip is connected.
|
||||
Parameters which can be altered:
|
||||
.Pp
|
||||
General simulator related:
|
||||
.Bl -tag -width flag
|
||||
.It Fl l Aq log_level
|
||||
change logging level to
|
||||
.Aq log_level
|
||||
.El
|
||||
.Pp
|
||||
Chip related:
|
||||
.Bl -tag -width flag
|
||||
.It Fl p Aq prog_time
|
||||
change prog time for specified chip to
|
||||
.Aq prog_time
|
||||
.It Fl e Aq erase_time
|
||||
change erase time for specified chip to
|
||||
.Aq erase_time
|
||||
.It Fl r Aq read_time
|
||||
change read time for specified chip to
|
||||
.Aq read_time
|
||||
.It Fl E Aq error_ratio
|
||||
change error ratio for specified chip to
|
||||
.Aq error_ratio .
|
||||
Error ratio is a number of errors per million read/write bytes.
|
||||
.El
|
||||
.Pp
|
||||
Additionally, flag
|
||||
.Fl h
|
||||
will list parameters which can be altered.
|
||||
.El
|
||||
.Bl -tag -width periphlist
|
||||
.It Ic bb
|
||||
Marks/unmarks a specified block as bad.
|
||||
To mark/unmark the bad condition an a block, the following parameters
|
||||
have to be supplied: controller number, CS number, and at least one
|
||||
block number.
|
||||
It is possible to specify multiple blocks, by separating blocks numbers
|
||||
with a comma.
|
||||
The following options can be used for the 'bb' command:
|
||||
.Bl -tag -width flag
|
||||
.It Fl U
|
||||
unmark the bad previously marked block as bad.
|
||||
.It Fl L
|
||||
list all blocks marked as bad on a given chip.
|
||||
.El
|
||||
.It Ic log
|
||||
Prints activity log of the specified controller to stdout; if
|
||||
controller number is not specifed, logs for all available
|
||||
controllers are printed.
|
||||
.It Ic stats
|
||||
Print statistics of the selected controller, chip and page.
|
||||
Statistics includes read count, write count, raw read count, raw
|
||||
write count, ECC stats (succeeded corrections, failed correction).
|
||||
.It Ic dump
|
||||
Dumps a snaphot of a single chip (including data and bad blocks
|
||||
information, wearout level) into the file.
|
||||
.It Ic restore
|
||||
Restores chip state from a dump-file snapshot (produced previously
|
||||
with the 'dump' command).
|
||||
.It Ic start
|
||||
Starts a controller i.e. the simulation.
|
||||
.It Ic stop
|
||||
Stops an already started controller; if the controller number is not
|
||||
supplied, attempts to stop all currently working controllers.
|
||||
.It Ic destroy
|
||||
Removes existing active chip/controller and its configuration from
|
||||
memory and releases the resources.
|
||||
Specifying flag
|
||||
.Fl a
|
||||
or
|
||||
.Fl -all
|
||||
causes removal of every chip and controller.
|
||||
Controller must be stopped in order to be destroyed.
|
||||
.It Ic error
|
||||
Directly overwrites a certain number of bytes in the specified page
|
||||
at a given offset with a supplied pattern (which mimics the
|
||||
corruption of flash contents).
|
||||
.It Ic help
|
||||
Prints synopsis,
|
||||
.Fl v
|
||||
gives more verbose output.
|
||||
.It Ic freeze
|
||||
Stops simulation of given controller (simulates power-loss).
|
||||
All commands issues to any chip on this controller are ignored.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr nand 4 ,
|
||||
.Xr nandsim 4
|
||||
.Xr nandsim.conf 5
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
utility first appeared in
|
||||
.Fx 10.0 .
|
||||
.Sh AUTHOR
|
||||
This utility was written by
|
||||
.An Lukasz Wojcik .
|
1397
usr.sbin/nandsim/nandsim.c
Normal file
1397
usr.sbin/nandsim/nandsim.c
Normal file
File diff suppressed because it is too large
Load Diff
957
usr.sbin/nandsim/nandsim_cfgparse.c
Normal file
957
usr.sbin/nandsim/nandsim_cfgparse.c
Normal file
@ -0,0 +1,957 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <dev/nand/nandsim.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nandsim_cfgparse.h"
|
||||
|
||||
#define warn(fmt, args...) do { \
|
||||
printf("WARNING: " fmt "\n", ##args); } while (0)
|
||||
|
||||
#define error(fmt, args...) do { \
|
||||
printf("ERROR: " fmt "\n", ##args); } while (0)
|
||||
|
||||
#define MSG_MANDATORYKEYMISSING "mandatory key \"%s\" value belonging to " \
|
||||
"section \"%s\" is missing!\n"
|
||||
|
||||
#define DEBUG
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug(fmt, args...) do { \
|
||||
printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0)
|
||||
#else
|
||||
#define debug(fmt, args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define STRBUFSIZ 2000
|
||||
|
||||
/* Macros extracts type and type size */
|
||||
#define TYPE(x) ((x) & 0xf8)
|
||||
#define SIZE(x) (((x) & 0x07))
|
||||
|
||||
/* Erase/Prog/Read time max and min values */
|
||||
#define DELAYTIME_MIN 10000
|
||||
#define DELAYTIME_MAX 10000000
|
||||
|
||||
/* Structure holding configuration for controller. */
|
||||
static struct sim_ctrl ctrl_conf;
|
||||
/* Structure holding configuration for chip. */
|
||||
static struct sim_chip chip_conf;
|
||||
|
||||
static struct nandsim_key nandsim_ctrl_keys[] = {
|
||||
{"num_cs", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num_cs, 0},
|
||||
{"ctrl_num", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num, 0},
|
||||
|
||||
{"ecc_layout", 1, VALUE_UINTARRAY | SIZE_16,
|
||||
(void *)&ctrl_conf.ecc_layout, MAX_ECC_BYTES},
|
||||
|
||||
{"filename", 0, VALUE_STRING,
|
||||
(void *)&ctrl_conf.filename, FILENAME_SIZE},
|
||||
|
||||
{"ecc", 0, VALUE_BOOL, (void *)&ctrl_conf.ecc, 0},
|
||||
{NULL, 0, 0, NULL, 0},
|
||||
};
|
||||
|
||||
static struct nandsim_key nandsim_chip_keys[] = {
|
||||
{"chip_cs", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.num, 0},
|
||||
{"chip_ctrl", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.ctrl_num,
|
||||
0},
|
||||
{"device_id", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.device_id,
|
||||
0},
|
||||
{"manufacturer_id", 1, VALUE_UINT | SIZE_8,
|
||||
(void *)&chip_conf.manufact_id, 0},
|
||||
{"model", 0, VALUE_STRING, (void *)&chip_conf.device_model,
|
||||
DEV_MODEL_STR_SIZE},
|
||||
{"manufacturer", 0, VALUE_STRING, (void *)&chip_conf.manufacturer,
|
||||
MAN_STR_SIZE},
|
||||
{"page_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.page_size,
|
||||
0},
|
||||
{"oob_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.oob_size,
|
||||
0},
|
||||
{"pages_per_block", 1, VALUE_UINT | SIZE_32,
|
||||
(void *)&chip_conf.pgs_per_blk, 0},
|
||||
{"blocks_per_lun", 1, VALUE_UINT | SIZE_32,
|
||||
(void *)&chip_conf.blks_per_lun, 0},
|
||||
{"luns", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.luns, 0},
|
||||
{"column_addr_cycle", 1,VALUE_UINT | SIZE_8,
|
||||
(void *)&chip_conf.col_addr_cycles, 0},
|
||||
{"row_addr_cycle", 1, VALUE_UINT | SIZE_8,
|
||||
(void *)&chip_conf.row_addr_cycles, 0},
|
||||
{"program_time", 0, VALUE_UINT | SIZE_32,
|
||||
(void *)&chip_conf.prog_time, 0},
|
||||
{"erase_time", 0, VALUE_UINT | SIZE_32,
|
||||
(void *)&chip_conf.erase_time, 0},
|
||||
{"read_time", 0, VALUE_UINT | SIZE_32,
|
||||
(void *)&chip_conf.read_time, 0},
|
||||
{"width", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.width, 0},
|
||||
{"wear_out", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.wear_level,
|
||||
0},
|
||||
{"bad_block_map", 0, VALUE_UINTARRAY | SIZE_32,
|
||||
(void *)&chip_conf.bad_block_map, MAX_BAD_BLOCKS},
|
||||
{NULL, 0, 0, NULL, 0},
|
||||
};
|
||||
|
||||
struct nandsim_section sections[] = {
|
||||
{"ctrl", (struct nandsim_key *)&nandsim_ctrl_keys},
|
||||
{"chip", (struct nandsim_key *)&nandsim_chip_keys},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
static uint8_t logoutputtoint(char *, int *);
|
||||
static uint8_t validate_chips(struct sim_chip *, int, struct sim_ctrl *, int);
|
||||
static uint8_t validate_ctrls(struct sim_ctrl *, int);
|
||||
static int configure_sim(const char *, struct rcfile *);
|
||||
static int create_ctrls(struct rcfile *, struct sim_ctrl **, int *);
|
||||
static int create_chips(struct rcfile *, struct sim_chip **, int *);
|
||||
static void destroy_ctrls(struct sim_ctrl *);
|
||||
static void destroy_chips(struct sim_chip *);
|
||||
static int validate_section_config(struct rcfile *, const char *, int);
|
||||
|
||||
int
|
||||
convert_argint(char *arg, int *value)
|
||||
{
|
||||
|
||||
if (arg == NULL || value == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
errno = 0;
|
||||
*value = (int)strtol(arg, NULL, 0);
|
||||
if (*value == 0 && errno != 0) {
|
||||
error("Cannot convert to number argument \'%s\'", arg);
|
||||
return (EINVAL);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
convert_arguint(char *arg, unsigned int *value)
|
||||
{
|
||||
|
||||
if (arg == NULL || value == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
errno = 0;
|
||||
*value = (unsigned int)strtol(arg, NULL, 0);
|
||||
if (*value == 0 && errno != 0) {
|
||||
error("Cannot convert to number argument \'%s\'", arg);
|
||||
return (EINVAL);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Parse given ',' separated list of bytes into buffer. */
|
||||
int
|
||||
parse_intarray(char *array, int **buffer)
|
||||
{
|
||||
char *tmp, *tmpstr, *origstr;
|
||||
unsigned int currbufp = 0, i;
|
||||
unsigned int count = 0, from = 0, to = 0;
|
||||
|
||||
/* Remove square braces */
|
||||
if (array[0] == '[')
|
||||
array ++;
|
||||
if (array[strlen(array)-1] == ']')
|
||||
array[strlen(array)-1] = ',';
|
||||
|
||||
from = strlen(array);
|
||||
origstr = (char *)malloc(sizeof(char) * from);
|
||||
strcpy(origstr, array);
|
||||
|
||||
tmpstr = (char *)strtok(array, ",");
|
||||
/* First loop checks for how big int array we need to allocate */
|
||||
while (tmpstr != NULL) {
|
||||
errno = 0;
|
||||
if ((tmp = strchr(tmpstr, '-')) != NULL) {
|
||||
*tmp = ' ';
|
||||
if (convert_arguint(tmpstr, &from) ||
|
||||
convert_arguint(tmp, &to)) {
|
||||
free(origstr);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
count += to - from + 1;
|
||||
} else {
|
||||
if (convert_arguint(tmpstr, &from)) {
|
||||
free(origstr);
|
||||
return (EINVAL);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
tmpstr = (char *)strtok(NULL, ",");
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
goto out;
|
||||
|
||||
/* Allocate buffer of ints */
|
||||
tmpstr = (char *)strtok(origstr, ",");
|
||||
*buffer = malloc(count * sizeof(int));
|
||||
|
||||
/* Second loop is just inserting converted values into int array */
|
||||
while (tmpstr != NULL) {
|
||||
errno = 0;
|
||||
if ((tmp = strchr(tmpstr, '-')) != NULL) {
|
||||
*tmp = ' ';
|
||||
from = strtol(tmpstr, NULL, 0);
|
||||
to = strtol(tmp, NULL, 0);
|
||||
tmpstr = strtok(NULL, ",");
|
||||
for (i = from; i <= to; i ++)
|
||||
(*buffer)[currbufp++] = i;
|
||||
continue;
|
||||
}
|
||||
errno = 0;
|
||||
from = (int)strtol(tmpstr, NULL, 0);
|
||||
(*buffer)[currbufp++] = from;
|
||||
tmpstr = (char *)strtok(NULL, ",");
|
||||
}
|
||||
out:
|
||||
free(origstr);
|
||||
return (count);
|
||||
}
|
||||
|
||||
/* Convert logoutput strings literals into appropriate ints. */
|
||||
static uint8_t
|
||||
logoutputtoint(char *logoutput, int *output)
|
||||
{
|
||||
int out;
|
||||
|
||||
if (strcmp(logoutput, "file") == 0)
|
||||
out = NANDSIM_OUTPUT_FILE;
|
||||
|
||||
else if (strcmp(logoutput, "console") == 0)
|
||||
out = NANDSIM_OUTPUT_CONSOLE;
|
||||
|
||||
else if (strcmp(logoutput, "ram") == 0)
|
||||
out = NANDSIM_OUTPUT_RAM;
|
||||
|
||||
else if (strcmp(logoutput, "none") == 0)
|
||||
out = NANDSIM_OUTPUT_NONE;
|
||||
else
|
||||
out = -1;
|
||||
|
||||
*output = out;
|
||||
|
||||
if (out == -1)
|
||||
return (EINVAL);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
configure_sim(const char *devfname, struct rcfile *f)
|
||||
{
|
||||
struct sim_param sim_conf;
|
||||
char buf[255];
|
||||
int err, tmpv, fd;
|
||||
|
||||
err = rc_getint(f, "sim", 0, "log_level", &tmpv);
|
||||
|
||||
if (tmpv < 0 || tmpv > 255 || err) {
|
||||
error("Bad log level specified (%d)\n", tmpv);
|
||||
return (ENOTSUP);
|
||||
} else
|
||||
sim_conf.log_level = tmpv;
|
||||
|
||||
rc_getstring(f, "sim", 0, "log_output", 255, (char *)&buf);
|
||||
|
||||
tmpv = -1;
|
||||
err = logoutputtoint((char *)&buf, &tmpv);
|
||||
if (err) {
|
||||
error("Log output specified in config file does not seem to "
|
||||
"be valid (%s)!", (char *)&buf);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
sim_conf.log_output = tmpv;
|
||||
|
||||
fd = open(devfname, O_RDWR);
|
||||
if (fd == -1) {
|
||||
error("could not open simulator device file (%s)!",
|
||||
devfname);
|
||||
return (EX_OSFILE);
|
||||
}
|
||||
|
||||
err = ioctl(fd, NANDSIM_SIM_PARAM, &sim_conf);
|
||||
if (err) {
|
||||
error("simulator parameters could not be modified: %s",
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return (EX_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
create_ctrls(struct rcfile *f, struct sim_ctrl **ctrls, int *cnt)
|
||||
{
|
||||
int count, i;
|
||||
struct sim_ctrl *ctrlsptr;
|
||||
|
||||
count = rc_getsectionscount(f, "ctrl");
|
||||
if (count > MAX_SIM_DEV) {
|
||||
error("Too many CTRL sections specified(%d)", count);
|
||||
return (ENOTSUP);
|
||||
} else if (count == 0) {
|
||||
error("No ctrl sections specified");
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
ctrlsptr = (struct sim_ctrl *)malloc(sizeof(struct sim_ctrl) * count);
|
||||
if (ctrlsptr == NULL) {
|
||||
error("Could not allocate memory for ctrl configuration");
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
bzero((void *)&ctrl_conf, sizeof(ctrl_conf));
|
||||
|
||||
/*
|
||||
* ECC layout have to end up with 0xffff, so
|
||||
* we're filling buffer with 0xff. If ecc_layout is
|
||||
* defined in config file, values will be overriden.
|
||||
*/
|
||||
memset((void *)&ctrl_conf.ecc_layout, 0xff,
|
||||
sizeof(ctrl_conf.ecc_layout));
|
||||
|
||||
if (validate_section_config(f, "ctrl", i) != 0) {
|
||||
free(ctrlsptr);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (parse_section(f, "ctrl", i) != 0) {
|
||||
free(ctrlsptr);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
memcpy(&ctrlsptr[i], &ctrl_conf, sizeof(ctrl_conf));
|
||||
/* Try to create ctrl with config parsed */
|
||||
debug("NUM=%d\nNUM_CS=%d\nECC=%d\nFILENAME=%s\nECC_LAYOUT[0]"
|
||||
"=%d\nECC_LAYOUT[1]=%d\n\n",
|
||||
ctrlsptr[i].num, ctrlsptr[i].num_cs, ctrlsptr[i].ecc,
|
||||
ctrlsptr[i].filename, ctrlsptr[i].ecc_layout[0],
|
||||
ctrlsptr[i].ecc_layout[1]);
|
||||
}
|
||||
*cnt = count;
|
||||
*ctrls = ctrlsptr;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_ctrls(struct sim_ctrl *ctrls)
|
||||
{
|
||||
|
||||
free(ctrls);
|
||||
}
|
||||
|
||||
static int
|
||||
create_chips(struct rcfile *f, struct sim_chip **chips, int *cnt)
|
||||
{
|
||||
struct sim_chip *chipsptr;
|
||||
int count, i;
|
||||
|
||||
count = rc_getsectionscount(f, "chip");
|
||||
if (count > (MAX_CTRL_CS * MAX_SIM_DEV)) {
|
||||
error("Too many chip sections specified(%d)", count);
|
||||
return (ENOTSUP);
|
||||
} else if (count == 0) {
|
||||
error("No chip sections specified");
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
chipsptr = (struct sim_chip *)malloc(sizeof(struct sim_chip) * count);
|
||||
if (chipsptr == NULL) {
|
||||
error("Could not allocate memory for chip configuration");
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
bzero((void *)&chip_conf, sizeof(chip_conf));
|
||||
|
||||
/*
|
||||
* Bad block map have to end up with 0xffff, so
|
||||
* we're filling array with 0xff. If bad block map is
|
||||
* defined in config file, values will be overriden.
|
||||
*/
|
||||
memset((void *)&chip_conf.bad_block_map, 0xff,
|
||||
sizeof(chip_conf.bad_block_map));
|
||||
|
||||
if (validate_section_config(f, "chip", i) != 0) {
|
||||
free(chipsptr);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (parse_section(f, "chip", i) != 0) {
|
||||
free(chipsptr);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
memcpy(&chipsptr[i], &chip_conf, sizeof(chip_conf));
|
||||
|
||||
/* Try to create chip with config parsed */
|
||||
debug("CHIP:\nNUM=%d\nCTRL_NUM=%d\nDEVID=%d\nMANID=%d\n"
|
||||
"PAGE_SZ=%d\nOOBSZ=%d\nREAD_T=%d\nDEVMODEL=%s\n"
|
||||
"MAN=%s\nCOLADDRCYCLES=%d\nROWADDRCYCLES=%d\nCHWIDTH=%d\n"
|
||||
"PGS/BLK=%d\nBLK/LUN=%d\nLUNS=%d\nERR_RATIO=%d\n"
|
||||
"WEARLEVEL=%d\nISWP=%d\n\n\n\n",
|
||||
chipsptr[i].num, chipsptr[i].ctrl_num,
|
||||
chipsptr[i].device_id, chipsptr[i].manufact_id,
|
||||
chipsptr[i].page_size, chipsptr[i].oob_size,
|
||||
chipsptr[i].read_time, chipsptr[i].device_model,
|
||||
chipsptr[i].manufacturer, chipsptr[i].col_addr_cycles,
|
||||
chipsptr[i].row_addr_cycles, chipsptr[i].width,
|
||||
chipsptr[i].pgs_per_blk, chipsptr[i].blks_per_lun,
|
||||
chipsptr[i].luns, chipsptr[i].error_ratio,
|
||||
chipsptr[i].wear_level, chipsptr[i].is_wp);
|
||||
}
|
||||
*cnt = count;
|
||||
*chips = chipsptr;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_chips(struct sim_chip *chips)
|
||||
{
|
||||
|
||||
free(chips);
|
||||
}
|
||||
|
||||
int
|
||||
parse_config(char *cfgfname, const char *devfname)
|
||||
{
|
||||
int err = 0, fd;
|
||||
unsigned int chipsectionscnt, ctrlsectionscnt, i;
|
||||
struct rcfile *f;
|
||||
struct sim_chip *chips;
|
||||
struct sim_ctrl *ctrls;
|
||||
|
||||
err = rc_open(cfgfname, "r", &f);
|
||||
if (err) {
|
||||
error("could not open configuration file (%s)", cfgfname);
|
||||
return (EX_NOINPUT);
|
||||
}
|
||||
|
||||
/* First, try to configure simulator itself. */
|
||||
if (configure_sim(devfname, f) != EX_OK) {
|
||||
rc_close(f);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
debug("SIM CONFIGURED!\n");
|
||||
/* Then create controllers' configs */
|
||||
if (create_ctrls(f, &ctrls, &ctrlsectionscnt) != 0) {
|
||||
rc_close(f);
|
||||
return (ENXIO);
|
||||
}
|
||||
debug("CTRLS CONFIG READ!\n");
|
||||
|
||||
/* Then create chips' configs */
|
||||
if (create_chips(f, &chips, &chipsectionscnt) != 0) {
|
||||
destroy_ctrls(ctrls);
|
||||
rc_close(f);
|
||||
return (ENXIO);
|
||||
}
|
||||
debug("CHIPS CONFIG READ!\n");
|
||||
|
||||
if (validate_ctrls(ctrls, ctrlsectionscnt) != 0) {
|
||||
destroy_ctrls(ctrls);
|
||||
destroy_chips(chips);
|
||||
rc_close(f);
|
||||
return (EX_SOFTWARE);
|
||||
}
|
||||
if (validate_chips(chips, chipsectionscnt, ctrls,
|
||||
ctrlsectionscnt) != 0) {
|
||||
destroy_ctrls(ctrls);
|
||||
destroy_chips(chips);
|
||||
rc_close(f);
|
||||
return (EX_SOFTWARE);
|
||||
}
|
||||
|
||||
/* Open device */
|
||||
fd = open(devfname, O_RDWR);
|
||||
if (fd == -1) {
|
||||
error("could not open simulator device file (%s)!",
|
||||
devfname);
|
||||
rc_close(f);
|
||||
destroy_chips(chips);
|
||||
destroy_ctrls(ctrls);
|
||||
return (EX_OSFILE);
|
||||
}
|
||||
|
||||
debug("SIM CONFIG STARTED!\n");
|
||||
|
||||
/* At this stage, both ctrls' and chips' configs should be valid */
|
||||
for (i = 0; i < ctrlsectionscnt; i++) {
|
||||
err = ioctl(fd, NANDSIM_CREATE_CTRL, &ctrls[i]);
|
||||
if (err) {
|
||||
if (err == EEXIST)
|
||||
error("Controller#%d already created\n",
|
||||
ctrls[i].num);
|
||||
else if (err == EINVAL)
|
||||
error("Incorrect controler number (%d)\n",
|
||||
ctrls[i].num);
|
||||
else
|
||||
error("Could not created controller#%d\n",
|
||||
ctrls[i].num);
|
||||
/* Errors during controller creation stops parsing */
|
||||
close(fd);
|
||||
rc_close(f);
|
||||
destroy_ctrls(ctrls);
|
||||
destroy_chips(chips);
|
||||
return (ENXIO);
|
||||
}
|
||||
debug("CTRL#%d CONFIG STARTED!\n", i);
|
||||
}
|
||||
|
||||
for (i = 0; i < chipsectionscnt; i++) {
|
||||
err = ioctl(fd, NANDSIM_CREATE_CHIP, &chips[i]);
|
||||
if (err) {
|
||||
if (err == EEXIST)
|
||||
error("Chip#%d for controller#%d already "
|
||||
"created\n", chips[i].num,
|
||||
chips[i].ctrl_num);
|
||||
else if (err == EINVAL)
|
||||
error("Incorrect chip number (%d:%d)\n",
|
||||
chips[i].num, chips[i].ctrl_num);
|
||||
else
|
||||
error("Could not create chip (%d:%d)\n",
|
||||
chips[i].num, chips[i].ctrl_num);
|
||||
error("Could not start chip#%d\n", i);
|
||||
destroy_chips(chips);
|
||||
destroy_ctrls(ctrls);
|
||||
close(fd);
|
||||
rc_close(f);
|
||||
return (ENXIO);
|
||||
}
|
||||
}
|
||||
debug("CHIPS CONFIG STARTED!\n");
|
||||
|
||||
close(fd);
|
||||
rc_close(f);
|
||||
destroy_chips(chips);
|
||||
destroy_ctrls(ctrls);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function tries to get appropriate value for given key, convert it to
|
||||
* array of ints (of given size), and perform all the neccesary checks and
|
||||
* conversions.
|
||||
*/
|
||||
static int
|
||||
get_argument_intarray(const char *sect_name, int sectno,
|
||||
struct nandsim_key *key, struct rcfile *f)
|
||||
{
|
||||
char strbuf[STRBUFSIZ];
|
||||
int *intbuf;
|
||||
int getres;
|
||||
uint32_t cnt, i = 0;
|
||||
|
||||
getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ,
|
||||
(char *)&strbuf);
|
||||
|
||||
if (getres != 0) {
|
||||
if (key->mandatory != 0) {
|
||||
error(MSG_MANDATORYKEYMISSING, key->keyname,
|
||||
sect_name);
|
||||
return (EINVAL);
|
||||
} else
|
||||
/* Non-mandatory key, not present -- skip */
|
||||
return (0);
|
||||
}
|
||||
cnt = parse_intarray((char *)&strbuf, &intbuf);
|
||||
cnt = (cnt <= key->maxlength) ? cnt : key->maxlength;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (SIZE(key->valuetype) == SIZE_8)
|
||||
*((uint8_t *)(key->field) + i) =
|
||||
(uint8_t)intbuf[i];
|
||||
else if (SIZE(key->valuetype) == SIZE_16)
|
||||
*((uint16_t *)(key->field) + i) =
|
||||
(uint16_t)intbuf[i];
|
||||
else
|
||||
*((uint32_t *)(key->field) + i) =
|
||||
(uint32_t)intbuf[i];
|
||||
}
|
||||
free(intbuf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function tries to get appropriate value for given key, convert it to
|
||||
* int of certain length.
|
||||
*/
|
||||
static int
|
||||
get_argument_int(const char *sect_name, int sectno, struct nandsim_key *key,
|
||||
struct rcfile *f)
|
||||
{
|
||||
int getres;
|
||||
uint32_t val;
|
||||
|
||||
getres = rc_getint(f, sect_name, sectno, key->keyname, &val);
|
||||
if (getres != 0) {
|
||||
|
||||
if (key->mandatory != 0) {
|
||||
error(MSG_MANDATORYKEYMISSING, key->keyname,
|
||||
sect_name);
|
||||
|
||||
return (EINVAL);
|
||||
} else
|
||||
/* Non-mandatory key, not present -- skip */
|
||||
return (0);
|
||||
}
|
||||
if (SIZE(key->valuetype) == SIZE_8)
|
||||
*(uint8_t *)(key->field) = (uint8_t)val;
|
||||
else if (SIZE(key->valuetype) == SIZE_16)
|
||||
*(uint16_t *)(key->field) = (uint16_t)val;
|
||||
else
|
||||
*(uint32_t *)(key->field) = (uint32_t)val;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Function tries to get string value for given key */
|
||||
static int
|
||||
get_argument_string(const char *sect_name, int sectno,
|
||||
struct nandsim_key *key, struct rcfile *f)
|
||||
{
|
||||
char strbuf[STRBUFSIZ];
|
||||
int getres;
|
||||
|
||||
getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ,
|
||||
strbuf);
|
||||
|
||||
if (getres != 0) {
|
||||
if (key->mandatory != 0) {
|
||||
error(MSG_MANDATORYKEYMISSING, key->keyname,
|
||||
sect_name);
|
||||
return (1);
|
||||
} else
|
||||
/* Non-mandatory key, not present -- skip */
|
||||
return (0);
|
||||
}
|
||||
strncpy(key->field, (char *)&strbuf, (size_t)(key->maxlength - 1));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Function tries to get on/off value for given key */
|
||||
static int
|
||||
get_argument_bool(const char *sect_name, int sectno, struct nandsim_key *key,
|
||||
struct rcfile *f)
|
||||
{
|
||||
int getres, val;
|
||||
|
||||
getres = rc_getbool(f, sect_name, sectno, key->keyname, &val);
|
||||
if (getres != 0) {
|
||||
if (key->mandatory != 0) {
|
||||
error(MSG_MANDATORYKEYMISSING, key->keyname,
|
||||
sect_name);
|
||||
return (1);
|
||||
} else
|
||||
/* Non-mandatory key, not present -- skip */
|
||||
return (0);
|
||||
}
|
||||
*(uint8_t *)key->field = (uint8_t)val;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
parse_section(struct rcfile *f, const char *sect_name, int sectno)
|
||||
{
|
||||
struct nandsim_key *key;
|
||||
struct nandsim_section *sect = (struct nandsim_section *)§ions;
|
||||
int getres = 0;
|
||||
|
||||
while (1) {
|
||||
if (sect == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (strcmp(sect->name, sect_name) == 0)
|
||||
break;
|
||||
else
|
||||
sect++;
|
||||
}
|
||||
key = sect->keys;
|
||||
do {
|
||||
debug("->Section: %s, Key: %s, type: %d, size: %d",
|
||||
sect_name, key->keyname, TYPE(key->valuetype),
|
||||
SIZE(key->valuetype)/2);
|
||||
|
||||
switch (TYPE(key->valuetype)) {
|
||||
case VALUE_UINT:
|
||||
/* Single int value */
|
||||
getres = get_argument_int(sect_name, sectno, key, f);
|
||||
|
||||
if (getres != 0)
|
||||
return (getres);
|
||||
|
||||
break;
|
||||
case VALUE_UINTARRAY:
|
||||
/* Array of ints */
|
||||
getres = get_argument_intarray(sect_name,
|
||||
sectno, key, f);
|
||||
|
||||
if (getres != 0)
|
||||
return (getres);
|
||||
|
||||
break;
|
||||
case VALUE_STRING:
|
||||
/* Array of chars */
|
||||
getres = get_argument_string(sect_name, sectno, key,
|
||||
f);
|
||||
|
||||
if (getres != 0)
|
||||
return (getres);
|
||||
|
||||
break;
|
||||
case VALUE_BOOL:
|
||||
/* Boolean value (true/false/on/off/yes/no) */
|
||||
getres = get_argument_bool(sect_name, sectno, key,
|
||||
f);
|
||||
|
||||
if (getres != 0)
|
||||
return (getres);
|
||||
|
||||
break;
|
||||
}
|
||||
} while ((++key)->keyname != NULL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
validate_chips(struct sim_chip *chips, int chipcnt,
|
||||
struct sim_ctrl *ctrls, int ctrlcnt)
|
||||
{
|
||||
int cchipcnt, i, width, j, id, max;
|
||||
|
||||
cchipcnt = chipcnt;
|
||||
for (chipcnt -= 1; chipcnt >= 0; chipcnt--) {
|
||||
if (chips[chipcnt].num >= MAX_CTRL_CS) {
|
||||
error("chip no. too high (%d)!!\n",
|
||||
chips[chipcnt].num);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (chips[chipcnt].ctrl_num >= MAX_SIM_DEV) {
|
||||
error("controller no. too high (%d)!!\n",
|
||||
chips[chipcnt].ctrl_num);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (chips[chipcnt].width != 8 &&
|
||||
chips[chipcnt].width != 16) {
|
||||
error("invalid width:%d for chip#%d",
|
||||
chips[chipcnt].width, chips[chipcnt].num);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/* Check if page size is > 512 and if its power of 2 */
|
||||
if (chips[chipcnt].page_size < 512 ||
|
||||
(chips[chipcnt].page_size &
|
||||
(chips[chipcnt].page_size - 1)) != 0) {
|
||||
error("invalid page size:%d for chip#%d at ctrl#%d!!"
|
||||
"\n", chips[chipcnt].page_size,
|
||||
chips[chipcnt].num,
|
||||
chips[chipcnt].ctrl_num);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/* Check if controller no. ctrl_num is configured */
|
||||
for (i = 0, id = -1; i < ctrlcnt && id == -1; i++)
|
||||
if (ctrls[i].num == chips[chipcnt].ctrl_num)
|
||||
id = i;
|
||||
|
||||
if (i == ctrlcnt && id == -1) {
|
||||
error("Missing configuration for controller %d"
|
||||
" (at least one chip is connected to it)",
|
||||
chips[chipcnt].ctrl_num);
|
||||
return (EINVAL);
|
||||
} else {
|
||||
/*
|
||||
* Controller is configured -> check oob_size
|
||||
* validity
|
||||
*/
|
||||
i = 0;
|
||||
max = ctrls[id].ecc_layout[0];
|
||||
while (i < MAX_ECC_BYTES &&
|
||||
ctrls[id].ecc_layout[i] != 0xffff) {
|
||||
|
||||
if (ctrls[id].ecc_layout[i] > max)
|
||||
max = ctrls[id].ecc_layout[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
if (chips[chipcnt].oob_size < (unsigned)i) {
|
||||
error("OOB size for chip#%d at ctrl#%d is "
|
||||
"smaller than ecc layout length!",
|
||||
chips[chipcnt].num,
|
||||
chips[chipcnt].ctrl_num);
|
||||
exit(EINVAL);
|
||||
}
|
||||
|
||||
if (chips[chipcnt].oob_size < (unsigned)max) {
|
||||
error("OOB size for chip#%d at ctrl#%d is "
|
||||
"smaller than maximal ecc position in "
|
||||
"defined layout!", chips[chipcnt].num,
|
||||
chips[chipcnt].ctrl_num);
|
||||
exit(EINVAL);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ((chips[chipcnt].erase_time < DELAYTIME_MIN ||
|
||||
chips[chipcnt].erase_time > DELAYTIME_MAX) &&
|
||||
chips[chipcnt].erase_time != 0) {
|
||||
error("Invalid erase time value for chip#%d at "
|
||||
"ctrl#%d",
|
||||
chips[chipcnt].num,
|
||||
chips[chipcnt].ctrl_num);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if ((chips[chipcnt].prog_time < DELAYTIME_MIN ||
|
||||
chips[chipcnt].prog_time > DELAYTIME_MAX) &&
|
||||
chips[chipcnt].prog_time != 0) {
|
||||
error("Invalid prog time value for chip#%d at "
|
||||
"ctr#%d!",
|
||||
chips[chipcnt].num,
|
||||
chips[chipcnt].ctrl_num);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if ((chips[chipcnt].read_time < DELAYTIME_MIN ||
|
||||
chips[chipcnt].read_time > DELAYTIME_MAX) &&
|
||||
chips[chipcnt].read_time != 0) {
|
||||
error("Invalid read time value for chip#%d at "
|
||||
"ctrl#%d!",
|
||||
chips[chipcnt].num,
|
||||
chips[chipcnt].ctrl_num);
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
/* Check if chips attached to the same controller, have same width */
|
||||
for (i = 0; i < ctrlcnt; i++) {
|
||||
width = -1;
|
||||
for (j = 0; j < cchipcnt; j++) {
|
||||
if (chips[j].ctrl_num == i) {
|
||||
if (width == -1) {
|
||||
width = chips[j].width;
|
||||
} else {
|
||||
if (width != chips[j].width) {
|
||||
error("Chips attached to "
|
||||
"ctrl#%d have different "
|
||||
"widths!\n", i);
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
validate_ctrls(struct sim_ctrl *ctrl, int ctrlcnt)
|
||||
{
|
||||
for (ctrlcnt -= 1; ctrlcnt >= 0; ctrlcnt--) {
|
||||
if (ctrl[ctrlcnt].num > MAX_SIM_DEV) {
|
||||
error("Controller no. too high (%d)!!\n",
|
||||
ctrl[ctrlcnt].num);
|
||||
return (EINVAL);
|
||||
}
|
||||
if (ctrl[ctrlcnt].num_cs > MAX_CTRL_CS) {
|
||||
error("Too many CS (%d)!!\n", ctrl[ctrlcnt].num_cs);
|
||||
return (EINVAL);
|
||||
}
|
||||
if (ctrl[ctrlcnt].ecc != 0 && ctrl[ctrlcnt].ecc != 1) {
|
||||
error("ECC is set to neither 0 nor 1 !\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int validate_section_config(struct rcfile *f, const char *sect_name,
|
||||
int sectno)
|
||||
{
|
||||
struct nandsim_key *key;
|
||||
struct nandsim_section *sect;
|
||||
char **keys_tbl;
|
||||
int i, match;
|
||||
|
||||
for (match = 0, sect = (struct nandsim_section *)§ions;
|
||||
sect != NULL; sect++) {
|
||||
if (strcmp(sect->name, sect_name) == 0) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match == 0)
|
||||
return (EINVAL);
|
||||
|
||||
keys_tbl = rc_getkeys(f, sect_name, sectno);
|
||||
if (keys_tbl == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
for (i = 0; keys_tbl[i] != NULL; i++) {
|
||||
key = sect->keys;
|
||||
match = 0;
|
||||
do {
|
||||
if (strcmp(keys_tbl[i], key->keyname) == 0) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
} while ((++key)->keyname != NULL);
|
||||
|
||||
if (match == 0) {
|
||||
error("Invalid key in config file: %s\n", keys_tbl[i]);
|
||||
free(keys_tbl);
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
free(keys_tbl);
|
||||
return (0);
|
||||
}
|
86
usr.sbin/nandsim/nandsim_cfgparse.h
Normal file
86
usr.sbin/nandsim/nandsim_cfgparse.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*-
|
||||
* Copyright (C) 2009-2012 Semihalf
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NANDSIM_CONFPARSER_H_
|
||||
#define _NANDSIM_CONFPARSER_H_
|
||||
|
||||
#define VALUE_UINT 0x08
|
||||
#define VALUE_INT 0x10
|
||||
#define VALUE_UINTARRAY 0x18
|
||||
#define VALUE_INTARRAY 0x20
|
||||
#define VALUE_STRING 0x28
|
||||
#define VALUE_CHAR 0x40
|
||||
#define VALUE_BOOL 0x48
|
||||
|
||||
#define SIZE_8 0x01
|
||||
#define SIZE_16 0x02
|
||||
#define SIZE_32 0x04
|
||||
|
||||
#include "nandsim_rcfile.h"
|
||||
|
||||
/*
|
||||
* keyname = name of a key,
|
||||
* mandatory = is key mandatory in section belonging to, 0=false 1=true
|
||||
* valuetype = what kind of value is assigned to that key, e.g.
|
||||
* VALUE_UINT | SIZE_8 -- unsigned uint size 8 bits;
|
||||
* VALUE_UINTARRAY | SIZE_8 -- array of uints 8-bit long;
|
||||
* VALUE_BOOL -- 'on', 'off','true','false','yes' or 'no'
|
||||
* literals;
|
||||
* VALUE_STRING -- strings
|
||||
* field = ptr to the field that should hold value for parsed value
|
||||
* maxlength = contains maximum length of an array (used only with either
|
||||
* VALUE_STRING or VALUE_(U)INTARRAY value types.
|
||||
*/
|
||||
struct nandsim_key {
|
||||
const char *keyname;
|
||||
uint8_t mandatory;
|
||||
uint8_t valuetype;
|
||||
void *field;
|
||||
uint32_t maxlength;
|
||||
};
|
||||
struct nandsim_section {
|
||||
const char *name;
|
||||
struct nandsim_key *keys;
|
||||
};
|
||||
|
||||
struct nandsim_config {
|
||||
struct sim_param **simparams;
|
||||
struct sim_chip **simchips;
|
||||
struct sim_ctrl **simctrls;
|
||||
int chipcnt;
|
||||
int ctrlcnt;
|
||||
};
|
||||
|
||||
int parse_intarray(char *, int **);
|
||||
int parse_config(char *, const char *);
|
||||
int parse_section(struct rcfile *, const char *, int);
|
||||
int compare_configs(struct nandsim_config *, struct nandsim_config *);
|
||||
int convert_argint(char *, int *);
|
||||
int convert_arguint(char *, unsigned int *);
|
||||
|
||||
#endif /* _NANDSIM_CONFPARSER_H_ */
|
440
usr.sbin/nandsim/nandsim_rcfile.c
Normal file
440
usr.sbin/nandsim/nandsim_rcfile.c
Normal file
@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Copyright (c) 1999, Boris Popov
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nandsim_rcfile.h"
|
||||
|
||||
SLIST_HEAD(rcfile_head, rcfile);
|
||||
static struct rcfile_head pf_head = {NULL};
|
||||
static struct rcsection *rc_findsect(struct rcfile *rcp,
|
||||
const char *sectname, int sect_id);
|
||||
static struct rcsection *rc_addsect(struct rcfile *rcp,
|
||||
const char *sectname);
|
||||
static int rc_sect_free(struct rcsection *rsp);
|
||||
static struct rckey *rc_sect_findkey(struct rcsection *rsp,
|
||||
const char *keyname);
|
||||
static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
|
||||
char *value);
|
||||
static void rc_key_free(struct rckey *p);
|
||||
static void rc_parse(struct rcfile *rcp);
|
||||
|
||||
static struct rcfile* rc_find(const char *filename);
|
||||
|
||||
/*
|
||||
* open rcfile and load its content, if already open - return previous handle
|
||||
*/
|
||||
int
|
||||
rc_open(const char *filename, const char *mode,struct rcfile **rcfile)
|
||||
{
|
||||
struct rcfile *rcp;
|
||||
FILE *f;
|
||||
rcp = rc_find(filename);
|
||||
if (rcp) {
|
||||
*rcfile = rcp;
|
||||
return (0);
|
||||
}
|
||||
f = fopen (filename, mode);
|
||||
if (f == NULL)
|
||||
return errno;
|
||||
rcp = malloc(sizeof(struct rcfile));
|
||||
if (rcp == NULL) {
|
||||
fclose(f);
|
||||
return ENOMEM;
|
||||
}
|
||||
bzero(rcp, sizeof(struct rcfile));
|
||||
rcp->rf_name = strdup(filename);
|
||||
rcp->rf_f = f;
|
||||
SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
|
||||
rc_parse(rcp);
|
||||
*rcfile = rcp;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
rc_close(struct rcfile *rcp)
|
||||
{
|
||||
struct rcsection *p,*n;
|
||||
|
||||
fclose(rcp->rf_f);
|
||||
for (p = SLIST_FIRST(&rcp->rf_sect); p; ) {
|
||||
n = p;
|
||||
p = SLIST_NEXT(p,rs_next);
|
||||
rc_sect_free(n);
|
||||
}
|
||||
free(rcp->rf_name);
|
||||
SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
|
||||
free(rcp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct rcfile*
|
||||
rc_find(const char *filename)
|
||||
{
|
||||
struct rcfile *p;
|
||||
|
||||
SLIST_FOREACH(p, &pf_head, rf_next)
|
||||
if (strcmp (filename, p->rf_name) == 0)
|
||||
return (p);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Find section with given name and id */
|
||||
static struct rcsection *
|
||||
rc_findsect(struct rcfile *rcp, const char *sectname, int sect_id)
|
||||
{
|
||||
struct rcsection *p;
|
||||
|
||||
SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
|
||||
if (strcmp(p->rs_name, sectname) == 0 && p->rs_id == sect_id)
|
||||
return (p);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct rcsection *
|
||||
rc_addsect(struct rcfile *rcp, const char *sectname)
|
||||
{
|
||||
struct rcsection *p;
|
||||
int id = 0;
|
||||
p = rc_findsect(rcp, sectname, 0);
|
||||
if (p) {
|
||||
/*
|
||||
* If section with that name already exists -- add one more,
|
||||
* same named, but with different id (higher by one)
|
||||
*/
|
||||
while (p != NULL) {
|
||||
id = p->rs_id + 1;
|
||||
p = rc_findsect(rcp, sectname, id);
|
||||
}
|
||||
}
|
||||
p = malloc(sizeof(*p));
|
||||
if (!p)
|
||||
return (NULL);
|
||||
p->rs_name = strdup(sectname);
|
||||
p->rs_id = id;
|
||||
SLIST_INIT(&p->rs_keys);
|
||||
SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
|
||||
return (p);
|
||||
}
|
||||
|
||||
static int
|
||||
rc_sect_free(struct rcsection *rsp)
|
||||
{
|
||||
struct rckey *p,*n;
|
||||
|
||||
for (p = SLIST_FIRST(&rsp->rs_keys); p; ) {
|
||||
n = p;
|
||||
p = SLIST_NEXT(p,rk_next);
|
||||
rc_key_free(n);
|
||||
}
|
||||
free(rsp->rs_name);
|
||||
free(rsp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct rckey *
|
||||
rc_sect_findkey(struct rcsection *rsp, const char *keyname)
|
||||
{
|
||||
struct rckey *p;
|
||||
|
||||
SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
|
||||
if (strcmp(p->rk_name, keyname)==0)
|
||||
return (p);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct rckey *
|
||||
rc_sect_addkey(struct rcsection *rsp, const char *name, char *value)
|
||||
{
|
||||
struct rckey *p;
|
||||
p = rc_sect_findkey(rsp, name);
|
||||
if (p) {
|
||||
free(p->rk_value);
|
||||
} else {
|
||||
p = malloc(sizeof(*p));
|
||||
if (!p)
|
||||
return (NULL);
|
||||
SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
|
||||
p->rk_name = strdup(name);
|
||||
}
|
||||
p->rk_value = value ? strdup(value) : strdup("");
|
||||
return (p);
|
||||
}
|
||||
|
||||
static void
|
||||
rc_key_free(struct rckey *p)
|
||||
{
|
||||
free(p->rk_value);
|
||||
free(p->rk_name);
|
||||
free(p);
|
||||
}
|
||||
|
||||
enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
|
||||
|
||||
static void
|
||||
rc_parse(struct rcfile *rcp)
|
||||
{
|
||||
FILE *f = rcp->rf_f;
|
||||
int state = stNewLine, c;
|
||||
struct rcsection *rsp = NULL;
|
||||
struct rckey *rkp = NULL;
|
||||
char buf[2048];
|
||||
char *next = buf, *last = &buf[sizeof(buf)-1];
|
||||
|
||||
while ((c = getc (f)) != EOF) {
|
||||
if (c == '\r')
|
||||
continue;
|
||||
if (state == stNewLine) {
|
||||
next = buf;
|
||||
if (isspace(c))
|
||||
continue; /* skip leading junk */
|
||||
if (c == '[') {
|
||||
state = stHeader;
|
||||
rsp = NULL;
|
||||
continue;
|
||||
}
|
||||
if (c == '#' || c == ';') {
|
||||
state = stSkipToEOL;
|
||||
} else { /* something meaningful */
|
||||
state = stGetKey;
|
||||
}
|
||||
}
|
||||
if (state == stSkipToEOL || next == last) {/* ignore long lines */
|
||||
if (c == '\n') {
|
||||
state = stNewLine;
|
||||
next = buf;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (state == stHeader) {
|
||||
if (c == ']') {
|
||||
*next = 0;
|
||||
next = buf;
|
||||
rsp = rc_addsect(rcp, buf);
|
||||
state = stSkipToEOL;
|
||||
} else
|
||||
*next++ = c;
|
||||
continue;
|
||||
}
|
||||
if (state == stGetKey) {
|
||||
if (c == ' ' || c == '\t')/* side effect: 'key name='*/
|
||||
continue; /* become 'keyname=' */
|
||||
if (c == '\n') { /* silently ignore ... */
|
||||
state = stNewLine;
|
||||
continue;
|
||||
}
|
||||
if (c != '=') {
|
||||
*next++ = c;
|
||||
continue;
|
||||
}
|
||||
*next = 0;
|
||||
if (rsp == NULL) {
|
||||
fprintf(stderr, "Key '%s' defined before "
|
||||
"section\n", buf);
|
||||
state = stSkipToEOL;
|
||||
continue;
|
||||
}
|
||||
rkp = rc_sect_addkey(rsp, buf, NULL);
|
||||
next = buf;
|
||||
state = stGetValue;
|
||||
continue;
|
||||
}
|
||||
/* only stGetValue left */
|
||||
if (state != stGetValue) {
|
||||
fprintf(stderr, "Well, I can't parse file "
|
||||
"'%s'\n",rcp->rf_name);
|
||||
state = stSkipToEOL;
|
||||
}
|
||||
if (c != '\n') {
|
||||
*next++ = c;
|
||||
continue;
|
||||
}
|
||||
*next = 0;
|
||||
rkp->rk_value = strdup(buf);
|
||||
state = stNewLine;
|
||||
rkp = NULL;
|
||||
} /* while */
|
||||
if (c == EOF && state == stGetValue) {
|
||||
*next = 0;
|
||||
rkp->rk_value = strdup(buf);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
rc_getstringptr(struct rcfile *rcp, const char *section, int sect_id,
|
||||
const char *key, char **dest)
|
||||
{
|
||||
struct rcsection *rsp;
|
||||
struct rckey *rkp;
|
||||
|
||||
*dest = NULL;
|
||||
rsp = rc_findsect(rcp, section, sect_id);
|
||||
if (!rsp)
|
||||
return (ENOENT);
|
||||
rkp = rc_sect_findkey(rsp,key);
|
||||
if (!rkp)
|
||||
return (ENOENT);
|
||||
*dest = rkp->rk_value;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
rc_getstring(struct rcfile *rcp, const char *section, int sect_id,
|
||||
const char *key, unsigned int maxlen, char *dest)
|
||||
{
|
||||
char *value;
|
||||
int error;
|
||||
|
||||
error = rc_getstringptr(rcp, section, sect_id, key, &value);
|
||||
if (error)
|
||||
return (error);
|
||||
if (strlen(value) >= maxlen) {
|
||||
fprintf(stderr, "line too long for key '%s' in section '%s',"
|
||||
"max = %d\n",key, section, maxlen);
|
||||
return (EINVAL);
|
||||
}
|
||||
strcpy(dest,value);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
rc_getint(struct rcfile *rcp, const char *section, int sect_id,
|
||||
const char *key, int *value)
|
||||
{
|
||||
struct rcsection *rsp;
|
||||
struct rckey *rkp;
|
||||
|
||||
rsp = rc_findsect(rcp, section, sect_id);
|
||||
if (!rsp)
|
||||
return (ENOENT);
|
||||
rkp = rc_sect_findkey(rsp,key);
|
||||
if (!rkp)
|
||||
return (ENOENT);
|
||||
errno = 0;
|
||||
*value = strtol(rkp->rk_value,NULL,0);
|
||||
if (errno) {
|
||||
fprintf(stderr, "invalid int value '%s' for key '%s' in "
|
||||
"section '%s'\n",rkp->rk_value,key,section);
|
||||
return (errno);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 1,yes,true
|
||||
* 0,no,false
|
||||
*/
|
||||
int
|
||||
rc_getbool(struct rcfile *rcp, const char *section, int sect_id,
|
||||
const char *key, int *value)
|
||||
{
|
||||
struct rcsection *rsp;
|
||||
struct rckey *rkp;
|
||||
char *p;
|
||||
|
||||
rsp = rc_findsect(rcp, section, sect_id);
|
||||
if (!rsp)
|
||||
return (ENOENT);
|
||||
rkp = rc_sect_findkey(rsp,key);
|
||||
if (!rkp)
|
||||
return (ENOENT);
|
||||
p = rkp->rk_value;
|
||||
while (*p && isspace(*p)) p++;
|
||||
if (*p == '0' || strcasecmp(p,"no") == 0 ||
|
||||
strcasecmp(p, "false") == 0 ||
|
||||
strcasecmp(p, "off") == 0) {
|
||||
*value = 0;
|
||||
return (0);
|
||||
}
|
||||
if (*p == '1' || strcasecmp(p,"yes") == 0 ||
|
||||
strcasecmp(p, "true") == 0 ||
|
||||
strcasecmp(p, "on") == 0) {
|
||||
*value = 1;
|
||||
return (0);
|
||||
}
|
||||
fprintf(stderr, "invalid boolean value '%s' for key '%s' in section "
|
||||
"'%s' \n",p, key, section);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/* Count how many sections with given name exists in configuration. */
|
||||
int rc_getsectionscount(struct rcfile *f, const char *sectname)
|
||||
{
|
||||
struct rcsection *p;
|
||||
int count = 0;
|
||||
|
||||
p = rc_findsect(f, sectname, 0);
|
||||
if (p) {
|
||||
while (p != NULL) {
|
||||
count = p->rs_id + 1;
|
||||
p = rc_findsect(f, sectname, count);
|
||||
}
|
||||
return (count);
|
||||
} else
|
||||
return (0);
|
||||
}
|
||||
|
||||
char **
|
||||
rc_getkeys(struct rcfile *rcp, const char *sectname, int sect_id)
|
||||
{
|
||||
struct rcsection *rsp;
|
||||
struct rckey *p;
|
||||
char **names_tbl;
|
||||
int i = 0, count = 0;
|
||||
|
||||
rsp = rc_findsect(rcp, sectname, sect_id);
|
||||
if (rsp == NULL)
|
||||
return (NULL);
|
||||
|
||||
SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
|
||||
count++;
|
||||
|
||||
names_tbl = malloc(sizeof(char *) * (count + 1));
|
||||
if (names_tbl == NULL)
|
||||
return (NULL);
|
||||
|
||||
SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
|
||||
names_tbl[i++] = p->rk_name;
|
||||
|
||||
names_tbl[i] = NULL;
|
||||
return (names_tbl);
|
||||
}
|
||||
|
70
usr.sbin/nandsim/nandsim_rcfile.h
Normal file
70
usr.sbin/nandsim/nandsim_rcfile.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 1999, Boris Popov
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp
|
||||
*/
|
||||
|
||||
#ifndef _SIMRC_H_
|
||||
#define _SIMRC_H_
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
struct rckey {
|
||||
SLIST_ENTRY(rckey) rk_next;
|
||||
char *rk_name; /* key name */
|
||||
char *rk_value; /* key value */
|
||||
};
|
||||
|
||||
struct rcsection {
|
||||
SLIST_ENTRY(rcsection) rs_next;
|
||||
SLIST_HEAD(rckey_head,rckey) rs_keys; /* key list */
|
||||
char *rs_name; /* section name */
|
||||
int rs_id; /* allow few same named */
|
||||
};
|
||||
|
||||
struct rcfile {
|
||||
SLIST_ENTRY(rcfile) rf_next;
|
||||
SLIST_HEAD(rcsec_head, rcsection) rf_sect; /* sections list */
|
||||
char *rf_name; /* file name */
|
||||
FILE *rf_f; /* file desc */
|
||||
};
|
||||
|
||||
int rc_open(const char *, const char *,struct rcfile **);
|
||||
int rc_close(struct rcfile *);
|
||||
int rc_getstringptr(struct rcfile *, const char *, int, const char *,
|
||||
char **);
|
||||
int rc_getstring(struct rcfile *, const char *, int, const char *,
|
||||
unsigned int, char *);
|
||||
int rc_getint(struct rcfile *, const char *, int, const char *, int *);
|
||||
int rc_getbool(struct rcfile *, const char *, int, const char *, int *);
|
||||
int rc_getsectionscount(struct rcfile *, const char *);
|
||||
char **rc_getkeys(struct rcfile *, const char *, int);
|
||||
|
||||
#endif /* _SIMRC_H_ */
|
174
usr.sbin/nandsim/sample.conf
Normal file
174
usr.sbin/nandsim/sample.conf
Normal file
@ -0,0 +1,174 @@
|
||||
#-
|
||||
# Copyright (C) 2009-2012 Semihalf
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
#
|
||||
# Sample NANDsim configuration file.
|
||||
#
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# [sim] General (common) simulator configuration section.
|
||||
#
|
||||
[sim]
|
||||
# log_level=0..255
|
||||
log_level=11
|
||||
|
||||
# log_output=[none, console, ram, file]
|
||||
#
|
||||
# When log_output=file is specified, each [ctrl] section must have a
|
||||
# corresponding 'log_filename' field provided, which specifies log file name
|
||||
# to be used.
|
||||
log_output=none
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# [ctrl] Controller configuration section.
|
||||
#
|
||||
# There can be a number of controllers defined for simulation, each has a
|
||||
# dedicated [ctrl] section. With a given controller there are associated
|
||||
# subordinate NAND chips, which are tied to chip select lines.
|
||||
#
|
||||
[ctrl]
|
||||
# The number of this controller.
|
||||
# ctrl_num=0..3
|
||||
ctrl_num=0
|
||||
|
||||
# The number of chip selects available at this controller.
|
||||
# num_cs=1..4
|
||||
num_cs=1
|
||||
|
||||
# ECC enable flag.
|
||||
# ecc=[on|off]
|
||||
ecc=on
|
||||
|
||||
# ECC layout. This is the list of byte offsets within OOB area, which comprise
|
||||
# the ECC contents set.
|
||||
#
|
||||
# ecc_layout=[byte1, byte2-byte3, ..byten]
|
||||
ecc_layout=[0-53]
|
||||
|
||||
# Absolute path to the log file for this controller.
|
||||
#log_filename=/var/log/nandsim-ctl0.log
|
||||
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# [chip] Chip configuration section.
|
||||
#
|
||||
# There can be a number of individual NAND chip devices defined for
|
||||
# simulation, and each has a dedicated [chip] section.
|
||||
#
|
||||
# A particular chip needs to be associated with its parent NAND controller by
|
||||
# specifying the following fields: controller number (chip_ctrl) and the chip
|
||||
# select line it is connected to (chip_cs). The chip can be connected to only
|
||||
# a single (and unique) controller:cs pair.
|
||||
#
|
||||
[chip]
|
||||
# The number of parent controller. This has to fit one of the controller
|
||||
# instance number (ctrl_num from [ctrl] section).
|
||||
# chip_ctrl=0..3
|
||||
chip_ctrl=0
|
||||
|
||||
# Chip select line.
|
||||
# chip_cs=0..3
|
||||
chip_cs=0
|
||||
|
||||
# ONFI device identifier.
|
||||
# device_id=0x00..0xff
|
||||
device_id=0xd3
|
||||
|
||||
# ONFI manufacturer identifier.
|
||||
# manufacturer_id=0x00..0xff
|
||||
manufacturer_id=0xec
|
||||
|
||||
# Textual description of the chip.
|
||||
# model="model_name"
|
||||
model="k9xxg08uxM:1GiB 3,3V 8-bit"
|
||||
|
||||
# Textual name of the chip manufacturer.
|
||||
# manufacturer="manufacturer name"
|
||||
manufacturer="SAMSUNG"
|
||||
|
||||
# page_size=[must be power of 2 and >= 512] (in bytes)
|
||||
page_size=2048
|
||||
# oob_size=[>0]
|
||||
oob_size=64
|
||||
# pages_per_block=n*32
|
||||
pages_per_block=64
|
||||
# blocks_per_lun=[>0]
|
||||
blocks_per_lun=4096
|
||||
# luns=1..N
|
||||
luns=1
|
||||
# column_addr_cycle=[1,2]
|
||||
column_addr_cycle=2
|
||||
# row_addr_cycle=[1,2,3]
|
||||
row_addr_cycle=3
|
||||
|
||||
# program_time= (in us)
|
||||
program_time=0
|
||||
# erase_time= (in us)
|
||||
erase_time=0
|
||||
# read_time= (in us)
|
||||
read_time=0
|
||||
# ccs_time= (in us)
|
||||
#ccs_time=200
|
||||
|
||||
# Simulate write-protect on the chip.
|
||||
# write_protect=[yes|no]
|
||||
#write_protect=no
|
||||
|
||||
# Blocks wear-out threshold. Each block has a counter of program-erase cycles;
|
||||
# when this counter reaches 'wear_out' value a given block is treated as a bad
|
||||
# block (access will report error).
|
||||
#
|
||||
# Setting wear_out to 0 means that blocks will never wear out.
|
||||
#
|
||||
# wear_out=0..100000
|
||||
wear_out=50000
|
||||
|
||||
# Errors per million read/write bytes. This simulates an accidental read/write
|
||||
# block error, which can happen in real devices with certain probability. Note
|
||||
# this isn't a bad block condition i.e. the block at which the read/write
|
||||
# operation is simulated to fail here remains usable, only the operation has
|
||||
# not succeeded (this is where ECC comes into play and is supposed to correct
|
||||
# such problems).
|
||||
#
|
||||
# error_ratio=0..1000000
|
||||
#error_ratio=50
|
||||
|
||||
# Chip data bus width. All chips connected to the same controller must have
|
||||
# the same bus width.
|
||||
#
|
||||
# width=[8|16]
|
||||
width=8
|
||||
|
||||
# Bad block map. NANDsim emulates bad block behavior upon accessing a block
|
||||
# with number from the specified list.
|
||||
#
|
||||
# bad_block_map=[bad_block1, bad_block2-bad_block3, ..bad_blockn]
|
||||
bad_block_map=[100-200]
|
||||
|
11
usr.sbin/nandtool/Makefile
Normal file
11
usr.sbin/nandtool/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= nandtool
|
||||
SRCS= nandtool.c nand_read.c nand_write.c nand_erase.c nand_info.c
|
||||
SRCS+= nand_readoob.c nand_writeoob.c
|
||||
BINDIR= /usr/sbin
|
||||
DPADD= ${LIBGEOM}
|
||||
LDADD= -lgeom
|
||||
MAN= nandtool.8
|
||||
|
||||
.include <bsd.prog.mk>
|
114
usr.sbin/nandtool/nand_erase.c
Normal file
114
usr.sbin/nandtool/nand_erase.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/disk.h>
|
||||
#include <libgeom.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nandtool.h"
|
||||
|
||||
int nand_erase(struct cmd_param *params)
|
||||
{
|
||||
struct chip_param_io chip_params;
|
||||
char *dev;
|
||||
int fd = -1, ret = 0;
|
||||
off_t pos, count;
|
||||
off_t start, nblocks, i;
|
||||
int block_size, mult;
|
||||
|
||||
if (!(dev = param_get_string(params, "dev"))) {
|
||||
fprintf(stderr, "Please supply valid 'dev' parameter.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (param_has_value(params, "count"))
|
||||
count = param_get_intx(params, "count");
|
||||
else
|
||||
count = 1;
|
||||
|
||||
if ((fd = g_open(dev, 1)) < 0) {
|
||||
perrorf("Cannot open %s", dev);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
|
||||
perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
block_size = chip_params.page_size * chip_params.pages_per_block;
|
||||
|
||||
if (param_has_value(params, "page")) {
|
||||
pos = chip_params.page_size * param_get_intx(params, "page");
|
||||
mult = chip_params.page_size;
|
||||
} else if (param_has_value(params, "block")) {
|
||||
pos = block_size * param_get_intx(params, "block");
|
||||
mult = block_size;
|
||||
} else if (param_has_value(params, "pos")) {
|
||||
pos = param_get_intx(params, "pos");
|
||||
mult = 1;
|
||||
} else {
|
||||
/* Erase whole chip */
|
||||
if (ioctl(fd, DIOCGMEDIASIZE, &count) == -1) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
mult = 1;
|
||||
}
|
||||
|
||||
if (pos % block_size) {
|
||||
fprintf(stderr, "Position must be block-size aligned!\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
count *= mult;
|
||||
start = pos / block_size;
|
||||
nblocks = count / block_size;
|
||||
|
||||
for (i = 0; i < nblocks; i++) {
|
||||
if (g_delete(fd, (start + i) * block_size, block_size) == -1) {
|
||||
perrorf("Cannot erase block %d - probably a bad block",
|
||||
start + i);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
g_close(fd);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
86
usr.sbin/nandtool/nand_info.c
Normal file
86
usr.sbin/nandtool/nand_info.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <libgeom.h>
|
||||
#include <sys/disk.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nandtool.h"
|
||||
|
||||
int nand_info(struct cmd_param *params)
|
||||
{
|
||||
struct chip_param_io chip_params;
|
||||
int fd = -1, ret = 0;
|
||||
int block_size;
|
||||
off_t chip_size, media_size;
|
||||
const char *dev;
|
||||
|
||||
if ((dev = param_get_string(params, "dev")) == NULL) {
|
||||
fprintf(stderr, "Please supply 'dev' parameter, eg. "
|
||||
"'dev=/dev/gnand0'\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((fd = g_open(dev, 1)) == -1) {
|
||||
perrorf("Cannot open %s", dev);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
|
||||
perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ioctl(fd, DIOCGMEDIASIZE, &media_size) == -1) {
|
||||
perrorf("Cannot ioctl(DIOCGMEDIASIZE)");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
block_size = chip_params.page_size * chip_params.pages_per_block;
|
||||
chip_size = block_size * chip_params.blocks;
|
||||
|
||||
printf("Device:\t\t\t%s\n", dev);
|
||||
printf("Page size:\t\t%d bytes\n", chip_params.page_size);
|
||||
printf("Block size:\t\t%d bytes (%d KB)\n", block_size,
|
||||
block_size / 1024);
|
||||
printf("OOB size per page:\t%d bytes\n", chip_params.oob_size);
|
||||
printf("Chip size:\t\t%jd MB\n", (uintmax_t)(chip_size / 1024 / 1024));
|
||||
printf("Slice size:\t\t%jd MB\n",
|
||||
(uintmax_t)(media_size / 1024 / 1024));
|
||||
|
||||
out:
|
||||
g_close(fd);
|
||||
|
||||
return (ret);
|
||||
}
|
139
usr.sbin/nandtool/nand_read.c
Normal file
139
usr.sbin/nandtool/nand_read.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgeom.h>
|
||||
#include <sys/disk.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nandtool.h"
|
||||
|
||||
int nand_read(struct cmd_param *params)
|
||||
{
|
||||
struct chip_param_io chip_params;
|
||||
int fd = -1, out_fd = -1, done = 0, ret = 0;
|
||||
char *dev, *out;
|
||||
int pos, count, mult, block_size;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
if (!(dev = param_get_string(params, "dev"))) {
|
||||
fprintf(stderr, "You must specify 'dev' parameter\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((out = param_get_string(params, "out"))) {
|
||||
out_fd = open(out, O_WRONLY|O_CREAT);
|
||||
if (out_fd == -1) {
|
||||
perrorf("Cannot open %s for writing", out);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((fd = g_open(dev, 1)) == -1) {
|
||||
perrorf("Cannot open %s", dev);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
|
||||
perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
block_size = chip_params.page_size * chip_params.pages_per_block;
|
||||
|
||||
if (param_has_value(params, "page")) {
|
||||
pos = chip_params.page_size * param_get_int(params, "page");
|
||||
mult = chip_params.page_size;
|
||||
} else if (param_has_value(params, "block")) {
|
||||
pos = block_size * param_get_int(params, "block");
|
||||
mult = block_size;
|
||||
} else if (param_has_value(params, "pos")) {
|
||||
pos = param_get_int(params, "pos");
|
||||
mult = 1;
|
||||
if (pos % chip_params.page_size) {
|
||||
fprintf(stderr, "Position must be page-size aligned!\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "You must specify one of: 'block', 'page',"
|
||||
"'pos' arguments\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(param_has_value(params, "count")))
|
||||
count = mult;
|
||||
else
|
||||
count = param_get_int(params, "count") * mult;
|
||||
|
||||
if (!(buf = malloc(chip_params.page_size))) {
|
||||
perrorf("Cannot allocate buffer [size %x]",
|
||||
chip_params.page_size);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lseek(fd, pos, SEEK_SET);
|
||||
|
||||
while (done < count) {
|
||||
if ((ret = read(fd, buf, chip_params.page_size)) !=
|
||||
(int32_t)chip_params.page_size) {
|
||||
perrorf("read error (read %d bytes)", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (out_fd != -1) {
|
||||
done += ret;
|
||||
if ((ret = write(out_fd, buf, chip_params.page_size)) !=
|
||||
(int32_t)chip_params.page_size) {
|
||||
perrorf("write error (written %d bytes)", ret);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
hexdumpoffset(buf, chip_params.page_size, done);
|
||||
done += ret;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
g_close(fd);
|
||||
if (out_fd != -1)
|
||||
close(out_fd);
|
||||
if (buf)
|
||||
free(buf);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
111
usr.sbin/nandtool/nand_readoob.c
Normal file
111
usr.sbin/nandtool/nand_readoob.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgeom.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/disk.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nandtool.h"
|
||||
|
||||
int nand_read_oob(struct cmd_param *params)
|
||||
{
|
||||
struct chip_param_io chip_params;
|
||||
struct nand_oob_rw req;
|
||||
char *dev, *out;
|
||||
int fd = -1, fd_out = -1, ret = 0;
|
||||
int page;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
if ((page = param_get_int(params, "page")) < 0) {
|
||||
fprintf(stderr, "You must supply valid 'page' argument.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (!(dev = param_get_string(params, "dev"))) {
|
||||
fprintf(stderr, "You must supply 'dev' argument.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((out = param_get_string(params, "out"))) {
|
||||
if ((fd_out = open(out, O_WRONLY | O_CREAT)) == -1) {
|
||||
perrorf("Cannot open %s", out);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fd = g_open(dev, 1)) == -1) {
|
||||
perrorf("Cannot open %s", dev);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
|
||||
perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = malloc(chip_params.oob_size);
|
||||
if (buf == NULL) {
|
||||
perrorf("Cannot allocate %d bytes\n", chip_params.oob_size);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
req.page = page;
|
||||
req.len = chip_params.oob_size;
|
||||
req.data = buf;
|
||||
|
||||
if (ioctl(fd, NAND_IO_OOB_READ, &req) == -1) {
|
||||
perrorf("Cannot read OOB from %s", dev);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fd_out != -1)
|
||||
write(fd_out, buf, chip_params.oob_size);
|
||||
else
|
||||
hexdump(buf, chip_params.oob_size);
|
||||
|
||||
out:
|
||||
close(fd_out);
|
||||
|
||||
if (fd != -1)
|
||||
g_close(fd);
|
||||
if (buf)
|
||||
free(buf);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
143
usr.sbin/nandtool/nand_write.c
Normal file
143
usr.sbin/nandtool/nand_write.c
Normal file
@ -0,0 +1,143 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgeom.h>
|
||||
#include <sys/disk.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nandtool.h"
|
||||
|
||||
int nand_write(struct cmd_param *params)
|
||||
{
|
||||
struct chip_param_io chip_params;
|
||||
char *dev, *file;
|
||||
int in_fd = -1, ret = 0, done = 0;
|
||||
int fd, block_size, mult, pos, count;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
if (!(dev = param_get_string(params, "dev"))) {
|
||||
fprintf(stderr, "Please supply 'dev' argument.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (!(file = param_get_string(params, "in"))) {
|
||||
fprintf(stderr, "Please supply 'in' argument.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((fd = g_open(dev, 1)) == -1) {
|
||||
perrorf("Cannot open %s", dev);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((in_fd = open(file, O_RDONLY)) == -1) {
|
||||
perrorf("Cannot open file %s", file);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
|
||||
perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
block_size = chip_params.page_size * chip_params.pages_per_block;
|
||||
|
||||
if (param_has_value(params, "page")) {
|
||||
pos = chip_params.page_size * param_get_int(params, "page");
|
||||
mult = chip_params.page_size;
|
||||
} else if (param_has_value(params, "block")) {
|
||||
pos = block_size * param_get_int(params, "block");
|
||||
mult = block_size;
|
||||
} else if (param_has_value(params, "pos")) {
|
||||
pos = param_get_int(params, "pos");
|
||||
mult = 1;
|
||||
if (pos % chip_params.page_size) {
|
||||
fprintf(stderr, "Position must be page-size "
|
||||
"aligned!\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "You must specify one of: 'block', 'page',"
|
||||
"'pos' arguments\n");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(param_has_value(params, "count")))
|
||||
count = mult;
|
||||
else
|
||||
count = param_get_int(params, "count") * mult;
|
||||
|
||||
if (!(buf = malloc(chip_params.page_size))) {
|
||||
perrorf("Cannot allocate buffer [size %x]",
|
||||
chip_params.page_size);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lseek(fd, pos, SEEK_SET);
|
||||
|
||||
while (done < count) {
|
||||
if ((ret = read(in_fd, buf, chip_params.page_size)) !=
|
||||
(int32_t)chip_params.page_size) {
|
||||
if (ret > 0) {
|
||||
/* End of file ahead, truncate here */
|
||||
break;
|
||||
} else {
|
||||
perrorf("Cannot read from %s", file);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = write(fd, buf, chip_params.page_size)) !=
|
||||
(int32_t)chip_params.page_size) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
done += ret;
|
||||
}
|
||||
|
||||
out:
|
||||
g_close(fd);
|
||||
if (in_fd != -1)
|
||||
close(in_fd);
|
||||
if (buf)
|
||||
free(buf);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
113
usr.sbin/nandtool/nand_writeoob.c
Normal file
113
usr.sbin/nandtool/nand_writeoob.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgeom.h>
|
||||
#include <sys/disk.h>
|
||||
#include <dev/nand/nand_dev.h>
|
||||
#include "nandtool.h"
|
||||
|
||||
int nand_write_oob(struct cmd_param *params)
|
||||
{
|
||||
struct chip_param_io chip_params;
|
||||
struct nand_oob_rw req;
|
||||
char *dev, *in;
|
||||
int fd = -1, fd_in = -1, ret = 0;
|
||||
uint8_t *buf = NULL;
|
||||
int page;
|
||||
|
||||
if (!(dev = param_get_string(params, "dev"))) {
|
||||
fprintf(stderr, "Please supply valid 'dev' parameter.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (!(in = param_get_string(params, "in"))) {
|
||||
fprintf(stderr, "Please supply valid 'in' parameter.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((page = param_get_int(params, "page")) < 0) {
|
||||
fprintf(stderr, "Please supply valid 'page' parameter.\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((fd = g_open(dev, 1)) == -1) {
|
||||
perrorf("Cannot open %s", dev);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ((fd_in = open(in, O_RDONLY)) == -1) {
|
||||
perrorf("Cannot open %s", in);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) {
|
||||
perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = malloc(chip_params.oob_size);
|
||||
if (buf == NULL) {
|
||||
perrorf("Cannot allocate %d bytes\n", chip_params.oob_size);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (read(fd_in, buf, chip_params.oob_size) == -1) {
|
||||
perrorf("Cannot read from %s", in);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
req.page = page;
|
||||
req.len = chip_params.oob_size;
|
||||
req.data = buf;
|
||||
|
||||
if (ioctl(fd, NAND_IO_OOB_PROG, &req) == -1) {
|
||||
perrorf("Cannot write OOB to %s", dev);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
g_close(fd);
|
||||
if (fd_in != -1)
|
||||
close(fd_in);
|
||||
if (buf)
|
||||
free(buf);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
188
usr.sbin/nandtool/nandtool.8
Normal file
188
usr.sbin/nandtool/nandtool.8
Normal file
@ -0,0 +1,188 @@
|
||||
.\" Copyright (c) 2010 Semihalf
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd April 10, 2012
|
||||
.Dt NANDTOOL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm nandtool
|
||||
.Nd NAND devices swiss army knife
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Ar command
|
||||
.Op Ar operands ...
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility can be used to perform various operations on
|
||||
.Xr gnand 4
|
||||
devices (read, write, erase,
|
||||
read and write OOB area and to get info about NAND flash chip).
|
||||
.Pp
|
||||
The following commands are available:
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
.It Cm read Ns
|
||||
Read pages from NAND device.
|
||||
.It Cm write Ns
|
||||
Write pages to NAND device.
|
||||
.It Cm erase Ns
|
||||
Erase blocks.
|
||||
Requires offset aligned to block granularity.
|
||||
.It Cm info Ns
|
||||
Get information about NAND chip (page size, block size, OOB area size, chip size
|
||||
and media size)
|
||||
.It Cm readoob Ns
|
||||
Read OOB area from specified page.
|
||||
.It Cm writeoob Ns
|
||||
Write OOB area bound to specified page.
|
||||
.It Cm help Ns
|
||||
Get usage info.
|
||||
.El
|
||||
.Sh COMMAND read
|
||||
.Pp
|
||||
The following operands are available for
|
||||
.Nm
|
||||
.Cm read
|
||||
command:
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
.It Cm dev Ns = Ns Ar <path>
|
||||
Path to a
|
||||
.Xr gnand 4
|
||||
device node, required for all operations.
|
||||
.It Cm out Ns = Ns Ar <file>
|
||||
Output file path. If not specified, page contents
|
||||
will be dumped to stdout in format similar to
|
||||
.Xr hexdump 1
|
||||
.It Cm page Ns = Ns Ar <n>
|
||||
Offset on device, expressed as page number.
|
||||
.It Cm block Ns = Ns Ar <n>
|
||||
Offset on device, expressed as block number.
|
||||
.It Cm pos Ns = Ns Ar <n>
|
||||
Offset on device, expressed in bytes (however, must be aligned
|
||||
to page granularity).
|
||||
.It Cm count Ns = Ns Ar <n>
|
||||
Count of objects (pages, blocks, bytes).
|
||||
.El
|
||||
.Sh COMMAND readoob
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
.Pp
|
||||
The following operands are available for
|
||||
.Nm
|
||||
.Cm readoob
|
||||
command:
|
||||
.Pp
|
||||
.It Cm dev Ns = Ns Ar <path>
|
||||
Path to NAND device node.
|
||||
.It Cm page Ns = Ns Ar <n>
|
||||
Offset on device, expressed as page number.
|
||||
.It Cm out Ns = Ns Ar <file>
|
||||
Output file path, optional.
|
||||
.El
|
||||
.Sh COMMAND write
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
The following operands are available for
|
||||
.Nm
|
||||
.Cm write
|
||||
command:
|
||||
.It Cm dev Ns = Ns Ar <path>
|
||||
Path to NAND device node.
|
||||
.It Cm page Ns = Ns Ar <n>
|
||||
Offset on device, expressed as page number.
|
||||
.It Cm block Ns = Ns Ar <n>
|
||||
Offset on device, expressed as block number.
|
||||
.It Cm pos Ns = Ns Ar <n>
|
||||
Offset on device, expressed in bytes (however, must be aligned
|
||||
to page granularity).
|
||||
.It Cm in Ns = Ns Ar <file>
|
||||
Input file path.
|
||||
.El
|
||||
.Sh COMMAND writeoob
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
The following operands are available for
|
||||
.Nm
|
||||
.Cm writeoob
|
||||
command:
|
||||
.It Cm dev Ns = Ns Ar <path>
|
||||
Path to NAND device node.
|
||||
.It Cm page Ns = Ns Ar <n>
|
||||
Offset on device, expressed as page number.
|
||||
.It Cm in Ns = Ns Ar <file>
|
||||
Input file path.
|
||||
.El
|
||||
.Sh COMMAND erase
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
The following operands are available for
|
||||
.Nm
|
||||
.Cm erase
|
||||
command:
|
||||
.It Cm dev Ns = Ns Ar <path>
|
||||
Path to NAND device node.
|
||||
.It Cm page Ns = Ns Ar <n>
|
||||
Offset on device, expressed as page number.
|
||||
.It Cm block Ns = Ns Ar <n>
|
||||
Offset on device, expressed as block number.
|
||||
.It Cm pos Ns = Ns Ar <n>
|
||||
Offset on device, epressed in bytes (however, must be aligned
|
||||
to block granularity).
|
||||
.It Cm count Ns = Ns Ar <n>
|
||||
Count of objects (pages, blocks, bytes).
|
||||
.El
|
||||
.Pp
|
||||
WARNING: The only required parameter for the \fBerase\fP command is
|
||||
.Ar dev .
|
||||
When no other arguments are provided the whole device is erased!
|
||||
.Sh COMMAND info
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
There is only one operand available for
|
||||
.Nm
|
||||
.Cm info
|
||||
command:
|
||||
.It Cm dev Ns = Ns Ar <path>
|
||||
Path to NAND device node.
|
||||
.El
|
||||
.Sh COMMAND help
|
||||
.Bl -tag -width ".Cm of Ns = Ns Ar file"
|
||||
There is only one operand available for
|
||||
.Nm
|
||||
.Cm help
|
||||
command:
|
||||
.Pp
|
||||
.It Cm topic Ns = Ns Ar <name>
|
||||
Help topic.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
If the supplied argument
|
||||
.Ar dev
|
||||
points to a device node other than gnand<num> or gnand.raw<num> both
|
||||
.Nm
|
||||
.Cm readoob
|
||||
and
|
||||
.Nm
|
||||
.Cm writeoob
|
||||
return error.
|
||||
.Sh SEE ALSO
|
||||
.Xr gnand 4
|
283
usr.sbin/nandtool/nandtool.c
Normal file
283
usr.sbin/nandtool/nandtool.c
Normal file
@ -0,0 +1,283 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <sysexits.h>
|
||||
#include <libgeom.h>
|
||||
#include "nandtool.h"
|
||||
#include "usage.h"
|
||||
|
||||
int usage(struct cmd_param *);
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
const char *usage;
|
||||
int (*handler)(struct cmd_param *);
|
||||
} commands[] = {
|
||||
{ "help", nand_help_usage, usage },
|
||||
{ "read", nand_read_usage, nand_read },
|
||||
{ "write", nand_write_usage, nand_write },
|
||||
{ "erase", nand_erase_usage, nand_erase },
|
||||
{ "readoob", nand_read_oob_usage, nand_read_oob },
|
||||
{ "writeoob", nand_write_oob_usage, nand_write_oob },
|
||||
{ "info", nand_info_usage, nand_info },
|
||||
{ NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
static char *
|
||||
_param_get_stringx(struct cmd_param *params, const char *name, int doexit)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; params[i].name[0] != '\0'; i++) {
|
||||
if (!strcmp(params[i].name, name))
|
||||
return params[i].value;
|
||||
}
|
||||
|
||||
if (doexit) {
|
||||
perrorf("Missing parameter %s", name);
|
||||
exit(1);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
param_get_string(struct cmd_param *params, const char *name)
|
||||
{
|
||||
|
||||
return (_param_get_stringx(params, name, 0));
|
||||
}
|
||||
|
||||
static int
|
||||
_param_get_intx(struct cmd_param *params, const char *name, int doexit)
|
||||
{
|
||||
int ret;
|
||||
char *str = _param_get_stringx(params, name, doexit);
|
||||
|
||||
if (!str)
|
||||
return (-1);
|
||||
|
||||
errno = 0;
|
||||
ret = (int)strtol(str, (char **)NULL, 10);
|
||||
if (errno) {
|
||||
if (doexit) {
|
||||
perrorf("Invalid value for parameter %s", name);
|
||||
exit(1);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
param_get_intx(struct cmd_param *params, const char *name)
|
||||
{
|
||||
|
||||
return (_param_get_intx(params, name, 1));
|
||||
}
|
||||
|
||||
int
|
||||
param_get_int(struct cmd_param *params, const char *name)
|
||||
{
|
||||
|
||||
return (_param_get_intx(params, name, 0));
|
||||
}
|
||||
|
||||
int
|
||||
param_get_boolean(struct cmd_param *params, const char *name)
|
||||
{
|
||||
char *str = param_get_string(params, name);
|
||||
|
||||
if (!str)
|
||||
return (0);
|
||||
|
||||
if (!strcmp(str, "true") || !strcmp(str, "yes"))
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
param_has_value(struct cmd_param *params, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; params[i].name[0] != '\0'; i++) {
|
||||
if (!strcmp(params[i].name, name))
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
param_get_count(struct cmd_param *params)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; params[i].name[0] != '\0'; i++);
|
||||
|
||||
return (i);
|
||||
}
|
||||
|
||||
void
|
||||
hexdumpoffset(uint8_t *buf, int length, int off)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < length; i += 16) {
|
||||
printf("%08x: ", off + i);
|
||||
|
||||
for (j = 0; j < 16; j++)
|
||||
printf("%02x ", buf[i+j]);
|
||||
|
||||
printf("| ");
|
||||
|
||||
for (j = 0; j < 16; j++) {
|
||||
printf("%c", isalnum(buf[i+j])
|
||||
? buf[i+j]
|
||||
: '.');
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hexdump(uint8_t *buf, int length)
|
||||
{
|
||||
|
||||
hexdumpoffset(buf, length, 0);
|
||||
}
|
||||
|
||||
void *
|
||||
xmalloc(size_t len)
|
||||
{
|
||||
void *ret = malloc(len);
|
||||
|
||||
if (!ret) {
|
||||
fprintf(stderr, "Cannot allocate buffer of %zd bytes. "
|
||||
"Exiting.\n", len);
|
||||
exit(EX_OSERR);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
perrorf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
int
|
||||
usage(struct cmd_param *params)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!params || !param_get_count(params)) {
|
||||
fprintf(stderr, "Usage: nandtool <command> [arguments...]\n");
|
||||
fprintf(stderr, "Arguments are in form 'name=value'.\n\n");
|
||||
fprintf(stderr, "Available commands:\n");
|
||||
|
||||
for (i = 0; commands[i].name != NULL; i++)
|
||||
fprintf(stderr, "\t%s\n", commands[i].name);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "For information about particular command, "
|
||||
"type:\n");
|
||||
fprintf(stderr, "'nandtool help topic=<command>'\n");
|
||||
} else if (param_has_value(params, "topic")) {
|
||||
for (i = 0; commands[i].name != NULL; i++) {
|
||||
if (!strcmp(param_get_string(params, "topic"),
|
||||
commands[i].name)) {
|
||||
fprintf(stderr, commands[i].usage, "nandtool");
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "No such command\n");
|
||||
return (EX_SOFTWARE);
|
||||
} else {
|
||||
fprintf(stderr, "Wrong arguments given. Try: 'nandtool help'\n");
|
||||
}
|
||||
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
struct cmd_param *params;
|
||||
int i, ret, idx;
|
||||
|
||||
if (argc < 2) {
|
||||
usage(NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
params = malloc(sizeof(struct cmd_param) * (argc - 1));
|
||||
|
||||
for (i = 2, idx = 0; i < argc; i++, idx++) {
|
||||
if (sscanf(argv[i], "%63[^=]=%63s", params[idx].name,
|
||||
params[idx].value) < 2) {
|
||||
fprintf(stderr, "Syntax error in argument %d. "
|
||||
"Argument should be in form 'name=value'.\n", i);
|
||||
free(params);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
params[idx].name[0] = '\0';
|
||||
params[idx].value[0] = '\0';
|
||||
|
||||
for (i = 0; commands[i].name != NULL; i++) {
|
||||
if (!strcmp(commands[i].name, argv[1])) {
|
||||
ret = commands[i].handler(params);
|
||||
free(params);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
|
||||
free(params);
|
||||
fprintf(stderr, "Unknown command. Try '%s help'\n", argv[0]);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
57
usr.sbin/nandtool/nandtool.h
Normal file
57
usr.sbin/nandtool/nandtool.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*-
|
||||
* Copyright (c) 2010-2012 Semihalf.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __UTILS_H
|
||||
#define __UTILS_H
|
||||
|
||||
struct cmd_param
|
||||
{
|
||||
char name[64];
|
||||
char value[64];
|
||||
};
|
||||
|
||||
char *param_get_string(struct cmd_param *, const char *);
|
||||
int param_get_int(struct cmd_param *, const char *);
|
||||
int param_get_intx(struct cmd_param *, const char *);
|
||||
int param_get_boolean(struct cmd_param *, const char *);
|
||||
int param_has_value(struct cmd_param *, const char *);
|
||||
int param_get_count(struct cmd_param *);
|
||||
void perrorf(const char *, ...);
|
||||
void hexdumpoffset(uint8_t *, int, int);
|
||||
void hexdump(uint8_t *, int);
|
||||
void *xmalloc(size_t);
|
||||
|
||||
/* Command handlers */
|
||||
int nand_read(struct cmd_param *);
|
||||
int nand_write(struct cmd_param *);
|
||||
int nand_read_oob(struct cmd_param *);
|
||||
int nand_write_oob(struct cmd_param *);
|
||||
int nand_erase(struct cmd_param *);
|
||||
int nand_info(struct cmd_param *);
|
||||
|
||||
#endif /* __UTILS_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user