456 lines
13 KiB
C
456 lines
13 KiB
C
/****************************************************************************
|
|
* drivers/mtd/hamming.c
|
|
*
|
|
* Copyright (c) 2011, 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 <assert.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/mtd/hamming.h>
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: hamming_bitsinbyte
|
|
*
|
|
* Description:
|
|
* Counts the number of bits set to '1' in the given byte.
|
|
*
|
|
* Input Parameters:
|
|
* bytes - The byte to use.
|
|
*
|
|
* Returned Value:
|
|
* Returns the number of bits set to '1' in the given byte.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static unsigned int hamming_bitsinbyte(uint8_t byte)
|
|
{
|
|
unsigned int count = 0;
|
|
|
|
while (byte != 0)
|
|
{
|
|
if ((byte & 1) != 0)
|
|
{
|
|
count++;
|
|
}
|
|
|
|
byte >>= 1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hamming_bitsincode256
|
|
*
|
|
* Description:
|
|
* Counts the number of bits set to '1' in the given hamming code.
|
|
*
|
|
* Input Parameters:
|
|
* code - Hamming code
|
|
*
|
|
* Returned Value:
|
|
* Returns the number of bits set to '1' in the given hamming code.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint8_t hamming_bitsincode256(FAR uint8_t *code)
|
|
{
|
|
return hamming_bitsinbyte(code[0]) +
|
|
hamming_bitsinbyte(code[1]) +
|
|
hamming_bitsinbyte(code[2]);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hamming_compute256
|
|
*
|
|
* Description:
|
|
* Calculates the 22-bit hamming code for a 256-bytes block of data.
|
|
*
|
|
* Input Parameters:
|
|
* data - Data buffer to calculate code
|
|
* code - Pointer to a buffer where the code should be stored
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void hamming_compute256(FAR const uint8_t *data, FAR uint8_t *code)
|
|
{
|
|
uint8_t colsum = 0;
|
|
uint8_t evenline = 0;
|
|
uint8_t oddline = 0;
|
|
uint8_t evencol = 0;
|
|
uint8_t oddcol = 0;
|
|
int i;
|
|
|
|
/* Xor all bytes together to get the column sum;
|
|
* At the same time, calculate the even and odd line codes
|
|
*/
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
colsum ^= data[i];
|
|
|
|
/* If the xor sum of the byte is 0, then this byte has no incidence on
|
|
* the computed code; so check if the sum is 1.
|
|
*/
|
|
|
|
if ((hamming_bitsinbyte(data[i]) & 1) == 1)
|
|
{
|
|
/* Parity groups are formed by forcing a particular index bit to 0
|
|
* (even) or 1 (odd).
|
|
* Example on one byte:
|
|
*
|
|
* bits (dec) 7 6 5 4 3 2 1 0
|
|
* (bin) 111 110 101 100 011 010 001 000
|
|
* '---'---'---'----------.
|
|
* |
|
|
* groups P4' ooooooooooooooo eeeeeeeeeeeeeee P4 |
|
|
* P2' ooooooo eeeeeee ooooooo eeeeeee P2 |
|
|
* P1' ooo eee ooo eee ooo eee ooo eee P1 |
|
|
* |
|
|
* We can see that: |
|
|
* - P4 -> bit 2 of index is 0 --------------------'
|
|
* - P4' -> bit 2 of index is 1.
|
|
* - P2 -> bit 1 of index if 0.
|
|
* - etc...
|
|
* We deduce that a bit position has an impact on all even Px if
|
|
* the log2(x)nth bit of its index is 0
|
|
* ex: log2(4) = 2, bit2 of the index must be 0 (-> 0 1 2 3)
|
|
* and on all odd Px' if the log2(x)nth bit of its index is 1
|
|
* ex: log2(2) = 1, bit1 of the index must be 1 (-> 0 1 4 5)
|
|
*
|
|
* As such, we calculate all the possible Px and Px' values at the
|
|
* same time in two variables, evenline and oddline, such as
|
|
* evenline bits: P128 P64 P32 P16 P8 P4 P2 P1
|
|
* oddline bits: P128' P64' P32' P16' P8' P4' P2' P1'
|
|
*/
|
|
|
|
evenline ^= (255 - i);
|
|
oddline ^= i;
|
|
}
|
|
}
|
|
|
|
/* At this point, we have the line parities, and the column sum. First, We
|
|
* must calculate the parity group values on the column sum.
|
|
*/
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (colsum & 1)
|
|
{
|
|
evencol ^= (7 - i);
|
|
oddcol ^= i;
|
|
}
|
|
|
|
colsum >>= 1;
|
|
}
|
|
|
|
/* Now, we must interleave the parity values,
|
|
* to obtain the following layout:
|
|
* Code[0] = Line1
|
|
* Code[1] = Line2
|
|
* Code[2] = Column
|
|
* Line = Px' Px P(x-1)- P(x-1) ...
|
|
* Column = P4' P4 P2' P2 P1' P1 PadBit PadBit
|
|
*/
|
|
|
|
code[0] = 0;
|
|
code[1] = 0;
|
|
code[2] = 0;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
code[0] <<= 2;
|
|
code[1] <<= 2;
|
|
code[2] <<= 2;
|
|
|
|
/* Line 1 */
|
|
|
|
if ((oddline & 0x80) != 0)
|
|
{
|
|
code[0] |= 2;
|
|
}
|
|
|
|
if ((evenline & 0x80) != 0)
|
|
{
|
|
code[0] |= 1;
|
|
}
|
|
|
|
/* Line 2 */
|
|
|
|
if ((oddline & 0x08) != 0)
|
|
{
|
|
code[1] |= 2;
|
|
}
|
|
|
|
if ((evenline & 0x08) != 0)
|
|
{
|
|
code[1] |= 1;
|
|
}
|
|
|
|
/* Column */
|
|
|
|
if ((oddcol & 0x04) != 0)
|
|
{
|
|
code[2] |= 2;
|
|
}
|
|
|
|
if ((evencol & 0x04) != 0)
|
|
{
|
|
code[2] |= 1;
|
|
}
|
|
|
|
oddline <<= 1;
|
|
evenline <<= 1;
|
|
oddcol <<= 1;
|
|
evencol <<= 1;
|
|
}
|
|
|
|
/* Invert codes (linux compatibility) */
|
|
|
|
code[0] = (~(uint32_t)code[0]);
|
|
code[1] = (~(uint32_t)code[1]);
|
|
code[2] = (~(uint32_t)code[2]);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hamming_verify256
|
|
*
|
|
* Description:
|
|
* Verifies and corrects a 256-bytes block of data using the given 22-bits
|
|
* hamming code.
|
|
*
|
|
* Input Parameters:
|
|
* data - Data buffer to check
|
|
* original - Hamming code to use for verifying the data
|
|
*
|
|
* Returned Value:
|
|
* Zero on success, otherwise returns a HAMMING_ERROR_ code.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int hamming_verify256(FAR uint8_t *data, FAR const uint8_t *original)
|
|
{
|
|
/* Calculate new code */
|
|
|
|
uint8_t computed[3];
|
|
uint8_t correction[3];
|
|
|
|
hamming_compute256(data, computed);
|
|
|
|
/* Xor both codes together */
|
|
|
|
correction[0] = computed[0] ^ original[0];
|
|
correction[1] = computed[1] ^ original[1];
|
|
correction[2] = computed[2] ^ original[2];
|
|
|
|
/* If all bytes are 0, there is no error */
|
|
|
|
if ((correction[0] == 0) && (correction[1] == 0) && (correction[2] == 0))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* There are bit errors */
|
|
|
|
finfo("Read: %02x %02x %02x\n",
|
|
original[0], original[1], original[2]);
|
|
finfo("Computed: %02x %02x %02x\n",
|
|
computed[0], computed[1], computed[2]);
|
|
finfo("Correction: %02x %02x %02x\n",
|
|
correction[0], correction[1], correction[2]);
|
|
|
|
/* If there is a single bit error, there are 11 bits set to 1 */
|
|
|
|
if (hamming_bitsincode256(correction) == 11)
|
|
{
|
|
uint8_t byte;
|
|
uint8_t bit;
|
|
|
|
/* Get byte and bit indexes */
|
|
|
|
byte = correction[0] & 0x80;
|
|
byte |= (correction[0] << 1) & 0x40;
|
|
byte |= (correction[0] << 2) & 0x20;
|
|
byte |= (correction[0] << 3) & 0x10;
|
|
|
|
byte |= (correction[1] >> 4) & 0x08;
|
|
byte |= (correction[1] >> 3) & 0x04;
|
|
byte |= (correction[1] >> 2) & 0x02;
|
|
byte |= (correction[1] >> 1) & 0x01;
|
|
|
|
bit = (correction[2] >> 5) & 0x04;
|
|
bit |= (correction[2] >> 4) & 0x02;
|
|
bit |= (correction[2] >> 3) & 0x01;
|
|
|
|
/* Correct bit */
|
|
|
|
finfo("Correcting byte %d at bit %d\n", byte, bit);
|
|
data[byte] ^= (1 << bit);
|
|
|
|
return HAMMING_ERROR_SINGLEBIT;
|
|
}
|
|
|
|
/* Check if ECC has been corrupted */
|
|
|
|
if (hamming_bitsincode256(correction) == 1)
|
|
{
|
|
ferr("ERROR: ECC has been correupted\n");
|
|
return HAMMING_ERROR_ECC;
|
|
}
|
|
|
|
/* Otherwise, there are multiple bit errors */
|
|
|
|
else
|
|
{
|
|
ferr("ERROR: Multiple bit errors\n");
|
|
return HAMMING_ERROR_MULTIPLEBITS;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: hamming_compute256x
|
|
*
|
|
* Description:
|
|
* Computes 3-bytes hamming codes for a data block whose size is multiple
|
|
* of 256 bytes. Each 256 bytes block gets its own code.
|
|
*
|
|
* Input Parameters:
|
|
* data - Data to compute code for
|
|
* size - Data size in bytes
|
|
* code - Codes buffer
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void hamming_compute256x(FAR const uint8_t *data, size_t size,
|
|
FAR uint8_t *code)
|
|
{
|
|
ssize_t remaining = (ssize_t)size;
|
|
DEBUGASSERT((size & 0xff) == 0);
|
|
|
|
/* Loop, computing the Hamming code on each 256 byte chunk of data */
|
|
|
|
while (remaining > 0)
|
|
{
|
|
hamming_compute256(data, code);
|
|
|
|
/* Setup for the next 256 byte chunk */
|
|
|
|
data += 256;
|
|
code += 3;
|
|
remaining -= 256;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hamming_verify256x
|
|
*
|
|
* Description:
|
|
* Verifies 3-bytes hamming codes for a data block whose size is multiple
|
|
* of 256 bytes. Each 256-bytes block is verified with its own code.
|
|
*
|
|
* Input Parameters:
|
|
* data - Data buffer to verify
|
|
* size - Size of the data in bytes
|
|
* code - Original codes
|
|
*
|
|
* Returned Value:
|
|
* Return 0 if the data is correct, HAMMING_ERROR_SINGLEBIT if one or more
|
|
* block(s) have had a single bit corrected, or either HAMMING_ERROR_ECC
|
|
* or HAMMING_ERROR_MULTIPLEBITS.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int hamming_verify256x(FAR uint8_t *data,
|
|
size_t size,
|
|
FAR const uint8_t *code)
|
|
{
|
|
ssize_t remaining = (ssize_t)size;
|
|
int result = HAMMING_SUCCESS;
|
|
int ret;
|
|
|
|
DEBUGASSERT((size & 0xff) == 0);
|
|
|
|
/* Loop, verifying each 256 byte chunk of data */
|
|
|
|
while (remaining > 0)
|
|
{
|
|
result = hamming_verify256(data, code);
|
|
if (result != HAMMING_SUCCESS)
|
|
{
|
|
/* Check for the case of a single bit error that was corrected */
|
|
|
|
if (result == HAMMING_ERROR_SINGLEBIT)
|
|
{
|
|
/* Report the error, but continue verifying */
|
|
|
|
ret = HAMMING_ERROR_SINGLEBIT;
|
|
}
|
|
else
|
|
{
|
|
/* A bad error occurred, abort the verification and return the
|
|
* error code
|
|
*/
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/* Setup for the next 256 byte chunk */
|
|
|
|
data += 256;
|
|
code += 3;
|
|
remaining -= 256;
|
|
}
|
|
|
|
return ret;
|
|
}
|