summaryrefslogtreecommitdiff
path: root/nuttx/drivers/mtd
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-11-17 12:22:09 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-11-17 12:22:09 -0600
commitdd05b6c29749b2cc98f045cb057a1836a93cf4ca (patch)
tree0bd5c767821cf9f40d532b6f57aa10c720a02268 /nuttx/drivers/mtd
parentc871a0b7e2edcfb6957ed431e5ad84f43a28aa3d (diff)
downloadpx4-nuttx-dd05b6c29749b2cc98f045cb057a1836a93cf4ca.tar.gz
px4-nuttx-dd05b6c29749b2cc98f045cb057a1836a93cf4ca.tar.bz2
px4-nuttx-dd05b6c29749b2cc98f045cb057a1836a93cf4ca.zip
More NAND stuff
Diffstat (limited to 'nuttx/drivers/mtd')
-rw-r--r--nuttx/drivers/mtd/Kconfig6
-rw-r--r--nuttx/drivers/mtd/Make.defs3
-rwxr-xr-xnuttx/drivers/mtd/mtd_nand.c179
-rwxr-xr-xnuttx/drivers/mtd/mtd_nandraw.c (renamed from nuttx/drivers/mtd/mtd_rawnand.c)2
-rw-r--r--nuttx/drivers/mtd/mtd_nandscheme.c384
5 files changed, 558 insertions, 16 deletions
diff --git a/nuttx/drivers/mtd/Kconfig b/nuttx/drivers/mtd/Kconfig
index 1097897f0..687456972 100644
--- a/nuttx/drivers/mtd/Kconfig
+++ b/nuttx/drivers/mtd/Kconfig
@@ -87,6 +87,12 @@ config ARCH_NAND_HWECC
if MTD_NAND
+config MTD_NAND_BLOCKCHECK
+ bool "Block check"
+ default y
+ ---help---
+ Enable support for bad block checking.
+
config MTD_NAND_MAXNUMBLOCKS
int "Max blocks"
default 1024
diff --git a/nuttx/drivers/mtd/Make.defs b/nuttx/drivers/mtd/Make.defs
index 1d83cf5f9..feabf468f 100644
--- a/nuttx/drivers/mtd/Make.defs
+++ b/nuttx/drivers/mtd/Make.defs
@@ -46,7 +46,8 @@ CSRCS += mtd_partition.c
endif
ifeq ($(CONFIG_MTD_NAND),y)
-CSRCS += mtd_nand.c mtd_onfi.c mtd_rawnand.c mtd_nandmodel.c mtd_modeltab.c
+CSRCS += mtd_nand.c mtd_onfi.c mtd_nandscheme.c mtd_nandraw.c
+CSRCS += mtd_nandmodel.c mtd_modeltab.c
endif
ifeq ($(CONFIG_RAMMTD),y)
diff --git a/nuttx/drivers/mtd/mtd_nand.c b/nuttx/drivers/mtd/mtd_nand.c
index b0bf9826d..ada091b86 100755
--- a/nuttx/drivers/mtd/mtd_nand.c
+++ b/nuttx/drivers/mtd/mtd_nand.c
@@ -66,6 +66,10 @@
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
+/* Success Values returned by the nand_checkblock function */
+
+#define BADBLOCK 255
+#define GOODBLOCK 254
/****************************************************************************
* Private Types
@@ -74,6 +78,15 @@
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
+/* Sparing logic */
+
+#ifdef CONFIG_MTD_NAND_BLOCKCHECK
+static int nand_checkblock(FAR struct nand_dev_s *nand, off_t block);
+static int nand_devscan(FAR struct nand_dev_s *nand);
+#else
+# define nand_checkblock(n,b) (GOODBLOCK)
+# define nand_devscan(n)
+#endif
/* MTD driver methods */
@@ -95,6 +108,136 @@ static int nand_ioctl(struct mtd_dev_s *dev, int cmd,
****************************************************************************/
/****************************************************************************
+ * Name: nand_checkblock
+ *
+ * Description:
+ * Read and check for a bad block.
+ *
+ * Input Parameters:
+ * nand - Pointer to a struct nand_dev_s instance.
+ * block - Number of block to check.
+ *
+ * Returned Value:
+ * Returns BADBLOCK if the given block of a nandflash device is bad;
+ * returns GOODBLOCK if the block is good; or returns negated errno
+ * value on any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_NAND_BLOCKCHECK
+static int nand_checkblock(FAR struct nand_dev_s *nand, off_t block)
+{
+ uint8_t spare[CONFIG_MTD_NAND_MAXPAGESPARESIZE];
+ const struct nand_raw_s *raw;
+ const struct nand_model_s *model;
+ const struct nand_scheme_s *scheme;
+ uint8_t marker;
+ int ret;
+
+ DEBUGASSERT(nand && nand->raw);
+
+ /* Retrieve model scheme */
+
+ raw = nand->raw;
+ model = &raw->model;
+ scheme = nandmodel_getscheme(model);
+
+ /* Read spare area of first page of block */
+
+ ret = NAND_READPAGE(raw, block, 0, 0, spare);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Cannot read page #0 of block #%d\n", block);
+ return ret;
+ }
+
+ nandscheme_readbadblockmarker(scheme, spare, &marker);
+ if (marker != 0xff)
+ {
+ return BADBLOCK;
+ }
+
+ /* Read spare area of second page of block */
+
+ ret = NAND_READPAGE(raw, block, 1, 0, spare);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Cannot read page #1 of block #%d\n", block);
+ return ret;
+ }
+
+ nandscheme_readbadblockmarker(scheme, spare, &marker);
+ if (marker != 0xFF)
+ {
+ return BADBLOCK;
+ }
+
+ return GOODBLOCK;
+}
+#endif /* CONFIG_MTD_NAND_BLOCKCHECK */
+
+/****************************************************************************
+ * Name: nand_devscan
+ *
+ * Description:
+ * Scans the device to retrieve or create block status information.
+ *
+ * Input Parameters:
+ * nand - Pointer to a struct nand_dev_s instance.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_NAND_BLOCKCHECK
+static int nand_devscan(FAR struct nand_dev_s *nand)
+{
+ FAR const struct nand_raw_s *raw;
+ FAR const struct nand_model_s *model;
+ off_t numBlocks;
+ off_t block;
+ int ret;
+
+ DEBUGASSERT(nand && nand->raw);
+
+ /* Retrieve model information */
+
+ raw = nand->raw;
+ model = &raw->model;
+
+ numBlocks = nandmodel_getdevblocksize(model);
+
+ /* Initialize block statuses */
+
+ fvdbg("Retrieving bad block information ...\n");
+
+ /* Retrieve block status from their first page spare area */
+
+ for (block = 0; block < numBlocks; block++)
+ {
+ /* Read spare of first page */
+
+ ret = nand_checkblock(nand, block);
+ if (ret != GOODBLOCK)
+ {
+ if (ret == BADBLOCK)
+ {
+ fvdbg("Block %u is bad\n", (unsigned int)block);
+ }
+ else
+ {
+ fdbg("ERROR: Cannot retrieve info from block %u: %d\n",
+ (unsigned int)block, ret);
+ }
+ }
+ }
+
+ return OK;
+}
+#endif /* CONFIG_MTD_NAND_BLOCKCHECK */
+
+/****************************************************************************
* Name: nand_erase
*
* Description:
@@ -105,7 +248,7 @@ static int nand_ioctl(struct mtd_dev_s *dev, int cmd,
static int nand_erase(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks)
{
- struct nand_raw_s *priv = (struct nand_raw_s *)dev;
+ struct nand_raw_s *nand = (struct nand_raw_s *)dev;
/* The interface definition assumes that all erase blocks are the same size.
* If that is not true for this particular device, then transform the
@@ -129,7 +272,7 @@ static int nand_erase(struct mtd_dev_s *dev, off_t startblock,
static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, uint8_t *buf)
{
- struct nand_raw_s *priv = (struct nand_raw_s *)dev;
+ struct nand_raw_s *nand = (struct nand_raw_s *)dev;
/* The interface definition assumes that all read/write blocks are the same size.
* If that is not true for this particular device, then transform the
@@ -155,7 +298,7 @@ static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startblock,
static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, const uint8_t *buf)
{
- struct nand_raw_s *priv = (struct nand_raw_s *)dev;
+ struct nand_raw_s *nand = (struct nand_raw_s *)dev;
/* The interface definition assumes that all read/write blocks are the same size.
* If that is not true for this particular device, then transform the
@@ -176,7 +319,7 @@ static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startblock,
static int nand_ioctl(struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
- struct nand_raw_s *priv = (struct nand_raw_s *)dev;
+ struct nand_raw_s *nand = (struct nand_raw_s *)dev;
int ret = -EINVAL; /* Assume good command with bad parameters */
switch (cmd)
@@ -246,7 +389,7 @@ static int nand_ioctl(struct mtd_dev_s *dev, int cmd, unsigned long arg)
FAR struct mtd_dev_s *nand_initialize(FAR struct nand_raw_s *raw)
{
- FAR struct nand_dev_s *priv;
+ FAR struct nand_dev_s *nand;
struct onfi_pgparam_s onfi;
int ret;
@@ -338,8 +481,8 @@ FAR struct mtd_dev_s *nand_initialize(FAR struct nand_raw_s *raw)
/* Allocate an NAND MTD device structure */
- priv = (FAR struct nand_dev_s *)kzalloc(sizeof(struct nand_dev_s));
- if (!priv)
+ nand = (FAR struct nand_dev_s *)kzalloc(sizeof(struct nand_dev_s));
+ if (!nand)
{
fdbg("ERROR: Failed to allocate the NAND MTD device structure\n");
return NULL;
@@ -347,15 +490,23 @@ FAR struct mtd_dev_s *nand_initialize(FAR struct nand_raw_s *raw)
/* Initialize the NAND MTD device structure */
- priv->mtd.erase = nand_erase;
- priv->mtd.bread = nand_bread;
- priv->mtd.bwrite = nand_bwrite;
- priv->mtd.ioctl = nand_ioctl;
- priv->raw = raw;
+ nand->mtd.erase = nand_erase;
+ nand->mtd.bread = nand_bread;
+ nand->mtd.bwrite = nand_bwrite;
+ nand->mtd.ioctl = nand_ioctl;
+ nand->raw = raw;
- #warning Missing logic
+ /* Scan the device for bad blocks */
+
+ ret = nand_devscan(nand);
+ if (ret < 0)
+ {
+ fdbg("ERROR: nandspare_intialize failed\n", ret);
+ kfree(nand);
+ return NULL;
+ }
/* Return the implementation-specific state structure as the MTD device */
- return OK;
+ return &nand->mtd;
}
diff --git a/nuttx/drivers/mtd/mtd_rawnand.c b/nuttx/drivers/mtd/mtd_nandraw.c
index 92e4d82b6..012e470cc 100755
--- a/nuttx/drivers/mtd/mtd_rawnand.c
+++ b/nuttx/drivers/mtd/mtd_nandraw.c
@@ -1,5 +1,5 @@
/****************************************************************************
- * drivers/mtd/mtd_rawnand.c
+ * drivers/mtd/mtd_nandraw.c
*
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
diff --git a/nuttx/drivers/mtd/mtd_nandscheme.c b/nuttx/drivers/mtd/mtd_nandscheme.c
new file mode 100644
index 000000000..13df5d584
--- /dev/null
+++ b/nuttx/drivers/mtd/mtd_nandscheme.c
@@ -0,0 +1,384 @@
+/****************************************************************************
+ * include/nuttx/mtd/nand_scheme.c
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * This logic was based largely on Atmel sample code with modifications for
+ * better integration with NuttX. The Atmel sample code has a BSD
+ * compatibile license that requires this copyright notice:
+ *
+ * Copyright (c) 2012, Atmel Corporation
+ *
+ * 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 names NuttX nor Atmel nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/nand_config.h>
+
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/mtd/nand_scheme.h>
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Spare area placement scheme for 256 byte pages */
+
+const struct nand_scheme_s g_nand_sparescheme256 =
+{
+ /* Bad block marker is at position #5 */
+
+ 5,
+
+ /* 3 ecc bytes */
+
+ 3,
+
+ /* 4 extra bytes */
+
+ 4,
+
+ /* Ecc bytes positions */
+
+ {0, 1, 2},
+
+ /* Extra bytes positions */
+
+ {3, 4, 6, 7}
+};
+
+/* Spare area placement scheme for 512 byte pages */
+
+const struct nand_scheme_s g_nand_sparescheme512 =
+{
+ /* Bad block marker is at position #5 */
+
+ 5,
+
+ /* 6 ecc bytes */
+
+ 6,
+
+ /* 8 extra bytes */
+
+ 8,
+
+ /* Ecc bytes positions */
+
+ {0, 1, 2, 3, 6, 7},
+
+ /* Extra bytes positions */
+
+ {8, 9, 10, 11, 12, 13, 14, 15}
+};
+
+/* Spare area placement scheme for 2048 byte pages */
+
+const struct nand_scheme_s g_nand_sparescheme2048 =
+{
+ /* Bad block marker is at position #0 */
+
+ 0,
+
+ /* 24 ecc bytes */
+
+ 24,
+
+ /* 38 extra bytes */
+
+ 38,
+
+ /* Ecc bytes positions */
+
+ {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63},
+
+ /* Extra bytes positions */
+
+ { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39}
+};
+
+/* Spare area placement scheme for 4096 byte pages. */
+
+const struct nand_scheme_s g_nand_sparescheme4096 =
+{
+ /* Bad block marker is at position #0 */
+
+ 0,
+
+ /* 48 ecc bytes */
+
+ 48,
+
+ /* 78 extra bytes */
+
+ 78,
+
+ /* Ecc bytes positions */
+
+ { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 127},
+
+ /* Extra bytes positions */
+
+ { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79}
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nandscheme_readbadblockmarker
+ *
+ * Description:
+ * Reads the bad block marker inside a spare area buffer using the provided
+ * scheme.
+ *
+ * Input Parameters:
+ * scheme Pointer to a nand_scheme_s instance.
+ * spare Spare area buffer.
+ * marker Pointer to the variable to store the bad block marker.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+void nandscheme_readbadblockmarker(FAR const struct nand_scheme_s *scheme,
+ FAR const uint8_t *spare,
+ FAR uint8_t *marker)
+{
+ *marker = spare[scheme->bbpos];
+}
+
+/****************************************************************************
+ * Name: nandscheme_readbadblockmarker
+ *
+ * Description:
+ * Modifies the bad block marker inside a spare area, using the given
+ * scheme.
+ *
+ * Input Parameters:
+ * scheme Pointer to a nand_scheme_s instance.
+ * spare Spare area buffer.
+ * marker Bad block marker to write.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+void nandscheme_writebadblockmarker(FAR const struct nand_scheme_s *scheme,
+ FAR uint8_t *spare, uint8_t marker)
+{
+ spare[scheme->bbpos] = marker;
+}
+
+/****************************************************************************
+ * Name: nandscheme_readecc
+ *
+ * Description:
+ * Reads ECC information from a spare area using the provided scheme.
+ *
+ * Input Parameters:
+ * scheme Pointer to a nand_scheme_s instance.
+ * spare Spare area buffer.
+ * ecc ECC buffer.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+void nandscheme_readecc(FAR const struct nand_scheme_s *scheme,
+ FAR const uint8_t *spare, FAR uint8_t *ecc)
+{
+ int i;
+
+ for (i = 0; i < scheme->eccsize; i++)
+ {
+ ecc[i] = spare[scheme->eccbytepos[i]];
+ }
+}
+
+/****************************************************************************
+ * Name: nandscheme_writeecc
+ *
+ * Description:
+ * Writes ECC information in a spare area, using a particular scheme.
+ *
+ * Input Parameters:
+ * scheme Pointer to a nand_scheme_s instance.
+ * spare Spare area buffer.
+ * ecc ECC buffer.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+void nandscheme_writeecc(FAR const struct nand_scheme_s *scheme,
+ FAR uint8_t *spare, FAR const uint8_t *ecc)
+{
+ int i;
+
+ for (i = 0; i < scheme->eccsize; i++)
+ {
+ spare[scheme->eccbytepos[i]] = ecc[i];
+ }
+}
+
+/****************************************************************************
+ * Name: nandscheme_readextra
+ *
+ * Description:
+ * Reads extra bytes of information from a spare area, using the provided
+ * scheme.
+ *
+ * Input Parameters:
+ * scheme Pointer to a nand_scheme_s instance.
+ * spare Spare area buffer.
+ * extra Extra bytes buffer.
+ * size Number of extra bytes to read.
+ * offset Index where to read the first extra byte.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+void nandscheme_readextra(FAR const struct nand_scheme_s *scheme,
+ FAR const uint8_t *spare, FAR void *extra,
+ unsigned int size, unsigned int offset)
+{
+ DEBUGASSERT((size + offset) < scheme->nxbytes);
+
+ int i;
+
+ for (i = 0; i < size; i++)
+ {
+ ((uint8_t *)extra)[i] = spare[scheme->xbytepos[i+offset]];
+ }
+}
+
+/****************************************************************************
+ * Name: nandscheme_readextra
+ *
+ * Description:
+ * Write extra bytes of information inside a spare area, using the provided
+ * scheme.
+ *
+ * Input Parameters:
+ * scheme Pointer to a nand_scheme_s instance.
+ * spare Spare area buffer.
+ * extra Extra bytes buffer.
+ * size Number of extra bytes to write.
+ * offset Index where to write the first extra byte.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+void nandscheme_writeextra(FAR const struct nand_scheme_s *scheme,
+ FAR uint8_t *spare, FAR const void *extra,
+ unsigned int size, unsigned int offset)
+{
+ DEBUGASSERT((size + offset) < scheme->nxbytes);
+
+ uint32_t i;
+ for (i = 0; i < size; i++) {
+
+ spare[scheme->xbytepos[i+offset]] = ((uint8_t *) extra)[i];
+ }
+}
+
+/****************************************************************************
+ * Name: nandscheme_readextra
+ *
+ * Description:
+ * Build a scheme instance for 4096 page size nand flash
+ *
+ * Input Parameters:
+ * scheme Pointer to a nand_scheme_s instance.
+ * spareSize Size of spare area.
+ * offset Index where to write the first extra byte.
+ * size Number of extra bytes to write.
+ * offset Index where to write the first extra byte.
+ *
+ * Returned Values:
+ * OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int nandscheme_build4086(FAR struct nand_scheme_s *scheme,
+ unsigned int spareSize, unsigned int eccOffset)
+{
+ uint8_t eccsize = g_nand_sparescheme4096.eccsize;
+ int i;
+
+ if ((eccOffset + eccsize) > spareSize)
+ {
+ return -E2BIG;
+ }
+
+ scheme->bbpos = g_nand_sparescheme4096.bbpos;
+ scheme->eccsize = eccsize;
+
+ for (i = 0; i < eccsize; i++)
+ {
+ scheme->eccbytepos[i] = eccOffset + i;
+ }
+
+ scheme->nxbytes = spareSize - eccsize - 2;
+
+ for (i = 0; i < scheme->nxbytes; i++)
+ {
+ scheme->xbytepos[i] = 2 + i;
+ }
+
+ return OK;
+};