220 lines
9.2 KiB
Plaintext
220 lines
9.2 KiB
Plaintext
/*
|
|
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
|
|
*
|
|
* Copyright (c) 2017, Hard Consulting Corporation.
|
|
*
|
|
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
|
|
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version. See the LICENSE file at the top of the
|
|
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. The Djelic BCH code
|
|
* under djelic/ and the c++/ezpwd/bch_base wrapper is redistributed under the terms of the GPLv2+,
|
|
* regardless of the overall licensing terms.
|
|
*
|
|
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
* the GNU General Public License for more details.
|
|
*/
|
|
#ifndef _EZPWD_BCH_BASE
|
|
#define _EZPWD_BCH_BASE
|
|
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <vector>
|
|
|
|
//
|
|
// Presently, we simply import the Linux Kernel's "C" BCH API directly into the ezpwd:: namespace In
|
|
// order to compile, you must (at least) run "make djelic" in the base directory of the
|
|
// https://github.com/pjkundert/bch.git repo.
|
|
//
|
|
namespace ezpwd {
|
|
/**
|
|
* struct bch_control - BCH control structure
|
|
* @m: Galois field order
|
|
* @n: maximum codeword size in bits (= 2^m-1)
|
|
* @t: error correction capability in bits
|
|
* @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t)
|
|
* @ecc_bytes: ecc max size (m*t bits) in bytes
|
|
* @a_pow_tab: Galois field GF(2^m) exponentiation lookup table
|
|
* @a_log_tab: Galois field GF(2^m) log lookup table
|
|
* @mod8_tab: remainder generator polynomial lookup tables
|
|
* @ecc_buf: ecc parity words buffer
|
|
* @ecc_buf2: ecc parity words buffer
|
|
* @xi_tab: GF(2^m) base for solving degree 2 polynomial roots
|
|
* @syn: syndrome buffer
|
|
* @cache: log-based polynomial representation buffer
|
|
* @elp: error locator polynomial
|
|
* @poly_2t: temporary polynomials of degree 2t
|
|
*/
|
|
|
|
/**
|
|
* init_bch - initialize a BCH encoder/decoder
|
|
* @m: Galois field order, should be in the range 5-15
|
|
* @t: maximum error correction capability, in bits
|
|
* @prim_poly: user-provided primitive polynomial (or 0 to use default)
|
|
*
|
|
* Returns:
|
|
* a newly allocated BCH control structure if successful, NULL otherwise
|
|
*
|
|
* This initialization can take some time, as lookup tables are built for fast
|
|
* encoding/decoding; make sure not to call this function from a time critical
|
|
* path. Usually, init_bch() should be called on module/driver init and
|
|
* free_bch() should be called to release memory on exit.
|
|
*
|
|
* You may provide your own primitive polynomial of degree @m in argument
|
|
* @prim_poly, or let init_bch() use its default polynomial.
|
|
*
|
|
* Once init_bch() has successfully returned a pointer to a newly allocated
|
|
* BCH control structure, ecc length in bytes is given by member @ecc_bytes of
|
|
* the structure.
|
|
*/
|
|
|
|
/**
|
|
* encode_bch - calculate BCH ecc parity of data
|
|
* @bch: BCH control structure
|
|
* @data: data to encode
|
|
* @len: data length in bytes
|
|
* @ecc: ecc parity data, must be initialized by caller
|
|
*
|
|
* The @ecc parity array is used both as input and output parameter, in order to
|
|
* allow incremental computations. It should be of the size indicated by member
|
|
* @ecc_bytes of @bch, and should be initialized to 0 before the first call.
|
|
*
|
|
* The exact number of computed ecc parity bits is given by member @ecc_bits of
|
|
* @bch; it may be less than m*t for large values of t.
|
|
*/
|
|
|
|
/**
|
|
* decode_bch - decode received codeword and find bit error locations
|
|
* @bch: BCH control structure
|
|
* @data: received data, ignored if @calc_ecc is provided
|
|
* @len: data length in bytes, must always be provided
|
|
* @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc
|
|
* @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data
|
|
* @syn: hw computed syndrome data (if NULL, syndrome is calculated)
|
|
* @errloc: output array of error locations
|
|
*
|
|
* Returns:
|
|
* The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if
|
|
* invalid parameters were provided
|
|
*
|
|
* Depending on the available hw BCH support and the need to compute @calc_ecc
|
|
* separately (using encode_bch()), this function should be called with one of
|
|
* the following parameter configurations -
|
|
*
|
|
* by providing @data and @recv_ecc only:
|
|
* decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc)
|
|
*
|
|
* by providing @recv_ecc and @calc_ecc:
|
|
* decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc)
|
|
*
|
|
* by providing ecc = recv_ecc XOR calc_ecc:
|
|
* decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc)
|
|
*
|
|
* by providing syndrome results @syn:
|
|
* decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc)
|
|
*
|
|
* Once decode_bch() has successfully returned with a positive value, error
|
|
* locations returned in array @errloc should be interpreted as follows -
|
|
*
|
|
* if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for
|
|
* data correction)
|
|
*
|
|
* if (errloc[n] < 8*len), then n-th error is located in data and can be
|
|
* corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8);
|
|
*
|
|
* Note that this function does not perform any data correction by itself, it
|
|
* merely indicates error locations.
|
|
*/
|
|
|
|
/**
|
|
* init_bch - initialize a BCH encoder/decoder
|
|
* @m: Galois field order, should be in the range 5-15
|
|
* @t: maximum error correction capability, in bits
|
|
* @prim_poly: user-provided primitive polynomial (or 0 to use default)
|
|
*
|
|
* Returns:
|
|
* a newly allocated BCH control structure if successful, NULL otherwise
|
|
*
|
|
* This initialization can take some time, as lookup tables are built for fast
|
|
* encoding/decoding; make sure not to call this function from a time critical
|
|
* path. Usually, init_bch() should be called on module/driver init and
|
|
* free_bch() should be called to release memory on exit.
|
|
*
|
|
* You may provide your own primitive polynomial of degree @m in argument
|
|
* @prim_poly, or let init_bch() use its default polynomial.
|
|
*
|
|
* Once init_bch() has successfully returned a pointer to a newly allocated
|
|
* BCH control structure, ecc length in bytes is given by member @ecc_bytes of
|
|
* the structure.
|
|
*/
|
|
|
|
extern "C" {
|
|
#include "../../djelic_bch.h"
|
|
}
|
|
|
|
//
|
|
// correct_bch -- corrects data (but not parity!), as suggested by decode_bch, above
|
|
//
|
|
// A convenience interface that defaults all of the not strictly required parameters, and
|
|
// automatically corrects bit-errors in data *and* the supplied parity. Does not attempt to
|
|
// correct bit errors found in the parity data. If not supplied, 'errloc' is allocated
|
|
// internally; otherwise, it is assumed to be of at least size bch->t (the minimum error
|
|
// correction capacity of the BCH codec).
|
|
//
|
|
// However, beware -- at larger values of T, the actual correction capacity of the BCH codec
|
|
// could be greater than the requested T. Therefore, it is recommended that you always supply a
|
|
// larger than required errloc array; recommend T*2?
|
|
//
|
|
inline
|
|
int correct_bch(
|
|
struct bch_control *bch,
|
|
uint8_t *data,
|
|
unsigned int len,
|
|
uint8_t *recv_ecc,
|
|
const uint8_t *calc_ecc= 0,
|
|
const unsigned int *syn = 0,
|
|
unsigned int *errloc = 0 ) // must be sized at least bch->t; often, greater!
|
|
{
|
|
unsigned int _errloc[511]; // much larger than the correction capacity of largest supported BCH codec
|
|
if ( ! errloc )
|
|
errloc = _errloc;
|
|
int err = decode_bch( bch, data, len, recv_ecc, calc_ecc, syn, errloc );
|
|
if ( err > 0 ) {
|
|
// A +'ve number of bit-error correction locations were found
|
|
for ( int n = 0; n < err; ++n ) {
|
|
/**
|
|
* if (errloc[n] < 8*len), then n-th error is located in data and can be corrected
|
|
* with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8). If in the parity, it
|
|
* is assumed to be located at the end of the data, so offset by 'len' bytes.
|
|
*/
|
|
if ( errloc[n] < 8*len ) {
|
|
data[errloc[n] / 8] ^= 1 << ( errloc[n] % 8 );
|
|
} else if ( recv_ecc && errloc[n] < 8 * len + 8 * bch->ecc_bytes ) {
|
|
recv_ecc[errloc[n] / 8 - len]
|
|
^= 1 << ( errloc[n] % 8 );
|
|
}
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
//
|
|
// <ostream> << <ezpwd::bch_control> -- output codec in standard BCH( N, N-ECC, T ) form
|
|
//
|
|
inline
|
|
std::ostream &operator<<(
|
|
std::ostream &lhs,
|
|
const ezpwd::bch_control
|
|
&bch )
|
|
{
|
|
return lhs
|
|
<< "BCH( " << std::setw( 3 ) << bch.n
|
|
<< ", " << std::setw( 3 ) << bch.n - bch.ecc_bits
|
|
<< ", " << std::setw( 3 ) << bch.t
|
|
<< " )";
|
|
}
|
|
|
|
} // namespace ezpwd
|
|
|
|
#endif // _EZPWD_BCH_BASE
|