/**************************************************************************** * drivers/mtd/hamming.c * * Copyright (C) 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * This logic was taken directly from Atmel sample code with only * modifications for better integration with NuttX. The Atmel sample * code has a BSD compatible license that requires this copyright notice: * * 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 #include #include #include #include #include /**************************************************************************** * 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 Values: * 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 Values: * 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 Values: * 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 caculate 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 Values: * 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 Values: * None * ****************************************************************************/ void hamming_compute256x(FAR const uint8_t *data, size_t size, 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 Values: * 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; }