More NAND stuff
This commit is contained in:
parent
0e7a8668a5
commit
d54832a942
@ -6079,4 +6079,5 @@
|
||||
(2013-11-16).
|
||||
* drivers/mtd/mtd_rawnand.c and include/nuttx/mtd/nand_raw.h: More
|
||||
NAND support (2013-11-17).
|
||||
* drivers/mtd/mtd_nandscheme.c: More NAND support (2013-11-17).
|
||||
|
||||
|
@ -76,8 +76,7 @@
|
||||
|
||||
/* This type represents the state of the raw NAND MTD device. The struct
|
||||
* nand_raw_s must appear at the beginning of the definition so that you can
|
||||
* freely cast between pointers to struct mtd_dev_s, struct nand_raw_s, and
|
||||
* struct sam_rawnand_s.
|
||||
* freely cast between pointers to struct nand_raw_s and struct sam_rawnand_s.
|
||||
*/
|
||||
|
||||
struct sam_rawnand_s
|
||||
@ -92,14 +91,11 @@ struct sam_rawnand_s
|
||||
|
||||
/* MTD driver methods */
|
||||
|
||||
static int nand_erase(struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks);
|
||||
static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks, uint8_t *buf);
|
||||
static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks, const uint8_t *buf);
|
||||
static int nand_ioctl(struct mtd_dev_s *dev, int cmd,
|
||||
unsigned long arg);
|
||||
static int nand_eraseblock(struct nand_raw_s *raw, off_t block);
|
||||
static int nand_readpage(struct nand_raw_s *raw, off_t block,
|
||||
unsigned int page, void *data, void *spare);
|
||||
static int nand_writepage(struct nand_raw_s *raw, off_t block,
|
||||
unsigned int page, const void *data, const void *spare);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
@ -126,129 +122,82 @@ static struct sam_rawnand_s g_cs3nand;
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nand_erase
|
||||
* Name: nand_eraseblock
|
||||
*
|
||||
* Description:
|
||||
* Erase several blocks, each of the size previously reported.
|
||||
* Erases the specified block of the device.
|
||||
*
|
||||
* Input parameters:
|
||||
* raw - Lower-half, raw NAND FLASH interface
|
||||
* block - Number of the physical block to erase.
|
||||
*
|
||||
* Returned value.
|
||||
* OK is returned in succes; a negated errno value is returned on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int nand_erase(struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks)
|
||||
static int nand_eraseblock(struct nand_raw_s *raw, off_t block)
|
||||
{
|
||||
struct sam_rawnand_s *priv = (struct sam_rawnand_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
|
||||
* start block and nblocks as necessary.
|
||||
*/
|
||||
struct sam_rawnand_s *priv = (struct sam_rawnand_s *)raw;
|
||||
DEBUGASSERT(raw);
|
||||
#warning Missing logic
|
||||
|
||||
/* Erase the specified blocks and return status (OK or a negated errno) */
|
||||
|
||||
return OK;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nand_bread
|
||||
* Name: nand_readpage
|
||||
*
|
||||
* Description:
|
||||
* Read the specified number of blocks into the user provided buffer.
|
||||
* Reads the data and/or the spare areas of a page of a NAND FLASH into the
|
||||
* provided buffers.
|
||||
*
|
||||
* Input parameters:
|
||||
* raw - Lower-half, raw NAND FLASH interface
|
||||
* block - Number of the block where the page to read resides.
|
||||
* page - Number of the page to read inside the given block.
|
||||
* data - Buffer where the data area will be stored.
|
||||
* spare - Buffer where the spare area will be stored.
|
||||
*
|
||||
* Returned value.
|
||||
* OK is returned in succes; a negated errno value is returned on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks, uint8_t *buf)
|
||||
static int nand_readpage(struct nand_raw_s *raw, off_t block,
|
||||
unsigned int page, void *data, void *spare)
|
||||
{
|
||||
struct sam_rawnand_s *priv = (struct sam_rawnand_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
|
||||
* start block and nblocks as necessary.
|
||||
*/
|
||||
|
||||
/* Read the specified blocks into the provided user buffer and return status
|
||||
* (The positive, number of blocks actually read or a negated errno).
|
||||
*/
|
||||
struct sam_rawnand_s *priv = (struct sam_rawnand_s *)raw;
|
||||
DEBUGASSERT(raw);
|
||||
#warning Missing logic
|
||||
|
||||
return 0;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nand_bwrite
|
||||
* Name: nand_writepage
|
||||
*
|
||||
* Description:
|
||||
* Write the specified number of blocks from the user provided buffer.
|
||||
* Writes the data and/or the spare area of a page on a NAND FLASH chip.
|
||||
*
|
||||
* Input parameters:
|
||||
* raw - Lower-half, raw NAND FLASH interface
|
||||
* block - Number of the block where the page to write resides.
|
||||
* page - Number of the page to write inside the given block.
|
||||
* data - Buffer containing the data to be writting
|
||||
* spare - Buffer conatining the spare data to be written.
|
||||
*
|
||||
* Returned value.
|
||||
* OK is returned in succes; a negated errno value is returned on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks, const uint8_t *buf)
|
||||
static int nand_writepage(struct nand_raw_s *raw, off_t block,
|
||||
unsigned int page, const void *data,
|
||||
const void *spare)
|
||||
{
|
||||
struct sam_rawnand_s *priv = (struct sam_rawnand_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
|
||||
* start block and nblocks as necessary.
|
||||
*/
|
||||
|
||||
/* Write the specified blocks from the provided user buffer and return status
|
||||
* (The positive, number of blocks actually written or a negated errno)
|
||||
*/
|
||||
struct sam_rawnand_s *priv = (struct sam_rawnand_s *)raw;
|
||||
DEBUGASSERT(raw);
|
||||
#warning Missing logic
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nand_ioctl
|
||||
****************************************************************************/
|
||||
|
||||
static int nand_ioctl(struct mtd_dev_s *dev, int cmd, unsigned long arg)
|
||||
{
|
||||
struct sam_rawnand_s *priv = (struct sam_rawnand_s *)dev;
|
||||
int ret = -EINVAL; /* Assume good command with bad parameters */
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case MTDIOC_GEOMETRY:
|
||||
{
|
||||
struct mtd_geometry_s *geo = (struct mtd_geometry_s *)arg;
|
||||
if (geo)
|
||||
{
|
||||
/* Populate the geometry structure with information needed to know
|
||||
* the capacity and how to access the device.
|
||||
*
|
||||
* NOTE: that the device is treated as though it where just an array
|
||||
* of fixed size blocks. That is most likely not true, but the client
|
||||
* will expect the device logic to do whatever is necessary to make it
|
||||
* appear so.
|
||||
*/
|
||||
|
||||
geo->blocksize = 512; /* Size of one read/write block */
|
||||
geo->erasesize = 4096; /* Size of one erase block */
|
||||
geo->neraseblocks = 1024; /* Number of erase blocks */
|
||||
ret = OK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MTDIOC_BULKERASE:
|
||||
{
|
||||
/* Erase the entire device */
|
||||
|
||||
ret = OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case MTDIOC_XIPBASE:
|
||||
default:
|
||||
ret = -ENOTTY; /* Bad command */
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -259,14 +208,10 @@ static int nand_ioctl(struct mtd_dev_s *dev, int cmd, unsigned long arg)
|
||||
* Name: sam_nand_initialize
|
||||
*
|
||||
* Description:
|
||||
* Create and initialize an NAND MTD device instance. MTD devices are
|
||||
* not registered in the file system, but are created as instances that can
|
||||
* be bound to other functions (such as a block or character driver front
|
||||
* end).
|
||||
*
|
||||
* This MTD devices implements a RAW NAND interface: No ECC or sparing is
|
||||
* Create and initialize an raw NAND device instance. This driver
|
||||
* implements the RAW NAND interface: No software ECC or sparing is
|
||||
* performed here. Those necessary NAND features are provided by common,
|
||||
* higher level MTD layers found in drivers/mtd.
|
||||
* higher level NAND MTD layers found in drivers/mtd.
|
||||
*
|
||||
* Input parameters:
|
||||
* cs - Chip select number (in the event that multiple NAND devices
|
||||
@ -366,13 +311,12 @@ struct mtd_dev_s *sam_nand_initialize(int cs)
|
||||
/* Initialize the device structure */
|
||||
|
||||
memset(priv, 0, sizeof(struct sam_rawnand_s));
|
||||
priv->raw.mtd.erase = nand_erase;
|
||||
priv->raw.mtd.bread = nand_bread;
|
||||
priv->raw.mtd.bwrite = nand_bwrite;
|
||||
priv->raw.mtd.ioctl = nand_ioctl;
|
||||
priv->raw.cmdaddr = cmdaddr;
|
||||
priv->raw.addraddr = addraddr;
|
||||
priv->raw.dataaddr = dataaddr;
|
||||
priv->raw.eraseblock = nand_eraseblock;
|
||||
priv->raw.readpage = nand_readpage;
|
||||
priv->raw.writepage = nand_writepage;
|
||||
priv->cs = cs;
|
||||
|
||||
/* Initialize the NAND hardware */
|
||||
|
@ -67,19 +67,15 @@ extern "C" {
|
||||
* Name: sam_nand_initialize
|
||||
*
|
||||
* Description:
|
||||
* Create and initialize a raw NAND MTD device instance. MTD devices are
|
||||
* not registered in the file system, but are created as instances that can
|
||||
* be bound to other functions (such as a block or character driver front
|
||||
* end).
|
||||
*
|
||||
* This MTD devices implements a RAW NAND interface: No ECC or sparing is
|
||||
* Create and initialize an raw NAND device instance. This driver
|
||||
* implements the RAW NAND interface: No software ECC or sparing is
|
||||
* performed here. Those necessary NAND features are provided by common,
|
||||
* higher level MTD layers found in drivers/mtd.
|
||||
*
|
||||
* higher level NAND MTD layers found in drivers/mtd.
|
||||
*
|
||||
* Input parameters:
|
||||
* cs - Chip select number (in the event that multiple NAND devices
|
||||
* are connected on-board).
|
||||
*
|
||||
*
|
||||
* Returned value.
|
||||
* On success a non-NULL pointer to an MTD device structure is returned;
|
||||
* NULL is returned on a failure.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
||||
@ -94,6 +107,136 @@ static int nand_ioctl(struct mtd_dev_s *dev, int cmd,
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* 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
|
||||
*
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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>
|
384
drivers/mtd/mtd_nandscheme.c
Normal file
384
drivers/mtd/mtd_nandscheme.c
Normal file
@ -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;
|
||||
};
|
@ -100,26 +100,92 @@
|
||||
#define READ_DATA16(raw) \
|
||||
(*((volatile uint16_t *)raw->dataaddr))
|
||||
|
||||
/* struct nand_raw_s operations */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: NAND_ERASEBLOCK
|
||||
*
|
||||
* Description:
|
||||
* Erases the specified block of the device.
|
||||
*
|
||||
* Input parameters:
|
||||
* raw - Lower-half, raw NAND FLASH interface
|
||||
* block - Number of the physical block to erase.
|
||||
*
|
||||
* Returned value.
|
||||
* OK is returned in succes; a negated errno value is returned on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#define NAND_ERASEBLOCK(r,b) ((r)->eraseblock(r,b))
|
||||
|
||||
/****************************************************************************
|
||||
* Name: NAND_READPAGE
|
||||
*
|
||||
* Description:
|
||||
* Reads the data and/or the spare areas of a page of a NAND FLASH into the
|
||||
* provided buffers.
|
||||
*
|
||||
* Input parameters:
|
||||
* raw - Lower-half, raw NAND FLASH interface
|
||||
* block - Number of the block where the page to read resides.
|
||||
* page - Number of the page to read inside the given block.
|
||||
* data - Buffer where the data area will be stored.
|
||||
* spare - Buffer where the spare area will be stored.
|
||||
*
|
||||
* Returned value.
|
||||
* OK is returned in succes; a negated errno value is returned on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#define NAND_READPAGE(r,b,p,d,s) ((r)->readpage(r,b,p,d,s))
|
||||
|
||||
/****************************************************************************
|
||||
* Name: NAND_WRITEPAGE
|
||||
*
|
||||
* Description:
|
||||
* Writes the data and/or the spare area of a page on a NAND FLASH chip.
|
||||
*
|
||||
* Input parameters:
|
||||
* raw - Lower-half, raw NAND FLASH interface
|
||||
* block - Number of the block where the page to write resides.
|
||||
* page - Number of the page to write inside the given block.
|
||||
* data - Buffer containing the data to be writting
|
||||
* spare - Buffer conatining the spare data to be written.
|
||||
*
|
||||
* Returned value.
|
||||
* OK is returned in succes; a negated errno value is returned on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#define NAND_WRITEPAGE(r,b,p,d,s) ((r)->writepage(r,b,p,d,s))
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
/* This type represents the visible portion of the lower-half, raw NAND MTD
|
||||
* device. Rules:
|
||||
*
|
||||
* 1. The struct mtd_dev_s must appear at the beginning of the definition so
|
||||
* that you can freely cast between pointers to struct mtd_dev_s and struct
|
||||
* nand_raw_s.
|
||||
* 2. The lower-half driver may freely append additional information after
|
||||
* this required header information.
|
||||
* device. The lower-half driver may freely append additional information
|
||||
* after this required header information.
|
||||
*/
|
||||
|
||||
struct nand_raw_s
|
||||
{
|
||||
struct mtd_dev_s mtd; /* Externally visible part of the driver */
|
||||
/* NAND data */
|
||||
|
||||
struct nand_model_s model; /* The NAND model storage */
|
||||
uintptr_t cmdaddr; /* NAND command address base */
|
||||
uintptr_t addraddr; /* NAND address address base */
|
||||
uintptr_t dataaddr; /* NAND data address */
|
||||
|
||||
/* NAND operations */
|
||||
|
||||
CODE int (*eraseblock)(FAR struct nand_raw_s *raw, off_t block);
|
||||
CODE int (*readpage)(FAR struct nand_raw_s *raw, off_t block,
|
||||
unsigned int page, FAR void *data, FAR void *spare);
|
||||
CODE int (*writepage)(FAR struct nand_raw_s *raw, off_t block,
|
||||
unsigned int page, FAR const void *data,
|
||||
FAR const void *spare);
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -97,6 +97,152 @@ EXTERN const struct nand_scheme_s g_nand_sparescheme4096;
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* 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);
|
||||
|
||||
/****************************************************************************
|
||||
* 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);
|
||||
|
||||
/****************************************************************************
|
||||
* 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);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nandschem_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);
|
||||
|
||||
/****************************************************************************
|
||||
* 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);
|
||||
|
||||
/****************************************************************************
|
||||
* 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);
|
||||
|
||||
/****************************************************************************
|
||||
* 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);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user