From 6d64a43f2b3ff7ca685fff64427c84b81e659afa Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 15:16:39 +0200 Subject: Initial working build --- .gitignore | 10 + Cargo.toml | 8 + bch.h | 79 ++++ build.rs | 18 + src/bch.c | 1384 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 7 + wrapper.h | 2 + 7 files changed, 1508 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 bch.h create mode 100644 build.rs create mode 100644 src/bch.c create mode 100644 src/lib.rs create mode 100644 wrapper.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b6b607 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/target +**/*.rs.bk + +#Added by cargo +# +#already existing elements are commented out + +#/target +#**/*.rs.bk +Cargo.lock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d60413f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "bchlib" +version = "0.1.0" +authors = ["Yuval Adam <_@yuv.al>"] +edition = "2018" + +[build-dependencies] +bindgen = "0.42.2" diff --git a/bch.h b/bch.h new file mode 100644 index 0000000..295b4ef --- /dev/null +++ b/bch.h @@ -0,0 +1,79 @@ +/* + * Generic binary BCH encoding/decoding library + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. +*/ +#ifndef _BCH_H +#define _BCH_H + +#include + +/** + * 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 + */ +struct bch_control { + unsigned int m; + unsigned int n; + unsigned int t; + unsigned int ecc_bits; + unsigned int ecc_bytes; +/* private: */ + uint16_t *a_pow_tab; + uint16_t *a_log_tab; + uint32_t *mod8_tab; + uint32_t *ecc_buf; + uint32_t *ecc_buf2; + unsigned int *xi_tab; + unsigned int *syn; + int *cache; + struct gf_poly *elp; + struct gf_poly *poly_2t[4]; +}; + +struct bch_control *init_bch(int m, int t, unsigned int prim_poly); + +void free_bch(struct bch_control *bch); + +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc); + +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc); + +#endif /* _BCH_H */ diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6c515a4 --- /dev/null +++ b/build.rs @@ -0,0 +1,18 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +fn main() { + println!("cargo:rustc-link-lib=bch"); + + let bindings = bindgen::Builder::default() + .header("wrapper.h") + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/src/bch.c b/src/bch.c new file mode 100644 index 0000000..5db6d3a --- /dev/null +++ b/src/bch.c @@ -0,0 +1,1384 @@ +/* + * Generic binary BCH encoding/decoding library + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. + * + * Call init_bch to get a pointer to a newly allocated bch_control structure for + * the given m (Galois field order), t (error correction capability) and + * (optional) primitive polynomial parameters. + * + * Call encode_bch to compute and store ecc parity bytes to a given buffer. + * Call decode_bch to detect and locate errors in received data. + * + * On systems supporting hw BCH features, intermediate results may be provided + * to decode_bch in order to skip certain steps. See decode_bch() documentation + * for details. + * + * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of + * parameters m and t; thus allowing extra compiler optimizations and providing + * better (up to 2x) encoding performance. Using this option makes sense when + * (m,t) are fixed and known in advance, e.g. when using BCH error correction + * on a particular NAND flash device. + * + * Algorithmic details: + * + * Encoding is performed by processing 32 input bits in parallel, using 4 + * remainder lookup tables. + * + * The final stage of decoding involves the following internal steps: + * a. Syndrome computation + * b. Error locator polynomial computation using Berlekamp-Massey algorithm + * c. Error locator root finding (by far the most expensive step) + * + * In this implementation, step c is not performed using the usual Chien search. + * Instead, an alternative approach described in [1] is used. It consists in + * factoring the error locator polynomial using the Berlekamp Trace algorithm + * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial + * solving techniques [2] are used. The resulting algorithm, called BTZ, yields + * much better performance than Chien search for usual (m,t) values (typically + * m >= 13, t < 32, see [1]). + * + * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields + * of characteristic 2, in: Western European Workshop on Research in Cryptology + * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. + * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over + * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_BCH_CONST_PARAMS) +#define GF_M(_p) (CONFIG_BCH_CONST_M) +#define GF_T(_p) (CONFIG_BCH_CONST_T) +#define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) +#define BCH_MAX_M (CONFIG_BCH_CONST_M) +#define BCH_MAX_T (CONFIG_BCH_CONST_T) +#else +#define GF_M(_p) ((_p)->m) +#define GF_T(_p) ((_p)->t) +#define GF_N(_p) ((_p)->n) +#define BCH_MAX_M 15 /* 2KB */ +#define BCH_MAX_T 64 /* 64 bit correction */ +#endif + +#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) +#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) + +#define BCH_ECC_MAX_WORDS DIV_ROUND_UP(BCH_MAX_M * BCH_MAX_T, 32) + +#ifndef dbg +#define dbg(_fmt, args...) do {} while (0) +#endif + +/* + * represent a polynomial over GF(2^m) + */ +struct gf_poly { + unsigned int deg; /* polynomial degree */ + unsigned int c[0]; /* polynomial terms */ +}; + +/* given its degree, compute a polynomial size in bytes */ +#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) + +/* polynomial of degree 1 */ +struct gf_poly_deg1 { + struct gf_poly poly; + unsigned int c[2]; +}; + +/* + * same as encode_bch(), but process input data one byte at a time + */ +static void encode_bch_unaligned(struct bch_control *bch, + const unsigned char *data, unsigned int len, + uint32_t *ecc) +{ + int i; + const uint32_t *p; + const int l = BCH_ECC_WORDS(bch)-1; + + while (len--) { + p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); + + for (i = 0; i < l; i++) + ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); + + ecc[l] = (ecc[l] << 8)^(*p); + } +} + +/* + * convert ecc bytes to aligned, zero-padded 32-bit ecc words + */ +static void load_ecc8(struct bch_control *bch, uint32_t *dst, + const uint8_t *src) +{ + uint8_t pad[4] = {0, 0, 0, 0}; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++, src += 4) + dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; + + memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); + dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; +} + +/* + * convert 32-bit ecc words to ecc bytes + */ +static void store_ecc8(struct bch_control *bch, uint8_t *dst, + const uint32_t *src) +{ + uint8_t pad[4]; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++) { + *dst++ = (src[i] >> 24); + *dst++ = (src[i] >> 16) & 0xff; + *dst++ = (src[i] >> 8) & 0xff; + *dst++ = (src[i] >> 0) & 0xff; + } + pad[0] = (src[nwords] >> 24); + pad[1] = (src[nwords] >> 16) & 0xff; + pad[2] = (src[nwords] >> 8) & 0xff; + pad[3] = (src[nwords] >> 0) & 0xff; + memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); +} + +/** + * 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. + */ +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc) +{ + const unsigned int l = BCH_ECC_WORDS(bch)-1; + unsigned int i, mlen; + unsigned long m; + uint32_t w, r[BCH_ECC_MAX_WORDS]; + const size_t r_bytes = BCH_ECC_WORDS(bch) * sizeof(*r); + const uint32_t * const tab0 = bch->mod8_tab; + const uint32_t * const tab1 = tab0 + 256*(l+1); + const uint32_t * const tab2 = tab1 + 256*(l+1); + const uint32_t * const tab3 = tab2 + 256*(l+1); + const uint32_t *pdata, *p0, *p1, *p2, *p3; + + if (WARN_ON(r_bytes > sizeof(r))) + return; + + if (ecc) { + /* load ecc parity bytes into internal 32-bit buffer */ + load_ecc8(bch, bch->ecc_buf, ecc); + } else { + memset(bch->ecc_buf, 0, r_bytes); + } + + /* process first unaligned data bytes */ + m = ((unsigned long)data) & 3; + if (m) { + mlen = (len < (4-m)) ? len : 4-m; + encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); + data += mlen; + len -= mlen; + } + + /* process 32-bit aligned data words */ + pdata = (uint32_t *)data; + mlen = len/4; + data += 4*mlen; + len -= 4*mlen; + memcpy(r, bch->ecc_buf, r_bytes); + + /* + * split each 32-bit word into 4 polynomials of weight 8 as follows: + * + * 31 ...24 23 ...16 15 ... 8 7 ... 0 + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt + * tttttttt mod g = r0 (precomputed) + * zzzzzzzz 00000000 mod g = r1 (precomputed) + * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) + * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 + */ + while (mlen--) { + /* input data is read in big-endian format */ + w = r[0]^cpu_to_be32(*pdata++); + p0 = tab0 + (l+1)*((w >> 0) & 0xff); + p1 = tab1 + (l+1)*((w >> 8) & 0xff); + p2 = tab2 + (l+1)*((w >> 16) & 0xff); + p3 = tab3 + (l+1)*((w >> 24) & 0xff); + + for (i = 0; i < l; i++) + r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; + + r[l] = p0[l]^p1[l]^p2[l]^p3[l]; + } + memcpy(bch->ecc_buf, r, r_bytes); + + /* process last unaligned bytes */ + if (len) + encode_bch_unaligned(bch, data, len, bch->ecc_buf); + + /* store ecc parity bytes into original parity buffer */ + if (ecc) + store_ecc8(bch, ecc, bch->ecc_buf); +} +EXPORT_SYMBOL_GPL(encode_bch); + +static inline int modulo(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + while (v >= n) { + v -= n; + v = (v & n) + (v >> GF_M(bch)); + } + return v; +} + +/* + * shorter and faster modulo function, only works when v < 2N. + */ +static inline int mod_s(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + return (v < n) ? v : v-n; +} + +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return fls(poly)-1; +} + +static inline int parity(unsigned int x) +{ + /* + * public domain code snippet, lifted from + * http://www-graphics.stanford.edu/~seander/bithacks.html + */ + x ^= x >> 1; + x ^= x >> 2; + x = (x & 0x11111111U) * 0x11111111U; + return (x >> 28) & 1; +} + +/* Galois field basic operations: multiply, divide, inverse, etc. */ + +static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) +{ + return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; +} + +static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + GF_N(bch)-bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) +{ + return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; +} + +static inline unsigned int a_pow(struct bch_control *bch, int i) +{ + return bch->a_pow_tab[modulo(bch, i)]; +} + +static inline int a_log(struct bch_control *bch, unsigned int x) +{ + return bch->a_log_tab[x]; +} + +static inline int a_ilog(struct bch_control *bch, unsigned int x) +{ + return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); +} + +/* + * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t + */ +static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, + unsigned int *syn) +{ + int i, j, s; + unsigned int m; + uint32_t poly; + const int t = GF_T(bch); + + s = bch->ecc_bits; + + /* make sure extra bits in last ecc word are cleared */ + m = ((unsigned int)s) & 31; + if (m) + ecc[s/32] &= ~((1u << (32-m))-1); + memset(syn, 0, 2*t*sizeof(*syn)); + + /* compute v(a^j) for j=1 .. 2t-1 */ + do { + poly = *ecc++; + s -= 32; + while (poly) { + i = deg(poly); + for (j = 0; j < 2*t; j += 2) + syn[j] ^= a_pow(bch, (j+1)*(i+s)); + + poly ^= (1 << i); + } + } while (s > 0); + + /* v(a^(2j)) = v(a^j)^2 */ + for (j = 0; j < t; j++) + syn[2*j+1] = gf_sqr(bch, syn[j]); +} + +static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) +{ + memcpy(dst, src, GF_POLY_SZ(src->deg)); +} + +static int compute_error_locator_polynomial(struct bch_control *bch, + const unsigned int *syn) +{ + const unsigned int t = GF_T(bch); + const unsigned int n = GF_N(bch); + unsigned int i, j, tmp, l, pd = 1, d = syn[0]; + struct gf_poly *elp = bch->elp; + struct gf_poly *pelp = bch->poly_2t[0]; + struct gf_poly *elp_copy = bch->poly_2t[1]; + int k, pp = -1; + + memset(pelp, 0, GF_POLY_SZ(2*t)); + memset(elp, 0, GF_POLY_SZ(2*t)); + + pelp->deg = 0; + pelp->c[0] = 1; + elp->deg = 0; + elp->c[0] = 1; + + /* use simplified binary Berlekamp-Massey algorithm */ + for (i = 0; (i < t) && (elp->deg <= t); i++) { + if (d) { + k = 2*i-pp; + gf_poly_copy(elp_copy, elp); + /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ + tmp = a_log(bch, d)+n-a_log(bch, pd); + for (j = 0; j <= pelp->deg; j++) { + if (pelp->c[j]) { + l = a_log(bch, pelp->c[j]); + elp->c[j+k] ^= a_pow(bch, tmp+l); + } + } + /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ + tmp = pelp->deg+k; + if (tmp > elp->deg) { + elp->deg = tmp; + gf_poly_copy(pelp, elp_copy); + pd = d; + pp = 2*i; + } + } + /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ + if (i < t-1) { + d = syn[2*i+2]; + for (j = 1; j <= elp->deg; j++) + d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); + } + } + dbg("elp=%s\n", gf_poly_str(elp)); + return (elp->deg > t) ? -1 : (int)elp->deg; +} + +/* + * solve a m x m linear system in GF(2) with an expected number of solutions, + * and return the number of found solutions + */ +static int solve_linear_system(struct bch_control *bch, unsigned int *rows, + unsigned int *sol, int nsol) +{ + const int m = GF_M(bch); + unsigned int tmp, mask; + int rem, c, r, p, k, param[BCH_MAX_M]; + + k = 0; + mask = 1 << m; + + /* Gaussian elimination */ + for (c = 0; c < m; c++) { + rem = 0; + p = c-k; + /* find suitable row for elimination */ + for (r = p; r < m; r++) { + if (rows[r] & mask) { + if (r != p) { + tmp = rows[r]; + rows[r] = rows[p]; + rows[p] = tmp; + } + rem = r+1; + break; + } + } + if (rem) { + /* perform elimination on remaining rows */ + tmp = rows[p]; + for (r = rem; r < m; r++) { + if (rows[r] & mask) + rows[r] ^= tmp; + } + } else { + /* elimination not needed, store defective row index */ + param[k++] = c; + } + mask >>= 1; + } + /* rewrite system, inserting fake parameter rows */ + if (k > 0) { + p = k; + for (r = m-1; r >= 0; r--) { + if ((r > m-1-k) && rows[r]) + /* system has no solution */ + return 0; + + rows[r] = (p && (r == param[p-1])) ? + p--, 1u << (m-r) : rows[r-p]; + } + } + + if (nsol != (1 << k)) + /* unexpected number of solutions */ + return 0; + + for (p = 0; p < nsol; p++) { + /* set parameters for p-th solution */ + for (c = 0; c < k; c++) + rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); + + /* compute unique solution */ + tmp = 0; + for (r = m-1; r >= 0; r--) { + mask = rows[r] & (tmp|1); + tmp |= parity(mask) << (m-r); + } + sol[p] = tmp >> 1; + } + return nsol; +} + +/* + * this function builds and solves a linear system for finding roots of a degree + * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). + */ +static int find_affine4_roots(struct bch_control *bch, unsigned int a, + unsigned int b, unsigned int c, + unsigned int *roots) +{ + int i, j, k; + const int m = GF_M(bch); + unsigned int mask = 0xff, t, rows[16] = {0,}; + + j = a_log(bch, b); + k = a_log(bch, a); + rows[0] = c; + + /* buid linear system to solve X^4+aX^2+bX+c = 0 */ + for (i = 0; i < m; i++) { + rows[i+1] = bch->a_pow_tab[4*i]^ + (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ + (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); + j++; + k += 2; + } + /* + * transpose 16x16 matrix before passing it to linear solver + * warning: this code assumes m < 16 + */ + for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { + for (k = 0; k < 16; k = (k+j+1) & ~j) { + t = ((rows[k] >> j)^rows[k+j]) & mask; + rows[k] ^= (t << j); + rows[k+j] ^= t; + } + } + return solve_linear_system(bch, rows, roots, 4); +} + +/* + * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) + */ +static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0; + + if (poly->c[0]) + /* poly[X] = bX+c with c!=0, root=c/b */ + roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ + bch->a_log_tab[poly->c[1]]); + return n; +} + +/* + * compute roots of a degree 2 polynomial over GF(2^m) + */ +static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0, i, l0, l1, l2; + unsigned int u, v, r; + + if (poly->c[0] && poly->c[1]) { + + l0 = bch->a_log_tab[poly->c[0]]; + l1 = bch->a_log_tab[poly->c[1]]; + l2 = bch->a_log_tab[poly->c[2]]; + + /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ + u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); + /* + * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): + * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = + * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) + * i.e. r and r+1 are roots iff Tr(u)=0 + */ + r = 0; + v = u; + while (v) { + i = deg(v); + r ^= bch->xi_tab[i]; + v ^= (1 << i); + } + /* verify root */ + if ((gf_sqr(bch, r)^r) == u) { + /* reverse z=a/bX transformation and compute log(1/r) */ + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r]+l2); + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r^1]+l2); + } + } + return n; +} + +/* + * compute roots of a degree 3 polynomial over GF(2^m) + */ +static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, n = 0; + unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; + + if (poly->c[0]) { + /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ + e3 = poly->c[3]; + c2 = gf_div(bch, poly->c[0], e3); + b2 = gf_div(bch, poly->c[1], e3); + a2 = gf_div(bch, poly->c[2], e3); + + /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ + c = gf_mul(bch, a2, c2); /* c = a2c2 */ + b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ + a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ + + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a, b, c, tmp) == 4) { + /* remove a2 from final list of roots */ + for (i = 0; i < 4; i++) { + if (tmp[i] != a2) + roots[n++] = a_ilog(bch, tmp[i]); + } + } + } + return n; +} + +/* + * compute roots of a degree 4 polynomial over GF(2^m) + */ +static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, l, n = 0; + unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; + + if (poly->c[0] == 0) + return 0; + + /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ + e4 = poly->c[4]; + d = gf_div(bch, poly->c[0], e4); + c = gf_div(bch, poly->c[1], e4); + b = gf_div(bch, poly->c[2], e4); + a = gf_div(bch, poly->c[3], e4); + + /* use Y=1/X transformation to get an affine polynomial */ + if (a) { + /* first, eliminate cX by using z=X+e with ae^2+c=0 */ + if (c) { + /* compute e such that e^2 = c/a */ + f = gf_div(bch, c, a); + l = a_log(bch, f); + l += (l & 1) ? GF_N(bch) : 0; + e = a_pow(bch, l/2); + /* + * use transformation z=X+e: + * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d + * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d + * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d + * z^4 + az^3 + b'z^2 + d' + */ + d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; + b = gf_mul(bch, a, e)^b; + } + /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ + if (d == 0) + /* assume all roots have multiplicity 1 */ + return 0; + + c2 = gf_inv(bch, d); + b2 = gf_div(bch, a, d); + a2 = gf_div(bch, b, d); + } else { + /* polynomial is already affine */ + c2 = d; + b2 = c; + a2 = b; + } + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { + for (i = 0; i < 4; i++) { + /* post-process roots (reverse transformations) */ + f = a ? gf_inv(bch, roots[i]) : roots[i]; + roots[i] = a_ilog(bch, f^e); + } + n = 4; + } + return n; +} + +/* + * build monic, log-based representation of a polynomial + */ +static void gf_poly_logrep(struct bch_control *bch, + const struct gf_poly *a, int *rep) +{ + int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); + + /* represent 0 values with -1; warning, rep[d] is not set to 1 */ + for (i = 0; i < d; i++) + rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; +} + +/* + * compute polynomial Euclidean division remainder in GF(2^m)[X] + */ +static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, int *rep) +{ + int la, p, m; + unsigned int i, j, *c = a->c; + const unsigned int d = b->deg; + + if (a->deg < d) + return; + + /* reuse or compute log representation of denominator */ + if (!rep) { + rep = bch->cache; + gf_poly_logrep(bch, b, rep); + } + + for (j = a->deg; j >= d; j--) { + if (c[j]) { + la = a_log(bch, c[j]); + p = j-d; + for (i = 0; i < d; i++, p++) { + m = rep[i]; + if (m >= 0) + c[p] ^= bch->a_pow_tab[mod_s(bch, + m+la)]; + } + } + } + a->deg = d-1; + while (!c[a->deg] && a->deg) + a->deg--; +} + +/* + * compute polynomial Euclidean division quotient in GF(2^m)[X] + */ +static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, struct gf_poly *q) +{ + if (a->deg >= b->deg) { + q->deg = a->deg-b->deg; + /* compute a mod b (modifies a) */ + gf_poly_mod(bch, a, b, NULL); + /* quotient is stored in upper part of polynomial a */ + memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); + } else { + q->deg = 0; + q->c[0] = 0; + } +} + +/* + * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] + */ +static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, + struct gf_poly *b) +{ + struct gf_poly *tmp; + + dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); + + if (a->deg < b->deg) { + tmp = b; + b = a; + a = tmp; + } + + while (b->deg > 0) { + gf_poly_mod(bch, a, b, NULL); + tmp = b; + b = a; + a = tmp; + } + + dbg("%s\n", gf_poly_str(a)); + + return a; +} + +/* + * Given a polynomial f and an integer k, compute Tr(a^kX) mod f + * This is used in Berlekamp Trace algorithm for splitting polynomials + */ +static void compute_trace_bk_mod(struct bch_control *bch, int k, + const struct gf_poly *f, struct gf_poly *z, + struct gf_poly *out) +{ + const int m = GF_M(bch); + int i, j; + + /* z contains z^2j mod f */ + z->deg = 1; + z->c[0] = 0; + z->c[1] = bch->a_pow_tab[k]; + + out->deg = 0; + memset(out, 0, GF_POLY_SZ(f->deg)); + + /* compute f log representation only once */ + gf_poly_logrep(bch, f, bch->cache); + + for (i = 0; i < m; i++) { + /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ + for (j = z->deg; j >= 0; j--) { + out->c[j] ^= z->c[j]; + z->c[2*j] = gf_sqr(bch, z->c[j]); + z->c[2*j+1] = 0; + } + if (z->deg > out->deg) + out->deg = z->deg; + + if (i < m-1) { + z->deg *= 2; + /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ + gf_poly_mod(bch, z, f, bch->cache); + } + } + while (!out->c[out->deg] && out->deg) + out->deg--; + + dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); +} + +/* + * factor a polynomial using Berlekamp Trace algorithm (BTA) + */ +static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, + struct gf_poly **g, struct gf_poly **h) +{ + struct gf_poly *f2 = bch->poly_2t[0]; + struct gf_poly *q = bch->poly_2t[1]; + struct gf_poly *tk = bch->poly_2t[2]; + struct gf_poly *z = bch->poly_2t[3]; + struct gf_poly *gcd; + + dbg("factoring %s...\n", gf_poly_str(f)); + + *g = f; + *h = NULL; + + /* tk = Tr(a^k.X) mod f */ + compute_trace_bk_mod(bch, k, f, z, tk); + + if (tk->deg > 0) { + /* compute g = gcd(f, tk) (destructive operation) */ + gf_poly_copy(f2, f); + gcd = gf_poly_gcd(bch, f2, tk); + if (gcd->deg < f->deg) { + /* compute h=f/gcd(f,tk); this will modify f and q */ + gf_poly_div(bch, f, gcd, q); + /* store g and h in-place (clobbering f) */ + *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; + gf_poly_copy(*g, gcd); + gf_poly_copy(*h, q); + } + } +} + +/* + * find roots of a polynomial, using BTZ algorithm; see the beginning of this + * file for details + */ +static int find_poly_roots(struct bch_control *bch, unsigned int k, + struct gf_poly *poly, unsigned int *roots) +{ + int cnt; + struct gf_poly *f1, *f2; + + switch (poly->deg) { + /* handle low degree polynomials with ad hoc techniques */ + case 1: + cnt = find_poly_deg1_roots(bch, poly, roots); + break; + case 2: + cnt = find_poly_deg2_roots(bch, poly, roots); + break; + case 3: + cnt = find_poly_deg3_roots(bch, poly, roots); + break; + case 4: + cnt = find_poly_deg4_roots(bch, poly, roots); + break; + default: + /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ + cnt = 0; + if (poly->deg && (k <= GF_M(bch))) { + factor_polynomial(bch, k, poly, &f1, &f2); + if (f1) + cnt += find_poly_roots(bch, k+1, f1, roots); + if (f2) + cnt += find_poly_roots(bch, k+1, f2, roots+cnt); + } + break; + } + return cnt; +} + +#if defined(USE_CHIEN_SEARCH) +/* + * exhaustive root search (Chien) implementation - not used, included only for + * reference/comparison tests + */ +static int chien_search(struct bch_control *bch, unsigned int len, + struct gf_poly *p, unsigned int *roots) +{ + int m; + unsigned int i, j, syn, syn0, count = 0; + const unsigned int k = 8*len+bch->ecc_bits; + + /* use a log-based representation of polynomial */ + gf_poly_logrep(bch, p, bch->cache); + bch->cache[p->deg] = 0; + syn0 = gf_div(bch, p->c[0], p->c[p->deg]); + + for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { + /* compute elp(a^i) */ + for (j = 1, syn = syn0; j <= p->deg; j++) { + m = bch->cache[j]; + if (m >= 0) + syn ^= a_pow(bch, m+j*i); + } + if (syn == 0) { + roots[count++] = GF_N(bch)-i; + if (count == p->deg) + break; + } + } + return (count == p->deg) ? count : 0; +} +#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) +#endif /* USE_CHIEN_SEARCH */ + +/** + * 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. + */ +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc) +{ + const unsigned int ecc_words = BCH_ECC_WORDS(bch); + unsigned int nbits; + int i, err, nroots; + uint32_t sum; + + /* sanity check: make sure data length can be handled */ + if (8*len > (bch->n-bch->ecc_bits)) + return -EINVAL; + + /* if caller does not provide syndromes, compute them */ + if (!syn) { + if (!calc_ecc) { + /* compute received data ecc into an internal buffer */ + if (!data || !recv_ecc) + return -EINVAL; + encode_bch(bch, data, len, NULL); + } else { + /* load provided calculated ecc */ + load_ecc8(bch, bch->ecc_buf, calc_ecc); + } + /* load received ecc or assume it was XORed in calc_ecc */ + if (recv_ecc) { + load_ecc8(bch, bch->ecc_buf2, recv_ecc); + /* XOR received and calculated ecc */ + for (i = 0, sum = 0; i < (int)ecc_words; i++) { + bch->ecc_buf[i] ^= bch->ecc_buf2[i]; + sum |= bch->ecc_buf[i]; + } + if (!sum) + /* no error found */ + return 0; + } + compute_syndromes(bch, bch->ecc_buf, bch->syn); + syn = bch->syn; + } + + err = compute_error_locator_polynomial(bch, syn); + if (err > 0) { + nroots = find_poly_roots(bch, 1, bch->elp, errloc); + if (err != nroots) + err = -1; + } + if (err > 0) { + /* post-process raw error locations for easier correction */ + nbits = (len*8)+bch->ecc_bits; + for (i = 0; i < err; i++) { + if (errloc[i] >= nbits) { + err = -1; + break; + } + errloc[i] = nbits-1-errloc[i]; + errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); + } + } + return (err >= 0) ? err : -EBADMSG; +} +EXPORT_SYMBOL_GPL(decode_bch); + +/* + * generate Galois field lookup tables + */ +static int build_gf_tables(struct bch_control *bch, unsigned int poly) +{ + unsigned int i, x = 1; + const unsigned int k = 1 << deg(poly); + + /* primitive polynomial must be of degree m */ + if (k != (1u << GF_M(bch))) + return -1; + + for (i = 0; i < GF_N(bch); i++) { + bch->a_pow_tab[i] = x; + bch->a_log_tab[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; + bch->a_log_tab[0] = 0; + + return 0; +} + +/* + * compute generator polynomial remainder tables for fast encoding + */ +static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) +{ + int i, j, b, d; + uint32_t data, hi, lo, *tab; + const int l = BCH_ECC_WORDS(bch); + const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); + const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); + + memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); + + for (i = 0; i < 256; i++) { + /* p(X)=i is a small polynomial of weight <= 8 */ + for (b = 0; b < 4; b++) { + /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ + tab = bch->mod8_tab + (b*256+i)*l; + data = i << (8*b); + while (data) { + d = deg(data); + /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ + data ^= g[0] >> (31-d); + for (j = 0; j < ecclen; j++) { + hi = (d < 31) ? g[j] << (d+1) : 0; + lo = (j+1 < plen) ? + g[j+1] >> (31-d) : 0; + tab[j] ^= hi|lo; + } + } + } + } +} + +/* + * build a base for factoring degree 2 polynomials + */ +static int build_deg2_base(struct bch_control *bch) +{ + const int m = GF_M(bch); + int i, j, r; + unsigned int sum, x, y, remaining, ak = 0, xi[BCH_MAX_M]; + + /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ + for (i = 0; i < m; i++) { + for (j = 0, sum = 0; j < m; j++) + sum ^= a_pow(bch, i*(1 << j)); + + if (sum) { + ak = bch->a_pow_tab[i]; + break; + } + } + /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ + remaining = m; + memset(xi, 0, sizeof(xi)); + + for (x = 0; (x <= GF_N(bch)) && remaining; x++) { + y = gf_sqr(bch, x)^x; + for (i = 0; i < 2; i++) { + r = a_log(bch, y); + if (y && (r < m) && !xi[r]) { + bch->xi_tab[r] = x; + xi[r] = 1; + remaining--; + dbg("x%d = %x\n", r, x); + break; + } + y ^= ak; + } + } + /* should not happen but check anyway */ + return remaining ? -1 : 0; +} + +static void *bch_alloc(size_t size, int *err) +{ + void *ptr; + + ptr = kmalloc(size, GFP_KERNEL); + if (ptr == NULL) + *err = 1; + return ptr; +} + +/* + * compute generator polynomial for given (m,t) parameters. + */ +static uint32_t *compute_generator_polynomial(struct bch_control *bch) +{ + const unsigned int m = GF_M(bch); + const unsigned int t = GF_T(bch); + int n, err = 0; + unsigned int i, j, nbits, r, word, *roots; + struct gf_poly *g; + uint32_t *genpoly; + + g = bch_alloc(GF_POLY_SZ(m*t), &err); + roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); + genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); + + if (err) { + kfree(genpoly); + genpoly = NULL; + goto finish; + } + + /* enumerate all roots of g(X) */ + memset(roots , 0, (bch->n+1)*sizeof(*roots)); + for (i = 0; i < t; i++) { + for (j = 0, r = 2*i+1; j < m; j++) { + roots[r] = 1; + r = mod_s(bch, 2*r); + } + } + /* build generator polynomial g(X) */ + g->deg = 0; + g->c[0] = 1; + for (i = 0; i < GF_N(bch); i++) { + if (roots[i]) { + /* multiply g(X) by (X+root) */ + r = bch->a_pow_tab[i]; + g->c[g->deg+1] = 1; + for (j = g->deg; j > 0; j--) + g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; + + g->c[0] = gf_mul(bch, g->c[0], r); + g->deg++; + } + } + /* store left-justified binary representation of g(X) */ + n = g->deg+1; + i = 0; + + while (n > 0) { + nbits = (n > 32) ? 32 : n; + for (j = 0, word = 0; j < nbits; j++) { + if (g->c[n-1-j]) + word |= 1u << (31-j); + } + genpoly[i++] = word; + n -= nbits; + } + bch->ecc_bits = g->deg; + +finish: + kfree(g); + kfree(roots); + + return genpoly; +} + +/** + * 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. + */ +struct bch_control *init_bch(int m, int t, unsigned int prim_poly) +{ + int err = 0; + unsigned int i, words; + uint32_t *genpoly; + struct bch_control *bch = NULL; + + const int min_m = 5; + + /* default primitive polynomials */ + static const unsigned int prim_poly_tab[] = { + 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, + 0x402b, 0x8003, + }; + +#if defined(CONFIG_BCH_CONST_PARAMS) + if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { + printk(KERN_ERR "bch encoder/decoder was configured to support " + "parameters m=%d, t=%d only!\n", + CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); + goto fail; + } +#endif + if ((m < min_m) || (m > BCH_MAX_M)) + /* + * values of m greater than 15 are not currently supported; + * supporting m > 15 would require changing table base type + * (uint16_t) and a small patch in matrix transposition + */ + goto fail; + + if (t > BCH_MAX_T) + /* + * we can support larger than 64 bits if necessary, at the + * cost of higher stack usage. + */ + goto fail; + + /* sanity checks */ + if ((t < 1) || (m*t >= ((1 << m)-1))) + /* invalid t value */ + goto fail; + + /* select a primitive polynomial for generating GF(2^m) */ + if (prim_poly == 0) + prim_poly = prim_poly_tab[m-min_m]; + + bch = kzalloc(sizeof(*bch), GFP_KERNEL); + if (bch == NULL) + goto fail; + + bch->m = m; + bch->t = t; + bch->n = (1 << m)-1; + words = DIV_ROUND_UP(m*t, 32); + bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); + bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); + bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); + bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); + bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); + bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); + bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); + bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); + bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); + bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); + + if (err) + goto fail; + + err = build_gf_tables(bch, prim_poly); + if (err) + goto fail; + + /* use generator polynomial for computing encoding tables */ + genpoly = compute_generator_polynomial(bch); + if (genpoly == NULL) + goto fail; + + build_mod8_tables(bch, genpoly); + kfree(genpoly); + + err = build_deg2_base(bch); + if (err) + goto fail; + + return bch; + +fail: + free_bch(bch); + return NULL; +} +EXPORT_SYMBOL_GPL(init_bch); + +/** + * free_bch - free the BCH control structure + * @bch: BCH control structure to release + */ +void free_bch(struct bch_control *bch) +{ + unsigned int i; + + if (bch) { + kfree(bch->a_pow_tab); + kfree(bch->a_log_tab); + kfree(bch->mod8_tab); + kfree(bch->ecc_buf); + kfree(bch->ecc_buf2); + kfree(bch->xi_tab); + kfree(bch->syn); + kfree(bch->cache); + kfree(bch->elp); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + kfree(bch->poly_2t[i]); + + kfree(bch); + } +} +EXPORT_SYMBOL_GPL(free_bch); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ivan Djelic "); +MODULE_DESCRIPTION("Binary BCH encoder/decoder"); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..31e1bb2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/wrapper.h b/wrapper.h new file mode 100644 index 0000000..095f15d --- /dev/null +++ b/wrapper.h @@ -0,0 +1,2 @@ +#include +#include "bch.h" -- cgit v1.3.1 From 004fb40e5a5af447266c73b951a058ab014b528d Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 15:18:20 +0200 Subject: Add README --- README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8afb14b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# bchlib + +Rust bindings for BCH encoding/decoding library -- cgit v1.3.1 From 0cfc9e3e3d4e6b3bfffa82a61a8fa6399f92189c Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 16:20:28 +0200 Subject: Working version based on bch_codec --- Cargo.toml | 1 + bch.h | 79 --- build.rs | 6 + src/bch/bch | Bin 0 -> 22976 bytes src/bch/bch.c | 1523 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bch/bch.h | 99 ++++ wrapper.h | 3 +- 7 files changed, 1630 insertions(+), 81 deletions(-) delete mode 100644 bch.h create mode 100644 src/bch/bch create mode 100644 src/bch/bch.c create mode 100644 src/bch/bch.h diff --git a/Cargo.toml b/Cargo.toml index d60413f..7617876 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2018" [build-dependencies] bindgen = "0.42.2" +cc = "1.0" diff --git a/bch.h b/bch.h deleted file mode 100644 index 295b4ef..0000000 --- a/bch.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Generic binary BCH encoding/decoding library - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program 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. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright © 2011 Parrot S.A. - * - * Author: Ivan Djelic - * - * Description: - * - * This library provides runtime configurable encoding/decoding of binary - * Bose-Chaudhuri-Hocquenghem (BCH) codes. -*/ -#ifndef _BCH_H -#define _BCH_H - -#include - -/** - * 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 - */ -struct bch_control { - unsigned int m; - unsigned int n; - unsigned int t; - unsigned int ecc_bits; - unsigned int ecc_bytes; -/* private: */ - uint16_t *a_pow_tab; - uint16_t *a_log_tab; - uint32_t *mod8_tab; - uint32_t *ecc_buf; - uint32_t *ecc_buf2; - unsigned int *xi_tab; - unsigned int *syn; - int *cache; - struct gf_poly *elp; - struct gf_poly *poly_2t[4]; -}; - -struct bch_control *init_bch(int m, int t, unsigned int prim_poly); - -void free_bch(struct bch_control *bch); - -void encode_bch(struct bch_control *bch, const uint8_t *data, - unsigned int len, uint8_t *ecc); - -int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, - const uint8_t *recv_ecc, const uint8_t *calc_ecc, - const unsigned int *syn, unsigned int *errloc); - -#endif /* _BCH_H */ diff --git a/build.rs b/build.rs index 6c515a4..08715f8 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,5 @@ extern crate bindgen; +extern crate cc; use std::env; use std::path::PathBuf; @@ -15,4 +16,9 @@ fn main() { bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); + + cc::Build::new() + .file("src/bch/bch.c") + .include("src/bch/bch.h") + .compile("bch"); } diff --git a/src/bch/bch b/src/bch/bch new file mode 100644 index 0000000..5b438cd Binary files /dev/null and b/src/bch/bch differ diff --git a/src/bch/bch.c b/src/bch/bch.c new file mode 100644 index 0000000..87712e2 --- /dev/null +++ b/src/bch/bch.c @@ -0,0 +1,1523 @@ +/* + * Generic binary BCH encoding/decoding library + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. + * + * Call init_bch to get a pointer to a newly allocated bch_control structure for + * the given m (Galois field order), t (error correction capability) and + * (optional) primitive polynomial parameters. + * + * Call encode_bch to compute and store ecc parity bytes to a given buffer. + * Call decode_bch to detect and locate errors in received data. + * + * On systems supporting hw BCH features, intermediate results may be provided + * to decode_bch in order to skip certain steps. See decode_bch() documentation + * for details. + * + * Algorithmic details: + * + * Encoding is performed by processing 32 input bits in parallel, using 4 + * remainder lookup tables. + * + * The final stage of decoding involves the following internal steps: + * a. Syndrome computation + * b. Error locator polynomial computation using Berlekamp-Massey algorithm + * c. Error locator root finding (by far the most expensive step) + * + * In this implementation, step c is not performed using the usual Chien search. + * Instead, an alternative approach described in [1] is used. It consists in + * factoring the error locator polynomial using the Berlekamp Trace algorithm + * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial + * solving techniques [2] are used. The resulting algorithm, called BTZ, yields + * much better performance than Chien search for usual (m,t) values (typically + * m >= 13, t < 32, see [1]). + * + * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields + * of characteristic 2, in: Western European Workshop on Research in Cryptology + * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. + * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over + * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. + * + * History: + * 2015-05 Mark Borgerding (mark@borgerding.net): replaced linux kernel-specific functions, added bitwise encode/decode functions + */ + +#include +#include +#include "bch.h" +#include + +static +inline +uint32_t CPU_TO_BE32(uint32_t p) +{ + const uint8_t * bytes = (const uint8_t *)&p; + uint32_t out = + ((uint32_t)bytes[0] << 24) | + (bytes[1] << 16) | + (bytes[2] << 8) | + (bytes[3] ) ; + return out; +} + +static inline int FLS(uint32_t x) +{ + int r=0; + if (x>=(1<<16)) { r+=16;x>>=16; } + if (x>=(1<< 8)) { r+= 8;x>>= 8; } + if (x>=(1<< 4)) { r+= 4;x>>= 4; } + if (x>=(1<< 2)) { r+= 2;x>>= 2; } + if (x>=(1<< 1)) { r+= 1;x>>= 1; } + return r+x; +} + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +#define GF_M(_p) ((_p)->m) +#define GF_T(_p) ((_p)->t) +#define GF_N(_p) ((_p)->n) + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#endif + +#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) +#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) + +#ifndef dbg +#define dbg(_fmt, args...) do {} while (0) +#endif + +/* + * represent a polynomial over GF(2^m) + */ +struct gf_poly { + unsigned int deg; /* polynomial degree */ + unsigned int c[0]; /* polynomial terms */ +}; + +/* given its degree, compute a polynomial size in bytes */ +#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) + +/* polynomial of degree 1 */ +struct gf_poly_deg1 { + struct gf_poly poly; + unsigned int c[2]; +}; + +/* + * same as encode_bch(), but process input data one byte at a time + */ +static void encode_bch_unaligned(struct bch_control *bch, + const unsigned char *data, unsigned int len, + uint32_t *ecc) +{ + int i; + const uint32_t *p; + const int l = BCH_ECC_WORDS(bch)-1; + + while (len--) { + p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); + + for (i = 0; i < l; i++) + ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); + + ecc[l] = (ecc[l] << 8)^(*p); + } +} + +/* + * convert ecc bytes to aligned, zero-padded 32-bit ecc words + */ +static void load_ecc8(struct bch_control *bch, uint32_t *dst, + const uint8_t *src) +{ + uint8_t pad[4] = {0, 0, 0, 0}; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++, src += 4) + dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; + + memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); + dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; +} + +/* + * convert 32-bit ecc words to ecc bytes + */ +static void store_ecc8(struct bch_control *bch, uint8_t *dst, + const uint32_t *src) +{ + uint8_t pad[4]; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++) { + *dst++ = (src[i] >> 24); + *dst++ = (src[i] >> 16) & 0xff; + *dst++ = (src[i] >> 8) & 0xff; + *dst++ = (src[i] >> 0) & 0xff; + } + pad[0] = (src[nwords] >> 24); + pad[1] = (src[nwords] >> 16) & 0xff; + pad[2] = (src[nwords] >> 8) & 0xff; + pad[3] = (src[nwords] >> 0) & 0xff; + memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); +} + +/** + * 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. + */ +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc) +{ + const unsigned int l = BCH_ECC_WORDS(bch)-1; + unsigned int i, mlen; + unsigned long m; + uint32_t w, r[l+1]; + const uint32_t * const tab0 = bch->mod8_tab; + const uint32_t * const tab1 = tab0 + 256*(l+1); + const uint32_t * const tab2 = tab1 + 256*(l+1); + const uint32_t * const tab3 = tab2 + 256*(l+1); + const uint32_t *pdata, *p0, *p1, *p2, *p3; + + if (ecc) { + /* load ecc parity bytes into internal 32-bit buffer */ + load_ecc8(bch, bch->ecc_buf, ecc); + } else { + memset(bch->ecc_buf, 0, sizeof(r)); + } + + /* process first unaligned data bytes */ + m = ((unsigned long)data) & 3; + if (m) { + mlen = (len < (4-m)) ? len : 4-m; + encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); + data += mlen; + len -= mlen; + } + + /* process 32-bit aligned data words */ + pdata = (uint32_t *)data; + mlen = len/4; + data += 4*mlen; + len -= 4*mlen; + memcpy(r, bch->ecc_buf, sizeof(r)); + + /* + * split each 32-bit word into 4 polynomials of weight 8 as follows: + * + * 31 ...24 23 ...16 15 ... 8 7 ... 0 + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt + * tttttttt mod g = r0 (precomputed) + * zzzzzzzz 00000000 mod g = r1 (precomputed) + * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) + * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 + */ + while (mlen--) { + /* input data is read in big-endian format */ + w = r[0]^CPU_TO_BE32(*pdata++); + p0 = tab0 + (l+1)*((w >> 0) & 0xff); + p1 = tab1 + (l+1)*((w >> 8) & 0xff); + p2 = tab2 + (l+1)*((w >> 16) & 0xff); + p3 = tab3 + (l+1)*((w >> 24) & 0xff); + + for (i = 0; i < l; i++) + r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; + + r[l] = p0[l]^p1[l]^p2[l]^p3[l]; + } + memcpy(bch->ecc_buf, r, sizeof(r)); + + /* process last unaligned bytes */ + if (len) + encode_bch_unaligned(bch, data, len, bch->ecc_buf); + + /* store ecc parity bytes into original parity buffer */ + if (ecc) + store_ecc8(bch, ecc, bch->ecc_buf); +} + +static inline int modulo(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + while (v >= n) { + v -= n; + v = (v & n) + (v >> GF_M(bch)); + } + return v; +} + +/* + * shorter and faster modulo function, only works when v < 2N. + */ +static inline int mod_s(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + return (v < n) ? v : v-n; +} + +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return FLS(poly)-1; +} + +static inline int parity(unsigned int x) +{ + /* + * public domain code snippet, lifted from + * http://www-graphics.stanford.edu/~seander/bithacks.html + */ + x ^= x >> 1; + x ^= x >> 2; + x = (x & 0x11111111U) * 0x11111111U; + return (x >> 28) & 1; +} + +/* Galois field basic operations: multiply, divide, inverse, etc. */ + +static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) +{ + return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; +} + +static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + GF_N(bch)-bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) +{ + return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; +} + +static inline unsigned int a_pow(struct bch_control *bch, int i) +{ + return bch->a_pow_tab[modulo(bch, i)]; +} + +static inline int a_log(struct bch_control *bch, unsigned int x) +{ + return bch->a_log_tab[x]; +} + +static inline int a_ilog(struct bch_control *bch, unsigned int x) +{ + return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); +} + +/* + * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t + */ +static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, + unsigned int *syn) +{ + int i, j, s; + unsigned int m; + uint32_t poly; + const int t = GF_T(bch); + + s = bch->ecc_bits; + + /* make sure extra bits in last ecc word are cleared */ + m = ((unsigned int)s) & 31; + if (m) + ecc[s/32] &= ~((1u << (32-m))-1); + memset(syn, 0, 2*t*sizeof(*syn)); + + /* compute v(a^j) for j=1 .. 2t-1 */ + do { + poly = *ecc++; + s -= 32; + while (poly) { + i = deg(poly); + for (j = 0; j < 2*t; j += 2) + syn[j] ^= a_pow(bch, (j+1)*(i+s)); + + poly ^= (1 << i); + } + } while (s > 0); + + /* v(a^(2j)) = v(a^j)^2 */ + for (j = 0; j < t; j++) + syn[2*j+1] = gf_sqr(bch, syn[j]); +} + +static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) +{ + memcpy(dst, src, GF_POLY_SZ(src->deg)); +} + +static int compute_error_locator_polynomial(struct bch_control *bch, + const unsigned int *syn) +{ + const unsigned int t = GF_T(bch); + const unsigned int n = GF_N(bch); + unsigned int i, j, tmp, l, pd = 1, d = syn[0]; + struct gf_poly *elp = bch->elp; + struct gf_poly *pelp = bch->poly_2t[0]; + struct gf_poly *elp_copy = bch->poly_2t[1]; + int k, pp = -1; + + memset(pelp, 0, GF_POLY_SZ(2*t)); + memset(elp, 0, GF_POLY_SZ(2*t)); + + pelp->deg = 0; + pelp->c[0] = 1; + elp->deg = 0; + elp->c[0] = 1; + + /* use simplified binary Berlekamp-Massey algorithm */ + for (i = 0; (i < t) && (elp->deg <= t); i++) { + if (d) { + k = 2*i-pp; + gf_poly_copy(elp_copy, elp); + /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ + tmp = a_log(bch, d)+n-a_log(bch, pd); + for (j = 0; j <= pelp->deg; j++) { + if (pelp->c[j]) { + l = a_log(bch, pelp->c[j]); + elp->c[j+k] ^= a_pow(bch, tmp+l); + } + } + /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ + tmp = pelp->deg+k; + if (tmp > elp->deg) { + elp->deg = tmp; + gf_poly_copy(pelp, elp_copy); + pd = d; + pp = 2*i; + } + } + /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ + if (i < t-1) { + d = syn[2*i+2]; + for (j = 1; j <= elp->deg; j++) + d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); + } + } + dbg("elp=%s\n", gf_poly_str(elp)); + return (elp->deg > t) ? -1 : (int)elp->deg; +} + +/* + * solve a m x m linear system in GF(2) with an expected number of solutions, + * and return the number of found solutions + */ +static int solve_linear_system(struct bch_control *bch, unsigned int *rows, + unsigned int *sol, int nsol) +{ + const int m = GF_M(bch); + unsigned int tmp, mask; + int rem, c, r, p, k, param[m]; + + k = 0; + mask = 1 << m; + + /* Gaussian elimination */ + for (c = 0; c < m; c++) { + rem = 0; + p = c-k; + /* find suitable row for elimination */ + for (r = p; r < m; r++) { + if (rows[r] & mask) { + if (r != p) { + tmp = rows[r]; + rows[r] = rows[p]; + rows[p] = tmp; + } + rem = r+1; + break; + } + } + if (rem) { + /* perform elimination on remaining rows */ + tmp = rows[p]; + for (r = rem; r < m; r++) { + if (rows[r] & mask) + rows[r] ^= tmp; + } + } else { + /* elimination not needed, store defective row index */ + param[k++] = c; + } + mask >>= 1; + } + /* rewrite system, inserting fake parameter rows */ + if (k > 0) { + p = k; + for (r = m-1; r >= 0; r--) { + if ((r > m-1-k) && rows[r]) + /* system has no solution */ + return 0; + + rows[r] = (p && (r == param[p-1])) ? + p--, 1u << (m-r) : rows[r-p]; + } + } + + if (nsol != (1 << k)) + /* unexpected number of solutions */ + return 0; + + for (p = 0; p < nsol; p++) { + /* set parameters for p-th solution */ + for (c = 0; c < k; c++) + rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); + + /* compute unique solution */ + tmp = 0; + for (r = m-1; r >= 0; r--) { + mask = rows[r] & (tmp|1); + tmp |= parity(mask) << (m-r); + } + sol[p] = tmp >> 1; + } + return nsol; +} + +/* + * this function builds and solves a linear system for finding roots of a degree + * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). + */ +static int find_affine4_roots(struct bch_control *bch, unsigned int a, + unsigned int b, unsigned int c, + unsigned int *roots) +{ + int i, j, k; + const int m = GF_M(bch); + unsigned int mask = 0xff, t, rows[16] = {0,}; + + j = a_log(bch, b); + k = a_log(bch, a); + rows[0] = c; + + /* buid linear system to solve X^4+aX^2+bX+c = 0 */ + for (i = 0; i < m; i++) { + rows[i+1] = bch->a_pow_tab[4*i]^ + (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ + (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); + j++; + k += 2; + } + /* + * transpose 16x16 matrix before passing it to linear solver + * warning: this code assumes m < 16 + */ + for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { + for (k = 0; k < 16; k = (k+j+1) & ~j) { + t = ((rows[k] >> j)^rows[k+j]) & mask; + rows[k] ^= (t << j); + rows[k+j] ^= t; + } + } + return solve_linear_system(bch, rows, roots, 4); +} + +/* + * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) + */ +static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0; + + if (poly->c[0]) + /* poly[X] = bX+c with c!=0, root=c/b */ + roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ + bch->a_log_tab[poly->c[1]]); + return n; +} + +/* + * compute roots of a degree 2 polynomial over GF(2^m) + */ +static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0, i, l0, l1, l2; + unsigned int u, v, r; + + if (poly->c[0] && poly->c[1]) { + + l0 = bch->a_log_tab[poly->c[0]]; + l1 = bch->a_log_tab[poly->c[1]]; + l2 = bch->a_log_tab[poly->c[2]]; + + /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ + u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); + /* + * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): + * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = + * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) + * i.e. r and r+1 are roots iff Tr(u)=0 + */ + r = 0; + v = u; + while (v) { + i = deg(v); + r ^= bch->xi_tab[i]; + v ^= (1 << i); + } + /* verify root */ + if ((gf_sqr(bch, r)^r) == u) { + /* reverse z=a/bX transformation and compute log(1/r) */ + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r]+l2); + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r^1]+l2); + } + } + return n; +} + +/* + * compute roots of a degree 3 polynomial over GF(2^m) + */ +static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, n = 0; + unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; + + if (poly->c[0]) { + /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ + e3 = poly->c[3]; + c2 = gf_div(bch, poly->c[0], e3); + b2 = gf_div(bch, poly->c[1], e3); + a2 = gf_div(bch, poly->c[2], e3); + + /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ + c = gf_mul(bch, a2, c2); /* c = a2c2 */ + b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ + a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ + + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a, b, c, tmp) == 4) { + /* remove a2 from final list of roots */ + for (i = 0; i < 4; i++) { + if (tmp[i] != a2) + roots[n++] = a_ilog(bch, tmp[i]); + } + } + } + return n; +} + +/* + * compute roots of a degree 4 polynomial over GF(2^m) + */ +static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, l, n = 0; + unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; + + if (poly->c[0] == 0) + return 0; + + /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ + e4 = poly->c[4]; + d = gf_div(bch, poly->c[0], e4); + c = gf_div(bch, poly->c[1], e4); + b = gf_div(bch, poly->c[2], e4); + a = gf_div(bch, poly->c[3], e4); + + /* use Y=1/X transformation to get an affine polynomial */ + if (a) { + /* first, eliminate cX by using z=X+e with ae^2+c=0 */ + if (c) { + /* compute e such that e^2 = c/a */ + f = gf_div(bch, c, a); + l = a_log(bch, f); + l += (l & 1) ? GF_N(bch) : 0; + e = a_pow(bch, l/2); + /* + * use transformation z=X+e: + * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d + * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d + * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d + * z^4 + az^3 + b'z^2 + d' + */ + d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; + b = gf_mul(bch, a, e)^b; + } + /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ + if (d == 0) + /* assume all roots have multiplicity 1 */ + return 0; + + c2 = gf_inv(bch, d); + b2 = gf_div(bch, a, d); + a2 = gf_div(bch, b, d); + } else { + /* polynomial is already affine */ + c2 = d; + b2 = c; + a2 = b; + } + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { + for (i = 0; i < 4; i++) { + /* post-process roots (reverse transformations) */ + f = a ? gf_inv(bch, roots[i]) : roots[i]; + roots[i] = a_ilog(bch, f^e); + } + n = 4; + } + return n; +} + +/* + * build monic, log-based representation of a polynomial + */ +static void gf_poly_logrep(struct bch_control *bch, + const struct gf_poly *a, int *rep) +{ + int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); + + /* represent 0 values with -1; warning, rep[d] is not set to 1 */ + for (i = 0; i < d; i++) + rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; +} + +/* + * compute polynomial Euclidean division remainder in GF(2^m)[X] + */ +static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, int *rep) +{ + int la, p, m; + unsigned int i, j, *c = a->c; + const unsigned int d = b->deg; + + if (a->deg < d) + return; + + /* reuse or compute log representation of denominator */ + if (!rep) { + rep = bch->cache; + gf_poly_logrep(bch, b, rep); + } + + for (j = a->deg; j >= d; j--) { + if (c[j]) { + la = a_log(bch, c[j]); + p = j-d; + for (i = 0; i < d; i++, p++) { + m = rep[i]; + if (m >= 0) + c[p] ^= bch->a_pow_tab[mod_s(bch, + m+la)]; + } + } + } + a->deg = d-1; + while (!c[a->deg] && a->deg) + a->deg--; +} + +/* + * compute polynomial Euclidean division quotient in GF(2^m)[X] + */ +static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, struct gf_poly *q) +{ + if (a->deg >= b->deg) { + q->deg = a->deg-b->deg; + /* compute a mod b (modifies a) */ + gf_poly_mod(bch, a, b, NULL); + /* quotient is stored in upper part of polynomial a */ + memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); + } else { + q->deg = 0; + q->c[0] = 0; + } +} + +/* + * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] + */ +static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, + struct gf_poly *b) +{ + struct gf_poly *tmp; + + dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); + + if (a->deg < b->deg) { + tmp = b; + b = a; + a = tmp; + } + + while (b->deg > 0) { + gf_poly_mod(bch, a, b, NULL); + tmp = b; + b = a; + a = tmp; + } + + dbg("%s\n", gf_poly_str(a)); + + return a; +} + +/* + * Given a polynomial f and an integer k, compute Tr(a^kX) mod f + * This is used in Berlekamp Trace algorithm for splitting polynomials + */ +static void compute_trace_bk_mod(struct bch_control *bch, int k, + const struct gf_poly *f, struct gf_poly *z, + struct gf_poly *out) +{ + const int m = GF_M(bch); + int i, j; + + /* z contains z^2j mod f */ + z->deg = 1; + z->c[0] = 0; + z->c[1] = bch->a_pow_tab[k]; + + out->deg = 0; + memset(out, 0, GF_POLY_SZ(f->deg)); + + /* compute f log representation only once */ + gf_poly_logrep(bch, f, bch->cache); + + for (i = 0; i < m; i++) { + /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ + for (j = z->deg; j >= 0; j--) { + out->c[j] ^= z->c[j]; + z->c[2*j] = gf_sqr(bch, z->c[j]); + z->c[2*j+1] = 0; + } + if (z->deg > out->deg) + out->deg = z->deg; + + if (i < m-1) { + z->deg *= 2; + /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ + gf_poly_mod(bch, z, f, bch->cache); + } + } + while (!out->c[out->deg] && out->deg) + out->deg--; + + dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); +} + +/* + * factor a polynomial using Berlekamp Trace algorithm (BTA) + */ +static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, + struct gf_poly **g, struct gf_poly **h) +{ + struct gf_poly *f2 = bch->poly_2t[0]; + struct gf_poly *q = bch->poly_2t[1]; + struct gf_poly *tk = bch->poly_2t[2]; + struct gf_poly *z = bch->poly_2t[3]; + struct gf_poly *gcd; + + dbg("factoring %s...\n", gf_poly_str(f)); + + *g = f; + *h = NULL; + + /* tk = Tr(a^k.X) mod f */ + compute_trace_bk_mod(bch, k, f, z, tk); + + if (tk->deg > 0) { + /* compute g = gcd(f, tk) (destructive operation) */ + gf_poly_copy(f2, f); + gcd = gf_poly_gcd(bch, f2, tk); + if (gcd->deg < f->deg) { + /* compute h=f/gcd(f,tk); this will modify f and q */ + gf_poly_div(bch, f, gcd, q); + /* store g and h in-place (clobbering f) */ + *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; + gf_poly_copy(*g, gcd); + gf_poly_copy(*h, q); + } + } +} + +/* + * find roots of a polynomial, using BTZ algorithm; see the beginning of this + * file for details + */ +static int find_poly_roots(struct bch_control *bch, unsigned int k, + struct gf_poly *poly, unsigned int *roots) +{ + int cnt; + struct gf_poly *f1, *f2; + + switch (poly->deg) { + /* handle low degree polynomials with ad hoc techniques */ + case 1: + cnt = find_poly_deg1_roots(bch, poly, roots); + break; + case 2: + cnt = find_poly_deg2_roots(bch, poly, roots); + break; + case 3: + cnt = find_poly_deg3_roots(bch, poly, roots); + break; + case 4: + cnt = find_poly_deg4_roots(bch, poly, roots); + break; + default: + /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ + cnt = 0; + if (poly->deg && (k <= GF_M(bch))) { + factor_polynomial(bch, k, poly, &f1, &f2); + if (f1) + cnt += find_poly_roots(bch, k+1, f1, roots); + if (f2) + cnt += find_poly_roots(bch, k+1, f2, roots+cnt); + } + break; + } + return cnt; +} + +#if defined(USE_CHIEN_SEARCH) +/* + * exhaustive root search (Chien) implementation - not used, included only for + * reference/comparison tests + */ +static int chien_search(struct bch_control *bch, unsigned int len, + struct gf_poly *p, unsigned int *roots) +{ + int m; + unsigned int i, j, syn, syn0, count = 0; + const unsigned int k = 8*len+bch->ecc_bits; + + /* use a log-based representation of polynomial */ + gf_poly_logrep(bch, p, bch->cache); + bch->cache[p->deg] = 0; + syn0 = gf_div(bch, p->c[0], p->c[p->deg]); + + for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { + /* compute elp(a^i) */ + for (j = 1, syn = syn0; j <= p->deg; j++) { + m = bch->cache[j]; + if (m >= 0) + syn ^= a_pow(bch, m+j*i); + } + if (syn == 0) { + roots[count++] = GF_N(bch)-i; + if (count == p->deg) + break; + } + } + return (count == p->deg) ? count : 0; +} +#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) +#endif /* USE_CHIEN_SEARCH */ + +/** + * 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. + */ +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc) +{ + const unsigned int ecc_words = BCH_ECC_WORDS(bch); + unsigned int nbits; + int i, err, nroots; + uint32_t sum; + + /* sanity check: make sure data length can be handled */ + if ( len > ((bch->n-bch->ecc_bits+7)/8)) + return -EINVAL; + + /* if caller does not provide syndromes, compute them */ + if (!syn) { + if (!calc_ecc) { + /* compute received data ecc into an internal buffer */ + if (!data || !recv_ecc) + return -EINVAL; + encode_bch(bch, data, len, NULL); + } else { + /* load provided calculated ecc */ + load_ecc8(bch, bch->ecc_buf, calc_ecc); + } + /* load received ecc or assume it was XORed in calc_ecc */ + if (recv_ecc) { + load_ecc8(bch, bch->ecc_buf2, recv_ecc); + /* XOR received and calculated ecc */ + for (i = 0, sum = 0; i < (int)ecc_words; i++) { + bch->ecc_buf[i] ^= bch->ecc_buf2[i]; + sum |= bch->ecc_buf[i]; + } + if (!sum) + /* no error found */ + return 0; + } + compute_syndromes(bch, bch->ecc_buf, bch->syn); + syn = bch->syn; + } + + err = compute_error_locator_polynomial(bch, syn); + if (err > 0) { + nroots = find_poly_roots(bch, 1, bch->elp, errloc); + if (err != nroots) + err = -1; + } + if (err > 0) { + /* post-process raw error locations for easier correction */ + nbits = (len*8)+bch->ecc_bits; + for (i = 0; i < err; i++) { + if (errloc[i] >= nbits) { + err = -1; + break; + } + errloc[i] = nbits-1-errloc[i]; + errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); + } + } + return (err >= 0) ? err : -EBADMSG; +} + +/* + * generate Galois field lookup tables + */ +static int build_gf_tables(struct bch_control *bch, unsigned int poly) +{ + unsigned int i, x = 1; + const unsigned int k = 1 << deg(poly); + + /* primitive polynomial must be of degree m */ + if (k != (1u << GF_M(bch))) + return -1; + + for (i = 0; i < GF_N(bch); i++) { + bch->a_pow_tab[i] = x; + bch->a_log_tab[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; + bch->a_log_tab[0] = 0; + + return 0; +} + +/* + * compute generator polynomial remainder tables for fast encoding + */ +static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) +{ + int i, j, b, d; + uint32_t data, hi, lo, *tab; + const int l = BCH_ECC_WORDS(bch); + const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); + const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); + + memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); + + for (i = 0; i < 256; i++) { + /* p(X)=i is a small polynomial of weight <= 8 */ + for (b = 0; b < 4; b++) { + /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ + tab = bch->mod8_tab + (b*256+i)*l; + data = i << (8*b); + while (data) { + d = deg(data); + /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ + data ^= g[0] >> (31-d); + for (j = 0; j < ecclen; j++) { + hi = (d < 31) ? g[j] << (d+1) : 0; + lo = (j+1 < plen) ? + g[j+1] >> (31-d) : 0; + tab[j] ^= hi|lo; + } + } + } + } +} + +/* + * build a base for factoring degree 2 polynomials + */ +static int build_deg2_base(struct bch_control *bch) +{ + const int m = GF_M(bch); + int i, j, r; + unsigned int sum, x, y, remaining, ak = 0, xi[m]; + + /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ + for (i = 0; i < m; i++) { + for (j = 0, sum = 0; j < m; j++) + sum ^= a_pow(bch, i*(1 << j)); + + if (sum) { + ak = bch->a_pow_tab[i]; + break; + } + } + /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ + remaining = m; + memset(xi, 0, sizeof(xi)); + + for (x = 0; (x <= GF_N(bch)) && remaining; x++) { + y = gf_sqr(bch, x)^x; + for (i = 0; i < 2; i++) { + r = a_log(bch, y); + if (y && (r < m) && !xi[r]) { + bch->xi_tab[r] = x; + xi[r] = 1; + remaining--; + dbg("x%d = %x\n", r, x); + break; + } + y ^= ak; + } + } + /* should not happen but check anyway */ + return remaining ? -1 : 0; +} + +static void *bch_alloc(size_t size, int *err) +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) + *err = 1; + return ptr; +} + +/* + * compute generator polynomial for given (m,t) parameters. + */ +static uint32_t *compute_generator_polynomial(struct bch_control *bch) +{ + const unsigned int m = GF_M(bch); + const unsigned int t = GF_T(bch); + int n, err = 0; + unsigned int i, j, nbits, r, word, *roots; + struct gf_poly *g; + uint32_t *genpoly; + + g = (struct gf_poly*)bch_alloc(GF_POLY_SZ(m*t), &err); + roots = (unsigned int*)bch_alloc((bch->n+1)*sizeof(*roots), &err); + genpoly = (uint32_t*)bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); + + if (err) { + free(genpoly); + genpoly = NULL; + goto finish; + } + + /* enumerate all roots of g(X) */ + memset(roots , 0, (bch->n+1)*sizeof(*roots)); + for (i = 0; i < t; i++) { + for (j = 0, r = 2*i+1; j < m; j++) { + roots[r] = 1; + r = mod_s(bch, 2*r); + } + } + /* build generator polynomial g(X) */ + g->deg = 0; + g->c[0] = 1; + for (i = 0; i < GF_N(bch); i++) { + if (roots[i]) { + /* multiply g(X) by (X+root) */ + r = bch->a_pow_tab[i]; + g->c[g->deg+1] = 1; + for (j = g->deg; j > 0; j--) + g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; + + g->c[0] = gf_mul(bch, g->c[0], r); + g->deg++; + } + } + /* store left-justified binary representation of g(X) */ + n = g->deg+1; + i = 0; + + while (n > 0) { + nbits = (n > 32) ? 32 : n; + for (j = 0, word = 0; j < nbits; j++) { + if (g->c[n-1-j]) + word |= 1u << (31-j); + } + genpoly[i++] = word; + n -= nbits; + } + bch->ecc_bits = g->deg; + +finish: + free(g); + free(roots); + + return genpoly; +} + +/** + * 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. + */ +struct bch_control *init_bch(int m, int t, unsigned int prim_poly) +{ + int err = 0; + unsigned int i, words; + uint32_t *genpoly; + struct bch_control *bch = NULL; + + const int min_m = 5; + const int max_m = 15; + + /* default primitive polynomials */ + static const unsigned int prim_poly_tab[] = { + 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, + 0x402b, 0x8003, + }; + + if ((m < min_m) || (m > max_m)) + /* + * values of m greater than 15 are not currently supported; + * supporting m > 15 would require changing table base type + * (uint16_t) and a small patch in matrix transposition + */ + goto fail; + + /* sanity checks */ + if ((t < 1) || (m*t >= ((1 << m)-1))) + /* invalid t value */ + goto fail; + + /* select a primitive polynomial for generating GF(2^m) */ + if (prim_poly == 0) + prim_poly = prim_poly_tab[m-min_m]; + + bch = (struct bch_control*)malloc(sizeof(*bch)); + if (bch == NULL) + goto fail; + memset(bch,0,sizeof(*bch)); + + bch->m = m; + bch->t = t; + bch->n = (1 << m)-1; + words = DIV_ROUND_UP(m*t, 32); + bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); + bch->a_pow_tab = (uint16_t*)bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); + bch->a_log_tab = (uint16_t*)bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); + bch->mod8_tab = (uint32_t*)bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); + bch->ecc_buf = (uint32_t*)bch_alloc(words*sizeof(*bch->ecc_buf), &err); + bch->ecc_buf2 = (uint32_t*)bch_alloc(words*sizeof(*bch->ecc_buf2), &err); + bch->xi_tab = (unsigned int*)bch_alloc(m*sizeof(*bch->xi_tab), &err); + bch->syn = (unsigned int*)bch_alloc(2*t*sizeof(*bch->syn), &err); + bch->cache = (int*)bch_alloc(2*t*sizeof(*bch->cache), &err); + bch->elp = (struct gf_poly*)bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + bch->poly_2t[i] = (struct gf_poly*)bch_alloc(GF_POLY_SZ(2*t), &err); + + if (err) + goto fail; + + err = build_gf_tables(bch, prim_poly); + if (err) + goto fail; + + /* use generator polynomial for computing encoding tables */ + genpoly = compute_generator_polynomial(bch); + if (genpoly == NULL) + goto fail; + + build_mod8_tables(bch, genpoly); + free(genpoly); + + err = build_deg2_base(bch); + if (err) + goto fail; + + return bch; + +fail: + free_bch(bch); + return NULL; +} + +/** + * free_bch - free the BCH control structure + * @bch: BCH control structure to release + */ +void free_bch(struct bch_control *bch) +{ + unsigned int i; + + if (bch) { + free(bch->a_pow_tab); + free(bch->a_log_tab); + free(bch->mod8_tab); + free(bch->ecc_buf); + free(bch->ecc_buf2); + free(bch->xi_tab); + free(bch->syn); + free(bch->cache); + free(bch->elp); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + free(bch->poly_2t[i]); + + free(bch->databuf); + + free(bch); + } +} + +static void check_databuf(struct bch_control *bch) +{ + if (bch->databuf == NULL) + bch->databuf = (uint8_t*)malloc( ((bch->n - bch->ecc_bits)+7)/8 + bch->ecc_bytes ); +} + +static int pack_databuf( struct bch_control *bch , const uint8_t *data) +{ + const int K = bch->n - bch->ecc_bits; + int k; + int ndatabytes = (K+7)/8; + int nPad=ndatabytes*8 - K; + uint8_t * bytes; + check_databuf(bch); + bytes = bch->databuf; + memset(bytes,0,ndatabytes); + for (k=0;k>3] |= mask; + } + return ndatabytes; +} + +/* + * + * */ +static void unpack_eccbits( struct bch_control *bch , uint8_t * ecc) +{ + int k; + uint8_t * ecc_bytes; + check_databuf(bch); + ecc_bytes = bch->databuf + ((bch->n - bch->ecc_bits)+7)/8; + // expand ecc bytes to bits + for (k=0;kecc_bits;++k) + ecc[k] = (ecc_bytes[k>>3] & (1<<(7-(k&7))))>0; +} + +static void pack_eccbits(struct bch_control *bch ,const uint8_t * ecc) +{ + int k; + uint8_t * ecc_bytes; + check_databuf(bch); + ecc_bytes = bch->databuf + ((bch->n - bch->ecc_bits)+7)/8; + // expand ecc bytes to bits + memset(ecc_bytes,0,bch->ecc_bytes); + for (k=0;kecc_bits;++k) { + int bit = (ecc[k]&1)!=0; // use only the LSB (can allow sloppy but nice feature of sending in ASCII '0' and '1') + uint8_t mask = (1<<(7-(k&7))); + if (bit) + ecc_bytes[k>>3] |= mask; + } +} + + +/** + * encodebits_bch - calculate BCH ecc parity of data + * @bch: BCH control structure + * @data: data bits to encode , length= bch->n - bch->ecc_bits + * @ecc: output ecc parity bits, length = bch->ecc_bits + * + * 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. + */ +void encodebits_bch(struct bch_control *bch, const uint8_t *data, uint8_t *ecc) +{ + int ndatabytes = pack_databuf(bch,data); + uint8_t * ecc_bytes = bch->databuf + ndatabytes; + memset(ecc_bytes,0,bch->ecc_bytes); + encode_bch(bch,bch->databuf,ndatabytes,ecc_bytes); + unpack_eccbits(bch,ecc); +} + +/** + * decodebits_bch - decode received codeword bits and find error locations + * @bch: BCH control structure + * @databits: received data, length = bch->n - bch->ecc_bits + * @recv_ecc_bits: received ecc, length = bch->ecc_bits + * @errloc: output array of error locations + * + * Returns: + * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if + * invalid parameters were provided + * + * if (errloc[i] < bch->n - bch->ecc_bits ), then + * databits[errloc[i]] is in error + * otherwise + * the i-th error is located in ecc (no need for data correction) + * + * Note that this function does not perform any data correction by itself, it + * merely indicates error locations. + */ +int decodebits_bch(struct bch_control *bch, const uint8_t *data, const uint8_t *recv_ecc, unsigned int *errloc) +{ + int nbytes; + int nerr; + + if ( (data==NULL) ||(recv_ecc==NULL)) { + return -EINVAL; // TODO handle the same calling conventions as decode_bch + } + + nbytes = pack_databuf(bch,data); + + pack_eccbits(bch,recv_ecc); + + nerr = decode_bch(bch, bch->databuf, nbytes, bch->databuf + nbytes,NULL,NULL,errloc); + if (nerr>0) { + const int K = bch->n - bch->ecc_bits; + int nPad=((K+7)/8)*8 - K; + // correct the errloc positions + int k; + for (k=0;k>3) < len) + data[bi>>3] ^= (1<<(bi&7)); + } + +} + +/** + * correctbits_bch - correct error locations as found in decodebits_bch + * @bch,@databits,@errloc: same as a previous call to decodebits_bch + * @nerr: returned from decodebits_bch + */ +void correctbits_bch(struct bch_control *bch, uint8_t *databits, unsigned int *errloc, int nerr) +{ + const int m = bch->n - bch->ecc_bits; + int i; + for (i=0;i + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. +*/ +#ifndef _BCH_H +#define _BCH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 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 + */ +struct bch_control { + unsigned int m; + unsigned int n; + unsigned int t; + unsigned int ecc_bits; + unsigned int ecc_bytes; +/* private: */ + uint16_t *a_pow_tab; + uint16_t *a_log_tab; + uint32_t *mod8_tab; + uint32_t *ecc_buf; + uint32_t *ecc_buf2; + unsigned int *xi_tab; + unsigned int *syn; + int *cache; + struct gf_poly *elp; + struct gf_poly *poly_2t[4]; + uint8_t *databuf; +}; + +struct bch_control *init_bch(int m, int t, unsigned int prim_poly); + +void free_bch(struct bch_control *bch); + +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc); + +void encodebits_bch(struct bch_control *bch, const uint8_t *data, uint8_t *ecc); + +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc); + +int decodebits_bch(struct bch_control *bch, const uint8_t *data, + const uint8_t *recv_ecc, unsigned int *errloc); + + +void correct_bch(struct bch_control *bch, uint8_t *data,unsigned int len, unsigned int *errloc, int nerr); + +void correctbits_bch(struct bch_control *bch, uint8_t *databits, unsigned int *errloc, int nerr); + + +#ifdef __cplusplus +} +#endif + +#endif /* _BCH_H */ diff --git a/wrapper.h b/wrapper.h index 095f15d..e1cf851 100644 --- a/wrapper.h +++ b/wrapper.h @@ -1,2 +1 @@ -#include -#include "bch.h" +#include "src/bch/bch.h" -- cgit v1.3.1 From 9e898bd49a954c5cf51bc0d35ad2b20e13b8fb87 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 16:21:54 +0200 Subject: Add GPLv2 license --- LICENSE.md | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 + 2 files changed, 343 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md index 8afb14b..26403dc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # bchlib Rust bindings for BCH encoding/decoding library + +## License + +[GPLv2](LICENSE.md) -- cgit v1.3.1 From 6c74f234cbcabf93edfe45b2d26c9bf3769c327b Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 16:24:48 +0200 Subject: Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26403dc..8b259bc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # bchlib -Rust bindings for BCH encoding/decoding library +Rust bindings for BCH encoding/decoding library, based on the [bch_codec](https://github.com/mborgerding/bch_codec) fork. ## License -- cgit v1.3.1 From ed3462d84c560bfdf9904b319775d8a038dad82d Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 16:25:33 +0200 Subject: Remove useless file --- src/bch.c | 1384 ------------------------------------------------------------- 1 file changed, 1384 deletions(-) delete mode 100644 src/bch.c diff --git a/src/bch.c b/src/bch.c deleted file mode 100644 index 5db6d3a..0000000 --- a/src/bch.c +++ /dev/null @@ -1,1384 +0,0 @@ -/* - * Generic binary BCH encoding/decoding library - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program 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. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright © 2011 Parrot S.A. - * - * Author: Ivan Djelic - * - * Description: - * - * This library provides runtime configurable encoding/decoding of binary - * Bose-Chaudhuri-Hocquenghem (BCH) codes. - * - * Call init_bch to get a pointer to a newly allocated bch_control structure for - * the given m (Galois field order), t (error correction capability) and - * (optional) primitive polynomial parameters. - * - * Call encode_bch to compute and store ecc parity bytes to a given buffer. - * Call decode_bch to detect and locate errors in received data. - * - * On systems supporting hw BCH features, intermediate results may be provided - * to decode_bch in order to skip certain steps. See decode_bch() documentation - * for details. - * - * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of - * parameters m and t; thus allowing extra compiler optimizations and providing - * better (up to 2x) encoding performance. Using this option makes sense when - * (m,t) are fixed and known in advance, e.g. when using BCH error correction - * on a particular NAND flash device. - * - * Algorithmic details: - * - * Encoding is performed by processing 32 input bits in parallel, using 4 - * remainder lookup tables. - * - * The final stage of decoding involves the following internal steps: - * a. Syndrome computation - * b. Error locator polynomial computation using Berlekamp-Massey algorithm - * c. Error locator root finding (by far the most expensive step) - * - * In this implementation, step c is not performed using the usual Chien search. - * Instead, an alternative approach described in [1] is used. It consists in - * factoring the error locator polynomial using the Berlekamp Trace algorithm - * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial - * solving techniques [2] are used. The resulting algorithm, called BTZ, yields - * much better performance than Chien search for usual (m,t) values (typically - * m >= 13, t < 32, see [1]). - * - * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields - * of characteristic 2, in: Western European Workshop on Research in Cryptology - * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. - * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over - * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_BCH_CONST_PARAMS) -#define GF_M(_p) (CONFIG_BCH_CONST_M) -#define GF_T(_p) (CONFIG_BCH_CONST_T) -#define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) -#define BCH_MAX_M (CONFIG_BCH_CONST_M) -#define BCH_MAX_T (CONFIG_BCH_CONST_T) -#else -#define GF_M(_p) ((_p)->m) -#define GF_T(_p) ((_p)->t) -#define GF_N(_p) ((_p)->n) -#define BCH_MAX_M 15 /* 2KB */ -#define BCH_MAX_T 64 /* 64 bit correction */ -#endif - -#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) -#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) - -#define BCH_ECC_MAX_WORDS DIV_ROUND_UP(BCH_MAX_M * BCH_MAX_T, 32) - -#ifndef dbg -#define dbg(_fmt, args...) do {} while (0) -#endif - -/* - * represent a polynomial over GF(2^m) - */ -struct gf_poly { - unsigned int deg; /* polynomial degree */ - unsigned int c[0]; /* polynomial terms */ -}; - -/* given its degree, compute a polynomial size in bytes */ -#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) - -/* polynomial of degree 1 */ -struct gf_poly_deg1 { - struct gf_poly poly; - unsigned int c[2]; -}; - -/* - * same as encode_bch(), but process input data one byte at a time - */ -static void encode_bch_unaligned(struct bch_control *bch, - const unsigned char *data, unsigned int len, - uint32_t *ecc) -{ - int i; - const uint32_t *p; - const int l = BCH_ECC_WORDS(bch)-1; - - while (len--) { - p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); - - for (i = 0; i < l; i++) - ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); - - ecc[l] = (ecc[l] << 8)^(*p); - } -} - -/* - * convert ecc bytes to aligned, zero-padded 32-bit ecc words - */ -static void load_ecc8(struct bch_control *bch, uint32_t *dst, - const uint8_t *src) -{ - uint8_t pad[4] = {0, 0, 0, 0}; - unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; - - for (i = 0; i < nwords; i++, src += 4) - dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; - - memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); - dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; -} - -/* - * convert 32-bit ecc words to ecc bytes - */ -static void store_ecc8(struct bch_control *bch, uint8_t *dst, - const uint32_t *src) -{ - uint8_t pad[4]; - unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; - - for (i = 0; i < nwords; i++) { - *dst++ = (src[i] >> 24); - *dst++ = (src[i] >> 16) & 0xff; - *dst++ = (src[i] >> 8) & 0xff; - *dst++ = (src[i] >> 0) & 0xff; - } - pad[0] = (src[nwords] >> 24); - pad[1] = (src[nwords] >> 16) & 0xff; - pad[2] = (src[nwords] >> 8) & 0xff; - pad[3] = (src[nwords] >> 0) & 0xff; - memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); -} - -/** - * 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. - */ -void encode_bch(struct bch_control *bch, const uint8_t *data, - unsigned int len, uint8_t *ecc) -{ - const unsigned int l = BCH_ECC_WORDS(bch)-1; - unsigned int i, mlen; - unsigned long m; - uint32_t w, r[BCH_ECC_MAX_WORDS]; - const size_t r_bytes = BCH_ECC_WORDS(bch) * sizeof(*r); - const uint32_t * const tab0 = bch->mod8_tab; - const uint32_t * const tab1 = tab0 + 256*(l+1); - const uint32_t * const tab2 = tab1 + 256*(l+1); - const uint32_t * const tab3 = tab2 + 256*(l+1); - const uint32_t *pdata, *p0, *p1, *p2, *p3; - - if (WARN_ON(r_bytes > sizeof(r))) - return; - - if (ecc) { - /* load ecc parity bytes into internal 32-bit buffer */ - load_ecc8(bch, bch->ecc_buf, ecc); - } else { - memset(bch->ecc_buf, 0, r_bytes); - } - - /* process first unaligned data bytes */ - m = ((unsigned long)data) & 3; - if (m) { - mlen = (len < (4-m)) ? len : 4-m; - encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); - data += mlen; - len -= mlen; - } - - /* process 32-bit aligned data words */ - pdata = (uint32_t *)data; - mlen = len/4; - data += 4*mlen; - len -= 4*mlen; - memcpy(r, bch->ecc_buf, r_bytes); - - /* - * split each 32-bit word into 4 polynomials of weight 8 as follows: - * - * 31 ...24 23 ...16 15 ... 8 7 ... 0 - * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt - * tttttttt mod g = r0 (precomputed) - * zzzzzzzz 00000000 mod g = r1 (precomputed) - * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) - * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) - * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 - */ - while (mlen--) { - /* input data is read in big-endian format */ - w = r[0]^cpu_to_be32(*pdata++); - p0 = tab0 + (l+1)*((w >> 0) & 0xff); - p1 = tab1 + (l+1)*((w >> 8) & 0xff); - p2 = tab2 + (l+1)*((w >> 16) & 0xff); - p3 = tab3 + (l+1)*((w >> 24) & 0xff); - - for (i = 0; i < l; i++) - r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; - - r[l] = p0[l]^p1[l]^p2[l]^p3[l]; - } - memcpy(bch->ecc_buf, r, r_bytes); - - /* process last unaligned bytes */ - if (len) - encode_bch_unaligned(bch, data, len, bch->ecc_buf); - - /* store ecc parity bytes into original parity buffer */ - if (ecc) - store_ecc8(bch, ecc, bch->ecc_buf); -} -EXPORT_SYMBOL_GPL(encode_bch); - -static inline int modulo(struct bch_control *bch, unsigned int v) -{ - const unsigned int n = GF_N(bch); - while (v >= n) { - v -= n; - v = (v & n) + (v >> GF_M(bch)); - } - return v; -} - -/* - * shorter and faster modulo function, only works when v < 2N. - */ -static inline int mod_s(struct bch_control *bch, unsigned int v) -{ - const unsigned int n = GF_N(bch); - return (v < n) ? v : v-n; -} - -static inline int deg(unsigned int poly) -{ - /* polynomial degree is the most-significant bit index */ - return fls(poly)-1; -} - -static inline int parity(unsigned int x) -{ - /* - * public domain code snippet, lifted from - * http://www-graphics.stanford.edu/~seander/bithacks.html - */ - x ^= x >> 1; - x ^= x >> 2; - x = (x & 0x11111111U) * 0x11111111U; - return (x >> 28) & 1; -} - -/* Galois field basic operations: multiply, divide, inverse, etc. */ - -static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, - unsigned int b) -{ - return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ - bch->a_log_tab[b])] : 0; -} - -static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) -{ - return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; -} - -static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, - unsigned int b) -{ - return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ - GF_N(bch)-bch->a_log_tab[b])] : 0; -} - -static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) -{ - return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; -} - -static inline unsigned int a_pow(struct bch_control *bch, int i) -{ - return bch->a_pow_tab[modulo(bch, i)]; -} - -static inline int a_log(struct bch_control *bch, unsigned int x) -{ - return bch->a_log_tab[x]; -} - -static inline int a_ilog(struct bch_control *bch, unsigned int x) -{ - return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); -} - -/* - * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t - */ -static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, - unsigned int *syn) -{ - int i, j, s; - unsigned int m; - uint32_t poly; - const int t = GF_T(bch); - - s = bch->ecc_bits; - - /* make sure extra bits in last ecc word are cleared */ - m = ((unsigned int)s) & 31; - if (m) - ecc[s/32] &= ~((1u << (32-m))-1); - memset(syn, 0, 2*t*sizeof(*syn)); - - /* compute v(a^j) for j=1 .. 2t-1 */ - do { - poly = *ecc++; - s -= 32; - while (poly) { - i = deg(poly); - for (j = 0; j < 2*t; j += 2) - syn[j] ^= a_pow(bch, (j+1)*(i+s)); - - poly ^= (1 << i); - } - } while (s > 0); - - /* v(a^(2j)) = v(a^j)^2 */ - for (j = 0; j < t; j++) - syn[2*j+1] = gf_sqr(bch, syn[j]); -} - -static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) -{ - memcpy(dst, src, GF_POLY_SZ(src->deg)); -} - -static int compute_error_locator_polynomial(struct bch_control *bch, - const unsigned int *syn) -{ - const unsigned int t = GF_T(bch); - const unsigned int n = GF_N(bch); - unsigned int i, j, tmp, l, pd = 1, d = syn[0]; - struct gf_poly *elp = bch->elp; - struct gf_poly *pelp = bch->poly_2t[0]; - struct gf_poly *elp_copy = bch->poly_2t[1]; - int k, pp = -1; - - memset(pelp, 0, GF_POLY_SZ(2*t)); - memset(elp, 0, GF_POLY_SZ(2*t)); - - pelp->deg = 0; - pelp->c[0] = 1; - elp->deg = 0; - elp->c[0] = 1; - - /* use simplified binary Berlekamp-Massey algorithm */ - for (i = 0; (i < t) && (elp->deg <= t); i++) { - if (d) { - k = 2*i-pp; - gf_poly_copy(elp_copy, elp); - /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ - tmp = a_log(bch, d)+n-a_log(bch, pd); - for (j = 0; j <= pelp->deg; j++) { - if (pelp->c[j]) { - l = a_log(bch, pelp->c[j]); - elp->c[j+k] ^= a_pow(bch, tmp+l); - } - } - /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ - tmp = pelp->deg+k; - if (tmp > elp->deg) { - elp->deg = tmp; - gf_poly_copy(pelp, elp_copy); - pd = d; - pp = 2*i; - } - } - /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ - if (i < t-1) { - d = syn[2*i+2]; - for (j = 1; j <= elp->deg; j++) - d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); - } - } - dbg("elp=%s\n", gf_poly_str(elp)); - return (elp->deg > t) ? -1 : (int)elp->deg; -} - -/* - * solve a m x m linear system in GF(2) with an expected number of solutions, - * and return the number of found solutions - */ -static int solve_linear_system(struct bch_control *bch, unsigned int *rows, - unsigned int *sol, int nsol) -{ - const int m = GF_M(bch); - unsigned int tmp, mask; - int rem, c, r, p, k, param[BCH_MAX_M]; - - k = 0; - mask = 1 << m; - - /* Gaussian elimination */ - for (c = 0; c < m; c++) { - rem = 0; - p = c-k; - /* find suitable row for elimination */ - for (r = p; r < m; r++) { - if (rows[r] & mask) { - if (r != p) { - tmp = rows[r]; - rows[r] = rows[p]; - rows[p] = tmp; - } - rem = r+1; - break; - } - } - if (rem) { - /* perform elimination on remaining rows */ - tmp = rows[p]; - for (r = rem; r < m; r++) { - if (rows[r] & mask) - rows[r] ^= tmp; - } - } else { - /* elimination not needed, store defective row index */ - param[k++] = c; - } - mask >>= 1; - } - /* rewrite system, inserting fake parameter rows */ - if (k > 0) { - p = k; - for (r = m-1; r >= 0; r--) { - if ((r > m-1-k) && rows[r]) - /* system has no solution */ - return 0; - - rows[r] = (p && (r == param[p-1])) ? - p--, 1u << (m-r) : rows[r-p]; - } - } - - if (nsol != (1 << k)) - /* unexpected number of solutions */ - return 0; - - for (p = 0; p < nsol; p++) { - /* set parameters for p-th solution */ - for (c = 0; c < k; c++) - rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); - - /* compute unique solution */ - tmp = 0; - for (r = m-1; r >= 0; r--) { - mask = rows[r] & (tmp|1); - tmp |= parity(mask) << (m-r); - } - sol[p] = tmp >> 1; - } - return nsol; -} - -/* - * this function builds and solves a linear system for finding roots of a degree - * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). - */ -static int find_affine4_roots(struct bch_control *bch, unsigned int a, - unsigned int b, unsigned int c, - unsigned int *roots) -{ - int i, j, k; - const int m = GF_M(bch); - unsigned int mask = 0xff, t, rows[16] = {0,}; - - j = a_log(bch, b); - k = a_log(bch, a); - rows[0] = c; - - /* buid linear system to solve X^4+aX^2+bX+c = 0 */ - for (i = 0; i < m; i++) { - rows[i+1] = bch->a_pow_tab[4*i]^ - (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ - (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); - j++; - k += 2; - } - /* - * transpose 16x16 matrix before passing it to linear solver - * warning: this code assumes m < 16 - */ - for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { - for (k = 0; k < 16; k = (k+j+1) & ~j) { - t = ((rows[k] >> j)^rows[k+j]) & mask; - rows[k] ^= (t << j); - rows[k+j] ^= t; - } - } - return solve_linear_system(bch, rows, roots, 4); -} - -/* - * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) - */ -static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int n = 0; - - if (poly->c[0]) - /* poly[X] = bX+c with c!=0, root=c/b */ - roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ - bch->a_log_tab[poly->c[1]]); - return n; -} - -/* - * compute roots of a degree 2 polynomial over GF(2^m) - */ -static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int n = 0, i, l0, l1, l2; - unsigned int u, v, r; - - if (poly->c[0] && poly->c[1]) { - - l0 = bch->a_log_tab[poly->c[0]]; - l1 = bch->a_log_tab[poly->c[1]]; - l2 = bch->a_log_tab[poly->c[2]]; - - /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ - u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); - /* - * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): - * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = - * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) - * i.e. r and r+1 are roots iff Tr(u)=0 - */ - r = 0; - v = u; - while (v) { - i = deg(v); - r ^= bch->xi_tab[i]; - v ^= (1 << i); - } - /* verify root */ - if ((gf_sqr(bch, r)^r) == u) { - /* reverse z=a/bX transformation and compute log(1/r) */ - roots[n++] = modulo(bch, 2*GF_N(bch)-l1- - bch->a_log_tab[r]+l2); - roots[n++] = modulo(bch, 2*GF_N(bch)-l1- - bch->a_log_tab[r^1]+l2); - } - } - return n; -} - -/* - * compute roots of a degree 3 polynomial over GF(2^m) - */ -static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int i, n = 0; - unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; - - if (poly->c[0]) { - /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ - e3 = poly->c[3]; - c2 = gf_div(bch, poly->c[0], e3); - b2 = gf_div(bch, poly->c[1], e3); - a2 = gf_div(bch, poly->c[2], e3); - - /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ - c = gf_mul(bch, a2, c2); /* c = a2c2 */ - b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ - a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ - - /* find the 4 roots of this affine polynomial */ - if (find_affine4_roots(bch, a, b, c, tmp) == 4) { - /* remove a2 from final list of roots */ - for (i = 0; i < 4; i++) { - if (tmp[i] != a2) - roots[n++] = a_ilog(bch, tmp[i]); - } - } - } - return n; -} - -/* - * compute roots of a degree 4 polynomial over GF(2^m) - */ -static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int i, l, n = 0; - unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; - - if (poly->c[0] == 0) - return 0; - - /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ - e4 = poly->c[4]; - d = gf_div(bch, poly->c[0], e4); - c = gf_div(bch, poly->c[1], e4); - b = gf_div(bch, poly->c[2], e4); - a = gf_div(bch, poly->c[3], e4); - - /* use Y=1/X transformation to get an affine polynomial */ - if (a) { - /* first, eliminate cX by using z=X+e with ae^2+c=0 */ - if (c) { - /* compute e such that e^2 = c/a */ - f = gf_div(bch, c, a); - l = a_log(bch, f); - l += (l & 1) ? GF_N(bch) : 0; - e = a_pow(bch, l/2); - /* - * use transformation z=X+e: - * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d - * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d - * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d - * z^4 + az^3 + b'z^2 + d' - */ - d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; - b = gf_mul(bch, a, e)^b; - } - /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ - if (d == 0) - /* assume all roots have multiplicity 1 */ - return 0; - - c2 = gf_inv(bch, d); - b2 = gf_div(bch, a, d); - a2 = gf_div(bch, b, d); - } else { - /* polynomial is already affine */ - c2 = d; - b2 = c; - a2 = b; - } - /* find the 4 roots of this affine polynomial */ - if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { - for (i = 0; i < 4; i++) { - /* post-process roots (reverse transformations) */ - f = a ? gf_inv(bch, roots[i]) : roots[i]; - roots[i] = a_ilog(bch, f^e); - } - n = 4; - } - return n; -} - -/* - * build monic, log-based representation of a polynomial - */ -static void gf_poly_logrep(struct bch_control *bch, - const struct gf_poly *a, int *rep) -{ - int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); - - /* represent 0 values with -1; warning, rep[d] is not set to 1 */ - for (i = 0; i < d; i++) - rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; -} - -/* - * compute polynomial Euclidean division remainder in GF(2^m)[X] - */ -static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, - const struct gf_poly *b, int *rep) -{ - int la, p, m; - unsigned int i, j, *c = a->c; - const unsigned int d = b->deg; - - if (a->deg < d) - return; - - /* reuse or compute log representation of denominator */ - if (!rep) { - rep = bch->cache; - gf_poly_logrep(bch, b, rep); - } - - for (j = a->deg; j >= d; j--) { - if (c[j]) { - la = a_log(bch, c[j]); - p = j-d; - for (i = 0; i < d; i++, p++) { - m = rep[i]; - if (m >= 0) - c[p] ^= bch->a_pow_tab[mod_s(bch, - m+la)]; - } - } - } - a->deg = d-1; - while (!c[a->deg] && a->deg) - a->deg--; -} - -/* - * compute polynomial Euclidean division quotient in GF(2^m)[X] - */ -static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, - const struct gf_poly *b, struct gf_poly *q) -{ - if (a->deg >= b->deg) { - q->deg = a->deg-b->deg; - /* compute a mod b (modifies a) */ - gf_poly_mod(bch, a, b, NULL); - /* quotient is stored in upper part of polynomial a */ - memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); - } else { - q->deg = 0; - q->c[0] = 0; - } -} - -/* - * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] - */ -static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, - struct gf_poly *b) -{ - struct gf_poly *tmp; - - dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); - - if (a->deg < b->deg) { - tmp = b; - b = a; - a = tmp; - } - - while (b->deg > 0) { - gf_poly_mod(bch, a, b, NULL); - tmp = b; - b = a; - a = tmp; - } - - dbg("%s\n", gf_poly_str(a)); - - return a; -} - -/* - * Given a polynomial f and an integer k, compute Tr(a^kX) mod f - * This is used in Berlekamp Trace algorithm for splitting polynomials - */ -static void compute_trace_bk_mod(struct bch_control *bch, int k, - const struct gf_poly *f, struct gf_poly *z, - struct gf_poly *out) -{ - const int m = GF_M(bch); - int i, j; - - /* z contains z^2j mod f */ - z->deg = 1; - z->c[0] = 0; - z->c[1] = bch->a_pow_tab[k]; - - out->deg = 0; - memset(out, 0, GF_POLY_SZ(f->deg)); - - /* compute f log representation only once */ - gf_poly_logrep(bch, f, bch->cache); - - for (i = 0; i < m; i++) { - /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ - for (j = z->deg; j >= 0; j--) { - out->c[j] ^= z->c[j]; - z->c[2*j] = gf_sqr(bch, z->c[j]); - z->c[2*j+1] = 0; - } - if (z->deg > out->deg) - out->deg = z->deg; - - if (i < m-1) { - z->deg *= 2; - /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ - gf_poly_mod(bch, z, f, bch->cache); - } - } - while (!out->c[out->deg] && out->deg) - out->deg--; - - dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); -} - -/* - * factor a polynomial using Berlekamp Trace algorithm (BTA) - */ -static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, - struct gf_poly **g, struct gf_poly **h) -{ - struct gf_poly *f2 = bch->poly_2t[0]; - struct gf_poly *q = bch->poly_2t[1]; - struct gf_poly *tk = bch->poly_2t[2]; - struct gf_poly *z = bch->poly_2t[3]; - struct gf_poly *gcd; - - dbg("factoring %s...\n", gf_poly_str(f)); - - *g = f; - *h = NULL; - - /* tk = Tr(a^k.X) mod f */ - compute_trace_bk_mod(bch, k, f, z, tk); - - if (tk->deg > 0) { - /* compute g = gcd(f, tk) (destructive operation) */ - gf_poly_copy(f2, f); - gcd = gf_poly_gcd(bch, f2, tk); - if (gcd->deg < f->deg) { - /* compute h=f/gcd(f,tk); this will modify f and q */ - gf_poly_div(bch, f, gcd, q); - /* store g and h in-place (clobbering f) */ - *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; - gf_poly_copy(*g, gcd); - gf_poly_copy(*h, q); - } - } -} - -/* - * find roots of a polynomial, using BTZ algorithm; see the beginning of this - * file for details - */ -static int find_poly_roots(struct bch_control *bch, unsigned int k, - struct gf_poly *poly, unsigned int *roots) -{ - int cnt; - struct gf_poly *f1, *f2; - - switch (poly->deg) { - /* handle low degree polynomials with ad hoc techniques */ - case 1: - cnt = find_poly_deg1_roots(bch, poly, roots); - break; - case 2: - cnt = find_poly_deg2_roots(bch, poly, roots); - break; - case 3: - cnt = find_poly_deg3_roots(bch, poly, roots); - break; - case 4: - cnt = find_poly_deg4_roots(bch, poly, roots); - break; - default: - /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ - cnt = 0; - if (poly->deg && (k <= GF_M(bch))) { - factor_polynomial(bch, k, poly, &f1, &f2); - if (f1) - cnt += find_poly_roots(bch, k+1, f1, roots); - if (f2) - cnt += find_poly_roots(bch, k+1, f2, roots+cnt); - } - break; - } - return cnt; -} - -#if defined(USE_CHIEN_SEARCH) -/* - * exhaustive root search (Chien) implementation - not used, included only for - * reference/comparison tests - */ -static int chien_search(struct bch_control *bch, unsigned int len, - struct gf_poly *p, unsigned int *roots) -{ - int m; - unsigned int i, j, syn, syn0, count = 0; - const unsigned int k = 8*len+bch->ecc_bits; - - /* use a log-based representation of polynomial */ - gf_poly_logrep(bch, p, bch->cache); - bch->cache[p->deg] = 0; - syn0 = gf_div(bch, p->c[0], p->c[p->deg]); - - for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { - /* compute elp(a^i) */ - for (j = 1, syn = syn0; j <= p->deg; j++) { - m = bch->cache[j]; - if (m >= 0) - syn ^= a_pow(bch, m+j*i); - } - if (syn == 0) { - roots[count++] = GF_N(bch)-i; - if (count == p->deg) - break; - } - } - return (count == p->deg) ? count : 0; -} -#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) -#endif /* USE_CHIEN_SEARCH */ - -/** - * 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. - */ -int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, - const uint8_t *recv_ecc, const uint8_t *calc_ecc, - const unsigned int *syn, unsigned int *errloc) -{ - const unsigned int ecc_words = BCH_ECC_WORDS(bch); - unsigned int nbits; - int i, err, nroots; - uint32_t sum; - - /* sanity check: make sure data length can be handled */ - if (8*len > (bch->n-bch->ecc_bits)) - return -EINVAL; - - /* if caller does not provide syndromes, compute them */ - if (!syn) { - if (!calc_ecc) { - /* compute received data ecc into an internal buffer */ - if (!data || !recv_ecc) - return -EINVAL; - encode_bch(bch, data, len, NULL); - } else { - /* load provided calculated ecc */ - load_ecc8(bch, bch->ecc_buf, calc_ecc); - } - /* load received ecc or assume it was XORed in calc_ecc */ - if (recv_ecc) { - load_ecc8(bch, bch->ecc_buf2, recv_ecc); - /* XOR received and calculated ecc */ - for (i = 0, sum = 0; i < (int)ecc_words; i++) { - bch->ecc_buf[i] ^= bch->ecc_buf2[i]; - sum |= bch->ecc_buf[i]; - } - if (!sum) - /* no error found */ - return 0; - } - compute_syndromes(bch, bch->ecc_buf, bch->syn); - syn = bch->syn; - } - - err = compute_error_locator_polynomial(bch, syn); - if (err > 0) { - nroots = find_poly_roots(bch, 1, bch->elp, errloc); - if (err != nroots) - err = -1; - } - if (err > 0) { - /* post-process raw error locations for easier correction */ - nbits = (len*8)+bch->ecc_bits; - for (i = 0; i < err; i++) { - if (errloc[i] >= nbits) { - err = -1; - break; - } - errloc[i] = nbits-1-errloc[i]; - errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); - } - } - return (err >= 0) ? err : -EBADMSG; -} -EXPORT_SYMBOL_GPL(decode_bch); - -/* - * generate Galois field lookup tables - */ -static int build_gf_tables(struct bch_control *bch, unsigned int poly) -{ - unsigned int i, x = 1; - const unsigned int k = 1 << deg(poly); - - /* primitive polynomial must be of degree m */ - if (k != (1u << GF_M(bch))) - return -1; - - for (i = 0; i < GF_N(bch); i++) { - bch->a_pow_tab[i] = x; - bch->a_log_tab[x] = i; - if (i && (x == 1)) - /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; - bch->a_log_tab[0] = 0; - - return 0; -} - -/* - * compute generator polynomial remainder tables for fast encoding - */ -static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) -{ - int i, j, b, d; - uint32_t data, hi, lo, *tab; - const int l = BCH_ECC_WORDS(bch); - const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); - const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); - - memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); - - for (i = 0; i < 256; i++) { - /* p(X)=i is a small polynomial of weight <= 8 */ - for (b = 0; b < 4; b++) { - /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ - tab = bch->mod8_tab + (b*256+i)*l; - data = i << (8*b); - while (data) { - d = deg(data); - /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ - data ^= g[0] >> (31-d); - for (j = 0; j < ecclen; j++) { - hi = (d < 31) ? g[j] << (d+1) : 0; - lo = (j+1 < plen) ? - g[j+1] >> (31-d) : 0; - tab[j] ^= hi|lo; - } - } - } - } -} - -/* - * build a base for factoring degree 2 polynomials - */ -static int build_deg2_base(struct bch_control *bch) -{ - const int m = GF_M(bch); - int i, j, r; - unsigned int sum, x, y, remaining, ak = 0, xi[BCH_MAX_M]; - - /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ - for (i = 0; i < m; i++) { - for (j = 0, sum = 0; j < m; j++) - sum ^= a_pow(bch, i*(1 << j)); - - if (sum) { - ak = bch->a_pow_tab[i]; - break; - } - } - /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ - remaining = m; - memset(xi, 0, sizeof(xi)); - - for (x = 0; (x <= GF_N(bch)) && remaining; x++) { - y = gf_sqr(bch, x)^x; - for (i = 0; i < 2; i++) { - r = a_log(bch, y); - if (y && (r < m) && !xi[r]) { - bch->xi_tab[r] = x; - xi[r] = 1; - remaining--; - dbg("x%d = %x\n", r, x); - break; - } - y ^= ak; - } - } - /* should not happen but check anyway */ - return remaining ? -1 : 0; -} - -static void *bch_alloc(size_t size, int *err) -{ - void *ptr; - - ptr = kmalloc(size, GFP_KERNEL); - if (ptr == NULL) - *err = 1; - return ptr; -} - -/* - * compute generator polynomial for given (m,t) parameters. - */ -static uint32_t *compute_generator_polynomial(struct bch_control *bch) -{ - const unsigned int m = GF_M(bch); - const unsigned int t = GF_T(bch); - int n, err = 0; - unsigned int i, j, nbits, r, word, *roots; - struct gf_poly *g; - uint32_t *genpoly; - - g = bch_alloc(GF_POLY_SZ(m*t), &err); - roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); - genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); - - if (err) { - kfree(genpoly); - genpoly = NULL; - goto finish; - } - - /* enumerate all roots of g(X) */ - memset(roots , 0, (bch->n+1)*sizeof(*roots)); - for (i = 0; i < t; i++) { - for (j = 0, r = 2*i+1; j < m; j++) { - roots[r] = 1; - r = mod_s(bch, 2*r); - } - } - /* build generator polynomial g(X) */ - g->deg = 0; - g->c[0] = 1; - for (i = 0; i < GF_N(bch); i++) { - if (roots[i]) { - /* multiply g(X) by (X+root) */ - r = bch->a_pow_tab[i]; - g->c[g->deg+1] = 1; - for (j = g->deg; j > 0; j--) - g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; - - g->c[0] = gf_mul(bch, g->c[0], r); - g->deg++; - } - } - /* store left-justified binary representation of g(X) */ - n = g->deg+1; - i = 0; - - while (n > 0) { - nbits = (n > 32) ? 32 : n; - for (j = 0, word = 0; j < nbits; j++) { - if (g->c[n-1-j]) - word |= 1u << (31-j); - } - genpoly[i++] = word; - n -= nbits; - } - bch->ecc_bits = g->deg; - -finish: - kfree(g); - kfree(roots); - - return genpoly; -} - -/** - * 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. - */ -struct bch_control *init_bch(int m, int t, unsigned int prim_poly) -{ - int err = 0; - unsigned int i, words; - uint32_t *genpoly; - struct bch_control *bch = NULL; - - const int min_m = 5; - - /* default primitive polynomials */ - static const unsigned int prim_poly_tab[] = { - 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, - 0x402b, 0x8003, - }; - -#if defined(CONFIG_BCH_CONST_PARAMS) - if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { - printk(KERN_ERR "bch encoder/decoder was configured to support " - "parameters m=%d, t=%d only!\n", - CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); - goto fail; - } -#endif - if ((m < min_m) || (m > BCH_MAX_M)) - /* - * values of m greater than 15 are not currently supported; - * supporting m > 15 would require changing table base type - * (uint16_t) and a small patch in matrix transposition - */ - goto fail; - - if (t > BCH_MAX_T) - /* - * we can support larger than 64 bits if necessary, at the - * cost of higher stack usage. - */ - goto fail; - - /* sanity checks */ - if ((t < 1) || (m*t >= ((1 << m)-1))) - /* invalid t value */ - goto fail; - - /* select a primitive polynomial for generating GF(2^m) */ - if (prim_poly == 0) - prim_poly = prim_poly_tab[m-min_m]; - - bch = kzalloc(sizeof(*bch), GFP_KERNEL); - if (bch == NULL) - goto fail; - - bch->m = m; - bch->t = t; - bch->n = (1 << m)-1; - words = DIV_ROUND_UP(m*t, 32); - bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); - bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); - bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); - bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); - bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); - bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); - bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); - bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); - bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); - bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); - - for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) - bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); - - if (err) - goto fail; - - err = build_gf_tables(bch, prim_poly); - if (err) - goto fail; - - /* use generator polynomial for computing encoding tables */ - genpoly = compute_generator_polynomial(bch); - if (genpoly == NULL) - goto fail; - - build_mod8_tables(bch, genpoly); - kfree(genpoly); - - err = build_deg2_base(bch); - if (err) - goto fail; - - return bch; - -fail: - free_bch(bch); - return NULL; -} -EXPORT_SYMBOL_GPL(init_bch); - -/** - * free_bch - free the BCH control structure - * @bch: BCH control structure to release - */ -void free_bch(struct bch_control *bch) -{ - unsigned int i; - - if (bch) { - kfree(bch->a_pow_tab); - kfree(bch->a_log_tab); - kfree(bch->mod8_tab); - kfree(bch->ecc_buf); - kfree(bch->ecc_buf2); - kfree(bch->xi_tab); - kfree(bch->syn); - kfree(bch->cache); - kfree(bch->elp); - - for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) - kfree(bch->poly_2t[i]); - - kfree(bch); - } -} -EXPORT_SYMBOL_GPL(free_bch); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ivan Djelic "); -MODULE_DESCRIPTION("Binary BCH encoder/decoder"); -- cgit v1.3.1 From 281ad58525200611beb20e094fec583bb1bf38ff Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 16:29:14 +0200 Subject: Cleanup build process --- build.rs | 7 ++----- src/lib.rs | 6 ++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/build.rs b/build.rs index 08715f8..789917a 100644 --- a/build.rs +++ b/build.rs @@ -5,6 +5,8 @@ use std::env; use std::path::PathBuf; fn main() { + cc::Build::new().file("src/bch/bch.c"); + println!("cargo:rustc-link-lib=bch"); let bindings = bindgen::Builder::default() @@ -16,9 +18,4 @@ fn main() { bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); - - cc::Build::new() - .file("src/bch/bch.c") - .include("src/bch/bch.h") - .compile("bch"); } diff --git a/src/lib.rs b/src/lib.rs index 31e1bb2..4a01ab7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,9 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + #[cfg(test)] mod tests { #[test] -- cgit v1.3.1 From 8593e6cc865582e12a84ba59ea530bc7a1b6b933 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 16:38:17 +0200 Subject: Update README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 8b259bc..96e1c81 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ Rust bindings for BCH encoding/decoding library, based on the [bch_codec](https://github.com/mborgerding/bch_codec) fork. +## Build + +The usual: + +```bash +$ cargo build +$ cargo test +``` + ## License [GPLv2](LICENSE.md) -- cgit v1.3.1 From 2cda7faab3eab847947d5da85202d1fb7bfbc766 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Fri, 15 Mar 2019 16:39:47 +0200 Subject: Remove redundant wrapper.h --- build.rs | 2 +- wrapper.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 wrapper.h diff --git a/build.rs b/build.rs index 789917a..3ccb76d 100644 --- a/build.rs +++ b/build.rs @@ -10,7 +10,7 @@ fn main() { println!("cargo:rustc-link-lib=bch"); let bindings = bindgen::Builder::default() - .header("wrapper.h") + .header("src/bch/bch.h") .generate() .expect("Unable to generate bindings"); diff --git a/wrapper.h b/wrapper.h deleted file mode 100644 index e1cf851..0000000 --- a/wrapper.h +++ /dev/null @@ -1 +0,0 @@ -#include "src/bch/bch.h" -- cgit v1.3.1 From 77614da6736185a50b71713e4e0b3f7a7c4e840b Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Mon, 18 Mar 2019 14:16:49 +0200 Subject: Initial version of bchlib-sys --- .gitignore | 3 ++- Cargo.toml | 6 +++++- README.md | 4 ++-- build.rs | 4 +--- src/bch/bch | Bin 22976 -> 0 bytes src/lib.rs | 6 +++++- 6 files changed, 15 insertions(+), 8 deletions(-) delete mode 100644 src/bch/bch diff --git a/.gitignore b/.gitignore index 8b6b607..391c970 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/src/bch/bch /target **/*.rs.bk @@ -7,4 +8,4 @@ #/target #**/*.rs.bk -Cargo.lock \ No newline at end of file +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 7617876..035ef45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,12 @@ [package] -name = "bchlib" +name = "bchlib-sys" version = "0.1.0" authors = ["Yuval Adam <_@yuv.al>"] edition = "2018" +description = "Low-level Rust bindings for BCH encoding/decoding library, based on the bch_codec fork" +license = "GPL-2.0" +license-file = "LICENSE.md" +repository = "https://github.com/yuvadm/bchlib-sys" [build-dependencies] bindgen = "0.42.2" diff --git a/README.md b/README.md index 96e1c81..721e13b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# bchlib +# bchlib-sys -Rust bindings for BCH encoding/decoding library, based on the [bch_codec](https://github.com/mborgerding/bch_codec) fork. +Low-level Rust bindings for BCH encoding/decoding library, based on the [bch_codec](https://github.com/mborgerding/bch_codec) fork. ## Build diff --git a/build.rs b/build.rs index 3ccb76d..605fec2 100644 --- a/build.rs +++ b/build.rs @@ -5,9 +5,7 @@ use std::env; use std::path::PathBuf; fn main() { - cc::Build::new().file("src/bch/bch.c"); - - println!("cargo:rustc-link-lib=bch"); + cc::Build::new().file("src/bch/bch.c").compile("bch"); let bindings = bindgen::Builder::default() .header("src/bch/bch.h") diff --git a/src/bch/bch b/src/bch/bch deleted file mode 100644 index 5b438cd..0000000 Binary files a/src/bch/bch and /dev/null differ diff --git a/src/lib.rs b/src/lib.rs index 4a01ab7..47eaa43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,12 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); #[cfg(test)] mod tests { + use super::*; + #[test] fn it_works() { - assert_eq!(2 + 2, 4); + unsafe { + let _c = init_bch(5, 2, 37); + } } } -- cgit v1.3.1 From 98f905dc82c9af22f09f75f4b85d9a97eccd3f44 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Mon, 18 Mar 2019 14:30:32 +0200 Subject: Update README --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 721e13b..a78c041 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,20 @@ # bchlib-sys +![Crates.io](https://img.shields.io/crates/v/bchlib-sys.svg) + Low-level Rust bindings for BCH encoding/decoding library, based on the [bch_codec](https://github.com/mborgerding/bch_codec) fork. +The higher level library can be found at: https://github.com/yuvadm/bchlib + +## Usage + +Add the library to your `Cargo.toml`: + +``` +[dependencies] +bchlib-sys = "0.1.0" +``` + ## Build The usual: -- cgit v1.3.1 From 7477bc29cfb6305f5e48a5f11f28e1384ca9f15b Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Mon, 18 Mar 2019 17:59:04 +0200 Subject: Cleanup Cargo.toml and update README --- Cargo.toml | 1 - README.md | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 035ef45..e6762e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Yuval Adam <_@yuv.al>"] edition = "2018" description = "Low-level Rust bindings for BCH encoding/decoding library, based on the bch_codec fork" license = "GPL-2.0" -license-file = "LICENSE.md" repository = "https://github.com/yuvadm/bchlib-sys" [build-dependencies] diff --git a/README.md b/README.md index a78c041..1325e75 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ $ cargo build $ cargo test ``` +Note that due to usage of `bindgen` you will need `clang` to be installed on your system. + ## License [GPLv2](LICENSE.md) -- cgit v1.3.1 From 186154550428c336ff9419ec4ed9ae11fb19c939 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Mon, 27 May 2019 10:32:18 +0300 Subject: Move all bchlib-sys files into bchlib-sys/ --- .gitignore | 11 - Cargo.toml | 12 - LICENSE.md | 339 ----------- README.md | 31 - bchlib-sys/.gitignore | 11 + bchlib-sys/Cargo.toml | 12 + bchlib-sys/LICENSE.md | 339 +++++++++++ bchlib-sys/README.md | 31 + bchlib-sys/build.rs | 19 + bchlib-sys/src/bch/bch.c | 1523 ++++++++++++++++++++++++++++++++++++++++++++++ bchlib-sys/src/bch/bch.h | 99 +++ bchlib-sys/src/lib.rs | 17 + build.rs | 19 - src/bch/bch.c | 1523 ---------------------------------------------- src/bch/bch.h | 99 --- src/lib.rs | 17 - 16 files changed, 2051 insertions(+), 2051 deletions(-) delete mode 100644 .gitignore delete mode 100644 Cargo.toml delete mode 100644 LICENSE.md delete mode 100644 README.md create mode 100644 bchlib-sys/.gitignore create mode 100644 bchlib-sys/Cargo.toml create mode 100644 bchlib-sys/LICENSE.md create mode 100644 bchlib-sys/README.md create mode 100644 bchlib-sys/build.rs create mode 100644 bchlib-sys/src/bch/bch.c create mode 100644 bchlib-sys/src/bch/bch.h create mode 100644 bchlib-sys/src/lib.rs delete mode 100644 build.rs delete mode 100644 src/bch/bch.c delete mode 100644 src/bch/bch.h delete mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 391c970..0000000 --- a/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -/src/bch/bch -/target -**/*.rs.bk - -#Added by cargo -# -#already existing elements are commented out - -#/target -#**/*.rs.bk -Cargo.lock diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index e6762e5..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "bchlib-sys" -version = "0.1.0" -authors = ["Yuval Adam <_@yuv.al>"] -edition = "2018" -description = "Low-level Rust bindings for BCH encoding/decoding library, based on the bch_codec fork" -license = "GPL-2.0" -repository = "https://github.com/yuvadm/bchlib-sys" - -[build-dependencies] -bindgen = "0.42.2" -cc = "1.0" diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index d159169..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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 2 of the License, or - (at your option) any later version. - - This program 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. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/README.md b/README.md deleted file mode 100644 index 1325e75..0000000 --- a/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# bchlib-sys - -![Crates.io](https://img.shields.io/crates/v/bchlib-sys.svg) - -Low-level Rust bindings for BCH encoding/decoding library, based on the [bch_codec](https://github.com/mborgerding/bch_codec) fork. - -The higher level library can be found at: https://github.com/yuvadm/bchlib - -## Usage - -Add the library to your `Cargo.toml`: - -``` -[dependencies] -bchlib-sys = "0.1.0" -``` - -## Build - -The usual: - -```bash -$ cargo build -$ cargo test -``` - -Note that due to usage of `bindgen` you will need `clang` to be installed on your system. - -## License - -[GPLv2](LICENSE.md) diff --git a/bchlib-sys/.gitignore b/bchlib-sys/.gitignore new file mode 100644 index 0000000..391c970 --- /dev/null +++ b/bchlib-sys/.gitignore @@ -0,0 +1,11 @@ +/src/bch/bch +/target +**/*.rs.bk + +#Added by cargo +# +#already existing elements are commented out + +#/target +#**/*.rs.bk +Cargo.lock diff --git a/bchlib-sys/Cargo.toml b/bchlib-sys/Cargo.toml new file mode 100644 index 0000000..e6762e5 --- /dev/null +++ b/bchlib-sys/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bchlib-sys" +version = "0.1.0" +authors = ["Yuval Adam <_@yuv.al>"] +edition = "2018" +description = "Low-level Rust bindings for BCH encoding/decoding library, based on the bch_codec fork" +license = "GPL-2.0" +repository = "https://github.com/yuvadm/bchlib-sys" + +[build-dependencies] +bindgen = "0.42.2" +cc = "1.0" diff --git a/bchlib-sys/LICENSE.md b/bchlib-sys/LICENSE.md new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/bchlib-sys/LICENSE.md @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/bchlib-sys/README.md b/bchlib-sys/README.md new file mode 100644 index 0000000..1325e75 --- /dev/null +++ b/bchlib-sys/README.md @@ -0,0 +1,31 @@ +# bchlib-sys + +![Crates.io](https://img.shields.io/crates/v/bchlib-sys.svg) + +Low-level Rust bindings for BCH encoding/decoding library, based on the [bch_codec](https://github.com/mborgerding/bch_codec) fork. + +The higher level library can be found at: https://github.com/yuvadm/bchlib + +## Usage + +Add the library to your `Cargo.toml`: + +``` +[dependencies] +bchlib-sys = "0.1.0" +``` + +## Build + +The usual: + +```bash +$ cargo build +$ cargo test +``` + +Note that due to usage of `bindgen` you will need `clang` to be installed on your system. + +## License + +[GPLv2](LICENSE.md) diff --git a/bchlib-sys/build.rs b/bchlib-sys/build.rs new file mode 100644 index 0000000..605fec2 --- /dev/null +++ b/bchlib-sys/build.rs @@ -0,0 +1,19 @@ +extern crate bindgen; +extern crate cc; + +use std::env; +use std::path::PathBuf; + +fn main() { + cc::Build::new().file("src/bch/bch.c").compile("bch"); + + let bindings = bindgen::Builder::default() + .header("src/bch/bch.h") + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/bchlib-sys/src/bch/bch.c b/bchlib-sys/src/bch/bch.c new file mode 100644 index 0000000..87712e2 --- /dev/null +++ b/bchlib-sys/src/bch/bch.c @@ -0,0 +1,1523 @@ +/* + * Generic binary BCH encoding/decoding library + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. + * + * Call init_bch to get a pointer to a newly allocated bch_control structure for + * the given m (Galois field order), t (error correction capability) and + * (optional) primitive polynomial parameters. + * + * Call encode_bch to compute and store ecc parity bytes to a given buffer. + * Call decode_bch to detect and locate errors in received data. + * + * On systems supporting hw BCH features, intermediate results may be provided + * to decode_bch in order to skip certain steps. See decode_bch() documentation + * for details. + * + * Algorithmic details: + * + * Encoding is performed by processing 32 input bits in parallel, using 4 + * remainder lookup tables. + * + * The final stage of decoding involves the following internal steps: + * a. Syndrome computation + * b. Error locator polynomial computation using Berlekamp-Massey algorithm + * c. Error locator root finding (by far the most expensive step) + * + * In this implementation, step c is not performed using the usual Chien search. + * Instead, an alternative approach described in [1] is used. It consists in + * factoring the error locator polynomial using the Berlekamp Trace algorithm + * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial + * solving techniques [2] are used. The resulting algorithm, called BTZ, yields + * much better performance than Chien search for usual (m,t) values (typically + * m >= 13, t < 32, see [1]). + * + * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields + * of characteristic 2, in: Western European Workshop on Research in Cryptology + * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. + * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over + * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. + * + * History: + * 2015-05 Mark Borgerding (mark@borgerding.net): replaced linux kernel-specific functions, added bitwise encode/decode functions + */ + +#include +#include +#include "bch.h" +#include + +static +inline +uint32_t CPU_TO_BE32(uint32_t p) +{ + const uint8_t * bytes = (const uint8_t *)&p; + uint32_t out = + ((uint32_t)bytes[0] << 24) | + (bytes[1] << 16) | + (bytes[2] << 8) | + (bytes[3] ) ; + return out; +} + +static inline int FLS(uint32_t x) +{ + int r=0; + if (x>=(1<<16)) { r+=16;x>>=16; } + if (x>=(1<< 8)) { r+= 8;x>>= 8; } + if (x>=(1<< 4)) { r+= 4;x>>= 4; } + if (x>=(1<< 2)) { r+= 2;x>>= 2; } + if (x>=(1<< 1)) { r+= 1;x>>= 1; } + return r+x; +} + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +#define GF_M(_p) ((_p)->m) +#define GF_T(_p) ((_p)->t) +#define GF_N(_p) ((_p)->n) + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#endif + +#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) +#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) + +#ifndef dbg +#define dbg(_fmt, args...) do {} while (0) +#endif + +/* + * represent a polynomial over GF(2^m) + */ +struct gf_poly { + unsigned int deg; /* polynomial degree */ + unsigned int c[0]; /* polynomial terms */ +}; + +/* given its degree, compute a polynomial size in bytes */ +#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) + +/* polynomial of degree 1 */ +struct gf_poly_deg1 { + struct gf_poly poly; + unsigned int c[2]; +}; + +/* + * same as encode_bch(), but process input data one byte at a time + */ +static void encode_bch_unaligned(struct bch_control *bch, + const unsigned char *data, unsigned int len, + uint32_t *ecc) +{ + int i; + const uint32_t *p; + const int l = BCH_ECC_WORDS(bch)-1; + + while (len--) { + p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); + + for (i = 0; i < l; i++) + ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); + + ecc[l] = (ecc[l] << 8)^(*p); + } +} + +/* + * convert ecc bytes to aligned, zero-padded 32-bit ecc words + */ +static void load_ecc8(struct bch_control *bch, uint32_t *dst, + const uint8_t *src) +{ + uint8_t pad[4] = {0, 0, 0, 0}; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++, src += 4) + dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; + + memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); + dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; +} + +/* + * convert 32-bit ecc words to ecc bytes + */ +static void store_ecc8(struct bch_control *bch, uint8_t *dst, + const uint32_t *src) +{ + uint8_t pad[4]; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++) { + *dst++ = (src[i] >> 24); + *dst++ = (src[i] >> 16) & 0xff; + *dst++ = (src[i] >> 8) & 0xff; + *dst++ = (src[i] >> 0) & 0xff; + } + pad[0] = (src[nwords] >> 24); + pad[1] = (src[nwords] >> 16) & 0xff; + pad[2] = (src[nwords] >> 8) & 0xff; + pad[3] = (src[nwords] >> 0) & 0xff; + memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); +} + +/** + * 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. + */ +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc) +{ + const unsigned int l = BCH_ECC_WORDS(bch)-1; + unsigned int i, mlen; + unsigned long m; + uint32_t w, r[l+1]; + const uint32_t * const tab0 = bch->mod8_tab; + const uint32_t * const tab1 = tab0 + 256*(l+1); + const uint32_t * const tab2 = tab1 + 256*(l+1); + const uint32_t * const tab3 = tab2 + 256*(l+1); + const uint32_t *pdata, *p0, *p1, *p2, *p3; + + if (ecc) { + /* load ecc parity bytes into internal 32-bit buffer */ + load_ecc8(bch, bch->ecc_buf, ecc); + } else { + memset(bch->ecc_buf, 0, sizeof(r)); + } + + /* process first unaligned data bytes */ + m = ((unsigned long)data) & 3; + if (m) { + mlen = (len < (4-m)) ? len : 4-m; + encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); + data += mlen; + len -= mlen; + } + + /* process 32-bit aligned data words */ + pdata = (uint32_t *)data; + mlen = len/4; + data += 4*mlen; + len -= 4*mlen; + memcpy(r, bch->ecc_buf, sizeof(r)); + + /* + * split each 32-bit word into 4 polynomials of weight 8 as follows: + * + * 31 ...24 23 ...16 15 ... 8 7 ... 0 + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt + * tttttttt mod g = r0 (precomputed) + * zzzzzzzz 00000000 mod g = r1 (precomputed) + * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) + * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 + */ + while (mlen--) { + /* input data is read in big-endian format */ + w = r[0]^CPU_TO_BE32(*pdata++); + p0 = tab0 + (l+1)*((w >> 0) & 0xff); + p1 = tab1 + (l+1)*((w >> 8) & 0xff); + p2 = tab2 + (l+1)*((w >> 16) & 0xff); + p3 = tab3 + (l+1)*((w >> 24) & 0xff); + + for (i = 0; i < l; i++) + r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; + + r[l] = p0[l]^p1[l]^p2[l]^p3[l]; + } + memcpy(bch->ecc_buf, r, sizeof(r)); + + /* process last unaligned bytes */ + if (len) + encode_bch_unaligned(bch, data, len, bch->ecc_buf); + + /* store ecc parity bytes into original parity buffer */ + if (ecc) + store_ecc8(bch, ecc, bch->ecc_buf); +} + +static inline int modulo(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + while (v >= n) { + v -= n; + v = (v & n) + (v >> GF_M(bch)); + } + return v; +} + +/* + * shorter and faster modulo function, only works when v < 2N. + */ +static inline int mod_s(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + return (v < n) ? v : v-n; +} + +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return FLS(poly)-1; +} + +static inline int parity(unsigned int x) +{ + /* + * public domain code snippet, lifted from + * http://www-graphics.stanford.edu/~seander/bithacks.html + */ + x ^= x >> 1; + x ^= x >> 2; + x = (x & 0x11111111U) * 0x11111111U; + return (x >> 28) & 1; +} + +/* Galois field basic operations: multiply, divide, inverse, etc. */ + +static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) +{ + return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; +} + +static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + GF_N(bch)-bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) +{ + return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; +} + +static inline unsigned int a_pow(struct bch_control *bch, int i) +{ + return bch->a_pow_tab[modulo(bch, i)]; +} + +static inline int a_log(struct bch_control *bch, unsigned int x) +{ + return bch->a_log_tab[x]; +} + +static inline int a_ilog(struct bch_control *bch, unsigned int x) +{ + return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); +} + +/* + * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t + */ +static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, + unsigned int *syn) +{ + int i, j, s; + unsigned int m; + uint32_t poly; + const int t = GF_T(bch); + + s = bch->ecc_bits; + + /* make sure extra bits in last ecc word are cleared */ + m = ((unsigned int)s) & 31; + if (m) + ecc[s/32] &= ~((1u << (32-m))-1); + memset(syn, 0, 2*t*sizeof(*syn)); + + /* compute v(a^j) for j=1 .. 2t-1 */ + do { + poly = *ecc++; + s -= 32; + while (poly) { + i = deg(poly); + for (j = 0; j < 2*t; j += 2) + syn[j] ^= a_pow(bch, (j+1)*(i+s)); + + poly ^= (1 << i); + } + } while (s > 0); + + /* v(a^(2j)) = v(a^j)^2 */ + for (j = 0; j < t; j++) + syn[2*j+1] = gf_sqr(bch, syn[j]); +} + +static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) +{ + memcpy(dst, src, GF_POLY_SZ(src->deg)); +} + +static int compute_error_locator_polynomial(struct bch_control *bch, + const unsigned int *syn) +{ + const unsigned int t = GF_T(bch); + const unsigned int n = GF_N(bch); + unsigned int i, j, tmp, l, pd = 1, d = syn[0]; + struct gf_poly *elp = bch->elp; + struct gf_poly *pelp = bch->poly_2t[0]; + struct gf_poly *elp_copy = bch->poly_2t[1]; + int k, pp = -1; + + memset(pelp, 0, GF_POLY_SZ(2*t)); + memset(elp, 0, GF_POLY_SZ(2*t)); + + pelp->deg = 0; + pelp->c[0] = 1; + elp->deg = 0; + elp->c[0] = 1; + + /* use simplified binary Berlekamp-Massey algorithm */ + for (i = 0; (i < t) && (elp->deg <= t); i++) { + if (d) { + k = 2*i-pp; + gf_poly_copy(elp_copy, elp); + /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ + tmp = a_log(bch, d)+n-a_log(bch, pd); + for (j = 0; j <= pelp->deg; j++) { + if (pelp->c[j]) { + l = a_log(bch, pelp->c[j]); + elp->c[j+k] ^= a_pow(bch, tmp+l); + } + } + /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ + tmp = pelp->deg+k; + if (tmp > elp->deg) { + elp->deg = tmp; + gf_poly_copy(pelp, elp_copy); + pd = d; + pp = 2*i; + } + } + /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ + if (i < t-1) { + d = syn[2*i+2]; + for (j = 1; j <= elp->deg; j++) + d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); + } + } + dbg("elp=%s\n", gf_poly_str(elp)); + return (elp->deg > t) ? -1 : (int)elp->deg; +} + +/* + * solve a m x m linear system in GF(2) with an expected number of solutions, + * and return the number of found solutions + */ +static int solve_linear_system(struct bch_control *bch, unsigned int *rows, + unsigned int *sol, int nsol) +{ + const int m = GF_M(bch); + unsigned int tmp, mask; + int rem, c, r, p, k, param[m]; + + k = 0; + mask = 1 << m; + + /* Gaussian elimination */ + for (c = 0; c < m; c++) { + rem = 0; + p = c-k; + /* find suitable row for elimination */ + for (r = p; r < m; r++) { + if (rows[r] & mask) { + if (r != p) { + tmp = rows[r]; + rows[r] = rows[p]; + rows[p] = tmp; + } + rem = r+1; + break; + } + } + if (rem) { + /* perform elimination on remaining rows */ + tmp = rows[p]; + for (r = rem; r < m; r++) { + if (rows[r] & mask) + rows[r] ^= tmp; + } + } else { + /* elimination not needed, store defective row index */ + param[k++] = c; + } + mask >>= 1; + } + /* rewrite system, inserting fake parameter rows */ + if (k > 0) { + p = k; + for (r = m-1; r >= 0; r--) { + if ((r > m-1-k) && rows[r]) + /* system has no solution */ + return 0; + + rows[r] = (p && (r == param[p-1])) ? + p--, 1u << (m-r) : rows[r-p]; + } + } + + if (nsol != (1 << k)) + /* unexpected number of solutions */ + return 0; + + for (p = 0; p < nsol; p++) { + /* set parameters for p-th solution */ + for (c = 0; c < k; c++) + rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); + + /* compute unique solution */ + tmp = 0; + for (r = m-1; r >= 0; r--) { + mask = rows[r] & (tmp|1); + tmp |= parity(mask) << (m-r); + } + sol[p] = tmp >> 1; + } + return nsol; +} + +/* + * this function builds and solves a linear system for finding roots of a degree + * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). + */ +static int find_affine4_roots(struct bch_control *bch, unsigned int a, + unsigned int b, unsigned int c, + unsigned int *roots) +{ + int i, j, k; + const int m = GF_M(bch); + unsigned int mask = 0xff, t, rows[16] = {0,}; + + j = a_log(bch, b); + k = a_log(bch, a); + rows[0] = c; + + /* buid linear system to solve X^4+aX^2+bX+c = 0 */ + for (i = 0; i < m; i++) { + rows[i+1] = bch->a_pow_tab[4*i]^ + (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ + (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); + j++; + k += 2; + } + /* + * transpose 16x16 matrix before passing it to linear solver + * warning: this code assumes m < 16 + */ + for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { + for (k = 0; k < 16; k = (k+j+1) & ~j) { + t = ((rows[k] >> j)^rows[k+j]) & mask; + rows[k] ^= (t << j); + rows[k+j] ^= t; + } + } + return solve_linear_system(bch, rows, roots, 4); +} + +/* + * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) + */ +static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0; + + if (poly->c[0]) + /* poly[X] = bX+c with c!=0, root=c/b */ + roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ + bch->a_log_tab[poly->c[1]]); + return n; +} + +/* + * compute roots of a degree 2 polynomial over GF(2^m) + */ +static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0, i, l0, l1, l2; + unsigned int u, v, r; + + if (poly->c[0] && poly->c[1]) { + + l0 = bch->a_log_tab[poly->c[0]]; + l1 = bch->a_log_tab[poly->c[1]]; + l2 = bch->a_log_tab[poly->c[2]]; + + /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ + u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); + /* + * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): + * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = + * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) + * i.e. r and r+1 are roots iff Tr(u)=0 + */ + r = 0; + v = u; + while (v) { + i = deg(v); + r ^= bch->xi_tab[i]; + v ^= (1 << i); + } + /* verify root */ + if ((gf_sqr(bch, r)^r) == u) { + /* reverse z=a/bX transformation and compute log(1/r) */ + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r]+l2); + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r^1]+l2); + } + } + return n; +} + +/* + * compute roots of a degree 3 polynomial over GF(2^m) + */ +static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, n = 0; + unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; + + if (poly->c[0]) { + /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ + e3 = poly->c[3]; + c2 = gf_div(bch, poly->c[0], e3); + b2 = gf_div(bch, poly->c[1], e3); + a2 = gf_div(bch, poly->c[2], e3); + + /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ + c = gf_mul(bch, a2, c2); /* c = a2c2 */ + b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ + a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ + + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a, b, c, tmp) == 4) { + /* remove a2 from final list of roots */ + for (i = 0; i < 4; i++) { + if (tmp[i] != a2) + roots[n++] = a_ilog(bch, tmp[i]); + } + } + } + return n; +} + +/* + * compute roots of a degree 4 polynomial over GF(2^m) + */ +static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, l, n = 0; + unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; + + if (poly->c[0] == 0) + return 0; + + /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ + e4 = poly->c[4]; + d = gf_div(bch, poly->c[0], e4); + c = gf_div(bch, poly->c[1], e4); + b = gf_div(bch, poly->c[2], e4); + a = gf_div(bch, poly->c[3], e4); + + /* use Y=1/X transformation to get an affine polynomial */ + if (a) { + /* first, eliminate cX by using z=X+e with ae^2+c=0 */ + if (c) { + /* compute e such that e^2 = c/a */ + f = gf_div(bch, c, a); + l = a_log(bch, f); + l += (l & 1) ? GF_N(bch) : 0; + e = a_pow(bch, l/2); + /* + * use transformation z=X+e: + * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d + * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d + * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d + * z^4 + az^3 + b'z^2 + d' + */ + d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; + b = gf_mul(bch, a, e)^b; + } + /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ + if (d == 0) + /* assume all roots have multiplicity 1 */ + return 0; + + c2 = gf_inv(bch, d); + b2 = gf_div(bch, a, d); + a2 = gf_div(bch, b, d); + } else { + /* polynomial is already affine */ + c2 = d; + b2 = c; + a2 = b; + } + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { + for (i = 0; i < 4; i++) { + /* post-process roots (reverse transformations) */ + f = a ? gf_inv(bch, roots[i]) : roots[i]; + roots[i] = a_ilog(bch, f^e); + } + n = 4; + } + return n; +} + +/* + * build monic, log-based representation of a polynomial + */ +static void gf_poly_logrep(struct bch_control *bch, + const struct gf_poly *a, int *rep) +{ + int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); + + /* represent 0 values with -1; warning, rep[d] is not set to 1 */ + for (i = 0; i < d; i++) + rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; +} + +/* + * compute polynomial Euclidean division remainder in GF(2^m)[X] + */ +static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, int *rep) +{ + int la, p, m; + unsigned int i, j, *c = a->c; + const unsigned int d = b->deg; + + if (a->deg < d) + return; + + /* reuse or compute log representation of denominator */ + if (!rep) { + rep = bch->cache; + gf_poly_logrep(bch, b, rep); + } + + for (j = a->deg; j >= d; j--) { + if (c[j]) { + la = a_log(bch, c[j]); + p = j-d; + for (i = 0; i < d; i++, p++) { + m = rep[i]; + if (m >= 0) + c[p] ^= bch->a_pow_tab[mod_s(bch, + m+la)]; + } + } + } + a->deg = d-1; + while (!c[a->deg] && a->deg) + a->deg--; +} + +/* + * compute polynomial Euclidean division quotient in GF(2^m)[X] + */ +static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, struct gf_poly *q) +{ + if (a->deg >= b->deg) { + q->deg = a->deg-b->deg; + /* compute a mod b (modifies a) */ + gf_poly_mod(bch, a, b, NULL); + /* quotient is stored in upper part of polynomial a */ + memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); + } else { + q->deg = 0; + q->c[0] = 0; + } +} + +/* + * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] + */ +static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, + struct gf_poly *b) +{ + struct gf_poly *tmp; + + dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); + + if (a->deg < b->deg) { + tmp = b; + b = a; + a = tmp; + } + + while (b->deg > 0) { + gf_poly_mod(bch, a, b, NULL); + tmp = b; + b = a; + a = tmp; + } + + dbg("%s\n", gf_poly_str(a)); + + return a; +} + +/* + * Given a polynomial f and an integer k, compute Tr(a^kX) mod f + * This is used in Berlekamp Trace algorithm for splitting polynomials + */ +static void compute_trace_bk_mod(struct bch_control *bch, int k, + const struct gf_poly *f, struct gf_poly *z, + struct gf_poly *out) +{ + const int m = GF_M(bch); + int i, j; + + /* z contains z^2j mod f */ + z->deg = 1; + z->c[0] = 0; + z->c[1] = bch->a_pow_tab[k]; + + out->deg = 0; + memset(out, 0, GF_POLY_SZ(f->deg)); + + /* compute f log representation only once */ + gf_poly_logrep(bch, f, bch->cache); + + for (i = 0; i < m; i++) { + /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ + for (j = z->deg; j >= 0; j--) { + out->c[j] ^= z->c[j]; + z->c[2*j] = gf_sqr(bch, z->c[j]); + z->c[2*j+1] = 0; + } + if (z->deg > out->deg) + out->deg = z->deg; + + if (i < m-1) { + z->deg *= 2; + /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ + gf_poly_mod(bch, z, f, bch->cache); + } + } + while (!out->c[out->deg] && out->deg) + out->deg--; + + dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); +} + +/* + * factor a polynomial using Berlekamp Trace algorithm (BTA) + */ +static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, + struct gf_poly **g, struct gf_poly **h) +{ + struct gf_poly *f2 = bch->poly_2t[0]; + struct gf_poly *q = bch->poly_2t[1]; + struct gf_poly *tk = bch->poly_2t[2]; + struct gf_poly *z = bch->poly_2t[3]; + struct gf_poly *gcd; + + dbg("factoring %s...\n", gf_poly_str(f)); + + *g = f; + *h = NULL; + + /* tk = Tr(a^k.X) mod f */ + compute_trace_bk_mod(bch, k, f, z, tk); + + if (tk->deg > 0) { + /* compute g = gcd(f, tk) (destructive operation) */ + gf_poly_copy(f2, f); + gcd = gf_poly_gcd(bch, f2, tk); + if (gcd->deg < f->deg) { + /* compute h=f/gcd(f,tk); this will modify f and q */ + gf_poly_div(bch, f, gcd, q); + /* store g and h in-place (clobbering f) */ + *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; + gf_poly_copy(*g, gcd); + gf_poly_copy(*h, q); + } + } +} + +/* + * find roots of a polynomial, using BTZ algorithm; see the beginning of this + * file for details + */ +static int find_poly_roots(struct bch_control *bch, unsigned int k, + struct gf_poly *poly, unsigned int *roots) +{ + int cnt; + struct gf_poly *f1, *f2; + + switch (poly->deg) { + /* handle low degree polynomials with ad hoc techniques */ + case 1: + cnt = find_poly_deg1_roots(bch, poly, roots); + break; + case 2: + cnt = find_poly_deg2_roots(bch, poly, roots); + break; + case 3: + cnt = find_poly_deg3_roots(bch, poly, roots); + break; + case 4: + cnt = find_poly_deg4_roots(bch, poly, roots); + break; + default: + /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ + cnt = 0; + if (poly->deg && (k <= GF_M(bch))) { + factor_polynomial(bch, k, poly, &f1, &f2); + if (f1) + cnt += find_poly_roots(bch, k+1, f1, roots); + if (f2) + cnt += find_poly_roots(bch, k+1, f2, roots+cnt); + } + break; + } + return cnt; +} + +#if defined(USE_CHIEN_SEARCH) +/* + * exhaustive root search (Chien) implementation - not used, included only for + * reference/comparison tests + */ +static int chien_search(struct bch_control *bch, unsigned int len, + struct gf_poly *p, unsigned int *roots) +{ + int m; + unsigned int i, j, syn, syn0, count = 0; + const unsigned int k = 8*len+bch->ecc_bits; + + /* use a log-based representation of polynomial */ + gf_poly_logrep(bch, p, bch->cache); + bch->cache[p->deg] = 0; + syn0 = gf_div(bch, p->c[0], p->c[p->deg]); + + for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { + /* compute elp(a^i) */ + for (j = 1, syn = syn0; j <= p->deg; j++) { + m = bch->cache[j]; + if (m >= 0) + syn ^= a_pow(bch, m+j*i); + } + if (syn == 0) { + roots[count++] = GF_N(bch)-i; + if (count == p->deg) + break; + } + } + return (count == p->deg) ? count : 0; +} +#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) +#endif /* USE_CHIEN_SEARCH */ + +/** + * 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. + */ +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc) +{ + const unsigned int ecc_words = BCH_ECC_WORDS(bch); + unsigned int nbits; + int i, err, nroots; + uint32_t sum; + + /* sanity check: make sure data length can be handled */ + if ( len > ((bch->n-bch->ecc_bits+7)/8)) + return -EINVAL; + + /* if caller does not provide syndromes, compute them */ + if (!syn) { + if (!calc_ecc) { + /* compute received data ecc into an internal buffer */ + if (!data || !recv_ecc) + return -EINVAL; + encode_bch(bch, data, len, NULL); + } else { + /* load provided calculated ecc */ + load_ecc8(bch, bch->ecc_buf, calc_ecc); + } + /* load received ecc or assume it was XORed in calc_ecc */ + if (recv_ecc) { + load_ecc8(bch, bch->ecc_buf2, recv_ecc); + /* XOR received and calculated ecc */ + for (i = 0, sum = 0; i < (int)ecc_words; i++) { + bch->ecc_buf[i] ^= bch->ecc_buf2[i]; + sum |= bch->ecc_buf[i]; + } + if (!sum) + /* no error found */ + return 0; + } + compute_syndromes(bch, bch->ecc_buf, bch->syn); + syn = bch->syn; + } + + err = compute_error_locator_polynomial(bch, syn); + if (err > 0) { + nroots = find_poly_roots(bch, 1, bch->elp, errloc); + if (err != nroots) + err = -1; + } + if (err > 0) { + /* post-process raw error locations for easier correction */ + nbits = (len*8)+bch->ecc_bits; + for (i = 0; i < err; i++) { + if (errloc[i] >= nbits) { + err = -1; + break; + } + errloc[i] = nbits-1-errloc[i]; + errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); + } + } + return (err >= 0) ? err : -EBADMSG; +} + +/* + * generate Galois field lookup tables + */ +static int build_gf_tables(struct bch_control *bch, unsigned int poly) +{ + unsigned int i, x = 1; + const unsigned int k = 1 << deg(poly); + + /* primitive polynomial must be of degree m */ + if (k != (1u << GF_M(bch))) + return -1; + + for (i = 0; i < GF_N(bch); i++) { + bch->a_pow_tab[i] = x; + bch->a_log_tab[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; + bch->a_log_tab[0] = 0; + + return 0; +} + +/* + * compute generator polynomial remainder tables for fast encoding + */ +static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) +{ + int i, j, b, d; + uint32_t data, hi, lo, *tab; + const int l = BCH_ECC_WORDS(bch); + const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); + const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); + + memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); + + for (i = 0; i < 256; i++) { + /* p(X)=i is a small polynomial of weight <= 8 */ + for (b = 0; b < 4; b++) { + /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ + tab = bch->mod8_tab + (b*256+i)*l; + data = i << (8*b); + while (data) { + d = deg(data); + /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ + data ^= g[0] >> (31-d); + for (j = 0; j < ecclen; j++) { + hi = (d < 31) ? g[j] << (d+1) : 0; + lo = (j+1 < plen) ? + g[j+1] >> (31-d) : 0; + tab[j] ^= hi|lo; + } + } + } + } +} + +/* + * build a base for factoring degree 2 polynomials + */ +static int build_deg2_base(struct bch_control *bch) +{ + const int m = GF_M(bch); + int i, j, r; + unsigned int sum, x, y, remaining, ak = 0, xi[m]; + + /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ + for (i = 0; i < m; i++) { + for (j = 0, sum = 0; j < m; j++) + sum ^= a_pow(bch, i*(1 << j)); + + if (sum) { + ak = bch->a_pow_tab[i]; + break; + } + } + /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ + remaining = m; + memset(xi, 0, sizeof(xi)); + + for (x = 0; (x <= GF_N(bch)) && remaining; x++) { + y = gf_sqr(bch, x)^x; + for (i = 0; i < 2; i++) { + r = a_log(bch, y); + if (y && (r < m) && !xi[r]) { + bch->xi_tab[r] = x; + xi[r] = 1; + remaining--; + dbg("x%d = %x\n", r, x); + break; + } + y ^= ak; + } + } + /* should not happen but check anyway */ + return remaining ? -1 : 0; +} + +static void *bch_alloc(size_t size, int *err) +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) + *err = 1; + return ptr; +} + +/* + * compute generator polynomial for given (m,t) parameters. + */ +static uint32_t *compute_generator_polynomial(struct bch_control *bch) +{ + const unsigned int m = GF_M(bch); + const unsigned int t = GF_T(bch); + int n, err = 0; + unsigned int i, j, nbits, r, word, *roots; + struct gf_poly *g; + uint32_t *genpoly; + + g = (struct gf_poly*)bch_alloc(GF_POLY_SZ(m*t), &err); + roots = (unsigned int*)bch_alloc((bch->n+1)*sizeof(*roots), &err); + genpoly = (uint32_t*)bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); + + if (err) { + free(genpoly); + genpoly = NULL; + goto finish; + } + + /* enumerate all roots of g(X) */ + memset(roots , 0, (bch->n+1)*sizeof(*roots)); + for (i = 0; i < t; i++) { + for (j = 0, r = 2*i+1; j < m; j++) { + roots[r] = 1; + r = mod_s(bch, 2*r); + } + } + /* build generator polynomial g(X) */ + g->deg = 0; + g->c[0] = 1; + for (i = 0; i < GF_N(bch); i++) { + if (roots[i]) { + /* multiply g(X) by (X+root) */ + r = bch->a_pow_tab[i]; + g->c[g->deg+1] = 1; + for (j = g->deg; j > 0; j--) + g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; + + g->c[0] = gf_mul(bch, g->c[0], r); + g->deg++; + } + } + /* store left-justified binary representation of g(X) */ + n = g->deg+1; + i = 0; + + while (n > 0) { + nbits = (n > 32) ? 32 : n; + for (j = 0, word = 0; j < nbits; j++) { + if (g->c[n-1-j]) + word |= 1u << (31-j); + } + genpoly[i++] = word; + n -= nbits; + } + bch->ecc_bits = g->deg; + +finish: + free(g); + free(roots); + + return genpoly; +} + +/** + * 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. + */ +struct bch_control *init_bch(int m, int t, unsigned int prim_poly) +{ + int err = 0; + unsigned int i, words; + uint32_t *genpoly; + struct bch_control *bch = NULL; + + const int min_m = 5; + const int max_m = 15; + + /* default primitive polynomials */ + static const unsigned int prim_poly_tab[] = { + 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, + 0x402b, 0x8003, + }; + + if ((m < min_m) || (m > max_m)) + /* + * values of m greater than 15 are not currently supported; + * supporting m > 15 would require changing table base type + * (uint16_t) and a small patch in matrix transposition + */ + goto fail; + + /* sanity checks */ + if ((t < 1) || (m*t >= ((1 << m)-1))) + /* invalid t value */ + goto fail; + + /* select a primitive polynomial for generating GF(2^m) */ + if (prim_poly == 0) + prim_poly = prim_poly_tab[m-min_m]; + + bch = (struct bch_control*)malloc(sizeof(*bch)); + if (bch == NULL) + goto fail; + memset(bch,0,sizeof(*bch)); + + bch->m = m; + bch->t = t; + bch->n = (1 << m)-1; + words = DIV_ROUND_UP(m*t, 32); + bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); + bch->a_pow_tab = (uint16_t*)bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); + bch->a_log_tab = (uint16_t*)bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); + bch->mod8_tab = (uint32_t*)bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); + bch->ecc_buf = (uint32_t*)bch_alloc(words*sizeof(*bch->ecc_buf), &err); + bch->ecc_buf2 = (uint32_t*)bch_alloc(words*sizeof(*bch->ecc_buf2), &err); + bch->xi_tab = (unsigned int*)bch_alloc(m*sizeof(*bch->xi_tab), &err); + bch->syn = (unsigned int*)bch_alloc(2*t*sizeof(*bch->syn), &err); + bch->cache = (int*)bch_alloc(2*t*sizeof(*bch->cache), &err); + bch->elp = (struct gf_poly*)bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + bch->poly_2t[i] = (struct gf_poly*)bch_alloc(GF_POLY_SZ(2*t), &err); + + if (err) + goto fail; + + err = build_gf_tables(bch, prim_poly); + if (err) + goto fail; + + /* use generator polynomial for computing encoding tables */ + genpoly = compute_generator_polynomial(bch); + if (genpoly == NULL) + goto fail; + + build_mod8_tables(bch, genpoly); + free(genpoly); + + err = build_deg2_base(bch); + if (err) + goto fail; + + return bch; + +fail: + free_bch(bch); + return NULL; +} + +/** + * free_bch - free the BCH control structure + * @bch: BCH control structure to release + */ +void free_bch(struct bch_control *bch) +{ + unsigned int i; + + if (bch) { + free(bch->a_pow_tab); + free(bch->a_log_tab); + free(bch->mod8_tab); + free(bch->ecc_buf); + free(bch->ecc_buf2); + free(bch->xi_tab); + free(bch->syn); + free(bch->cache); + free(bch->elp); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + free(bch->poly_2t[i]); + + free(bch->databuf); + + free(bch); + } +} + +static void check_databuf(struct bch_control *bch) +{ + if (bch->databuf == NULL) + bch->databuf = (uint8_t*)malloc( ((bch->n - bch->ecc_bits)+7)/8 + bch->ecc_bytes ); +} + +static int pack_databuf( struct bch_control *bch , const uint8_t *data) +{ + const int K = bch->n - bch->ecc_bits; + int k; + int ndatabytes = (K+7)/8; + int nPad=ndatabytes*8 - K; + uint8_t * bytes; + check_databuf(bch); + bytes = bch->databuf; + memset(bytes,0,ndatabytes); + for (k=0;k>3] |= mask; + } + return ndatabytes; +} + +/* + * + * */ +static void unpack_eccbits( struct bch_control *bch , uint8_t * ecc) +{ + int k; + uint8_t * ecc_bytes; + check_databuf(bch); + ecc_bytes = bch->databuf + ((bch->n - bch->ecc_bits)+7)/8; + // expand ecc bytes to bits + for (k=0;kecc_bits;++k) + ecc[k] = (ecc_bytes[k>>3] & (1<<(7-(k&7))))>0; +} + +static void pack_eccbits(struct bch_control *bch ,const uint8_t * ecc) +{ + int k; + uint8_t * ecc_bytes; + check_databuf(bch); + ecc_bytes = bch->databuf + ((bch->n - bch->ecc_bits)+7)/8; + // expand ecc bytes to bits + memset(ecc_bytes,0,bch->ecc_bytes); + for (k=0;kecc_bits;++k) { + int bit = (ecc[k]&1)!=0; // use only the LSB (can allow sloppy but nice feature of sending in ASCII '0' and '1') + uint8_t mask = (1<<(7-(k&7))); + if (bit) + ecc_bytes[k>>3] |= mask; + } +} + + +/** + * encodebits_bch - calculate BCH ecc parity of data + * @bch: BCH control structure + * @data: data bits to encode , length= bch->n - bch->ecc_bits + * @ecc: output ecc parity bits, length = bch->ecc_bits + * + * 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. + */ +void encodebits_bch(struct bch_control *bch, const uint8_t *data, uint8_t *ecc) +{ + int ndatabytes = pack_databuf(bch,data); + uint8_t * ecc_bytes = bch->databuf + ndatabytes; + memset(ecc_bytes,0,bch->ecc_bytes); + encode_bch(bch,bch->databuf,ndatabytes,ecc_bytes); + unpack_eccbits(bch,ecc); +} + +/** + * decodebits_bch - decode received codeword bits and find error locations + * @bch: BCH control structure + * @databits: received data, length = bch->n - bch->ecc_bits + * @recv_ecc_bits: received ecc, length = bch->ecc_bits + * @errloc: output array of error locations + * + * Returns: + * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if + * invalid parameters were provided + * + * if (errloc[i] < bch->n - bch->ecc_bits ), then + * databits[errloc[i]] is in error + * otherwise + * the i-th error is located in ecc (no need for data correction) + * + * Note that this function does not perform any data correction by itself, it + * merely indicates error locations. + */ +int decodebits_bch(struct bch_control *bch, const uint8_t *data, const uint8_t *recv_ecc, unsigned int *errloc) +{ + int nbytes; + int nerr; + + if ( (data==NULL) ||(recv_ecc==NULL)) { + return -EINVAL; // TODO handle the same calling conventions as decode_bch + } + + nbytes = pack_databuf(bch,data); + + pack_eccbits(bch,recv_ecc); + + nerr = decode_bch(bch, bch->databuf, nbytes, bch->databuf + nbytes,NULL,NULL,errloc); + if (nerr>0) { + const int K = bch->n - bch->ecc_bits; + int nPad=((K+7)/8)*8 - K; + // correct the errloc positions + int k; + for (k=0;k>3) < len) + data[bi>>3] ^= (1<<(bi&7)); + } + +} + +/** + * correctbits_bch - correct error locations as found in decodebits_bch + * @bch,@databits,@errloc: same as a previous call to decodebits_bch + * @nerr: returned from decodebits_bch + */ +void correctbits_bch(struct bch_control *bch, uint8_t *databits, unsigned int *errloc, int nerr) +{ + const int m = bch->n - bch->ecc_bits; + int i; + for (i=0;i + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. +*/ +#ifndef _BCH_H +#define _BCH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 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 + */ +struct bch_control { + unsigned int m; + unsigned int n; + unsigned int t; + unsigned int ecc_bits; + unsigned int ecc_bytes; +/* private: */ + uint16_t *a_pow_tab; + uint16_t *a_log_tab; + uint32_t *mod8_tab; + uint32_t *ecc_buf; + uint32_t *ecc_buf2; + unsigned int *xi_tab; + unsigned int *syn; + int *cache; + struct gf_poly *elp; + struct gf_poly *poly_2t[4]; + uint8_t *databuf; +}; + +struct bch_control *init_bch(int m, int t, unsigned int prim_poly); + +void free_bch(struct bch_control *bch); + +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc); + +void encodebits_bch(struct bch_control *bch, const uint8_t *data, uint8_t *ecc); + +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc); + +int decodebits_bch(struct bch_control *bch, const uint8_t *data, + const uint8_t *recv_ecc, unsigned int *errloc); + + +void correct_bch(struct bch_control *bch, uint8_t *data,unsigned int len, unsigned int *errloc, int nerr); + +void correctbits_bch(struct bch_control *bch, uint8_t *databits, unsigned int *errloc, int nerr); + + +#ifdef __cplusplus +} +#endif + +#endif /* _BCH_H */ diff --git a/bchlib-sys/src/lib.rs b/bchlib-sys/src/lib.rs new file mode 100644 index 0000000..47eaa43 --- /dev/null +++ b/bchlib-sys/src/lib.rs @@ -0,0 +1,17 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + unsafe { + let _c = init_bch(5, 2, 37); + } + } +} diff --git a/build.rs b/build.rs deleted file mode 100644 index 605fec2..0000000 --- a/build.rs +++ /dev/null @@ -1,19 +0,0 @@ -extern crate bindgen; -extern crate cc; - -use std::env; -use std::path::PathBuf; - -fn main() { - cc::Build::new().file("src/bch/bch.c").compile("bch"); - - let bindings = bindgen::Builder::default() - .header("src/bch/bch.h") - .generate() - .expect("Unable to generate bindings"); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); -} diff --git a/src/bch/bch.c b/src/bch/bch.c deleted file mode 100644 index 87712e2..0000000 --- a/src/bch/bch.c +++ /dev/null @@ -1,1523 +0,0 @@ -/* - * Generic binary BCH encoding/decoding library - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program 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. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright © 2011 Parrot S.A. - * - * Author: Ivan Djelic - * - * Description: - * - * This library provides runtime configurable encoding/decoding of binary - * Bose-Chaudhuri-Hocquenghem (BCH) codes. - * - * Call init_bch to get a pointer to a newly allocated bch_control structure for - * the given m (Galois field order), t (error correction capability) and - * (optional) primitive polynomial parameters. - * - * Call encode_bch to compute and store ecc parity bytes to a given buffer. - * Call decode_bch to detect and locate errors in received data. - * - * On systems supporting hw BCH features, intermediate results may be provided - * to decode_bch in order to skip certain steps. See decode_bch() documentation - * for details. - * - * Algorithmic details: - * - * Encoding is performed by processing 32 input bits in parallel, using 4 - * remainder lookup tables. - * - * The final stage of decoding involves the following internal steps: - * a. Syndrome computation - * b. Error locator polynomial computation using Berlekamp-Massey algorithm - * c. Error locator root finding (by far the most expensive step) - * - * In this implementation, step c is not performed using the usual Chien search. - * Instead, an alternative approach described in [1] is used. It consists in - * factoring the error locator polynomial using the Berlekamp Trace algorithm - * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial - * solving techniques [2] are used. The resulting algorithm, called BTZ, yields - * much better performance than Chien search for usual (m,t) values (typically - * m >= 13, t < 32, see [1]). - * - * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields - * of characteristic 2, in: Western European Workshop on Research in Cryptology - * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. - * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over - * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. - * - * History: - * 2015-05 Mark Borgerding (mark@borgerding.net): replaced linux kernel-specific functions, added bitwise encode/decode functions - */ - -#include -#include -#include "bch.h" -#include - -static -inline -uint32_t CPU_TO_BE32(uint32_t p) -{ - const uint8_t * bytes = (const uint8_t *)&p; - uint32_t out = - ((uint32_t)bytes[0] << 24) | - (bytes[1] << 16) | - (bytes[2] << 8) | - (bytes[3] ) ; - return out; -} - -static inline int FLS(uint32_t x) -{ - int r=0; - if (x>=(1<<16)) { r+=16;x>>=16; } - if (x>=(1<< 8)) { r+= 8;x>>= 8; } - if (x>=(1<< 4)) { r+= 4;x>>= 4; } - if (x>=(1<< 2)) { r+= 2;x>>= 2; } - if (x>=(1<< 1)) { r+= 1;x>>= 1; } - return r+x; -} - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) - -#define GF_M(_p) ((_p)->m) -#define GF_T(_p) ((_p)->t) -#define GF_N(_p) ((_p)->n) - -#ifndef DIV_ROUND_UP -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) -#endif - -#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) -#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) - -#ifndef dbg -#define dbg(_fmt, args...) do {} while (0) -#endif - -/* - * represent a polynomial over GF(2^m) - */ -struct gf_poly { - unsigned int deg; /* polynomial degree */ - unsigned int c[0]; /* polynomial terms */ -}; - -/* given its degree, compute a polynomial size in bytes */ -#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) - -/* polynomial of degree 1 */ -struct gf_poly_deg1 { - struct gf_poly poly; - unsigned int c[2]; -}; - -/* - * same as encode_bch(), but process input data one byte at a time - */ -static void encode_bch_unaligned(struct bch_control *bch, - const unsigned char *data, unsigned int len, - uint32_t *ecc) -{ - int i; - const uint32_t *p; - const int l = BCH_ECC_WORDS(bch)-1; - - while (len--) { - p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); - - for (i = 0; i < l; i++) - ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); - - ecc[l] = (ecc[l] << 8)^(*p); - } -} - -/* - * convert ecc bytes to aligned, zero-padded 32-bit ecc words - */ -static void load_ecc8(struct bch_control *bch, uint32_t *dst, - const uint8_t *src) -{ - uint8_t pad[4] = {0, 0, 0, 0}; - unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; - - for (i = 0; i < nwords; i++, src += 4) - dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; - - memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); - dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; -} - -/* - * convert 32-bit ecc words to ecc bytes - */ -static void store_ecc8(struct bch_control *bch, uint8_t *dst, - const uint32_t *src) -{ - uint8_t pad[4]; - unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; - - for (i = 0; i < nwords; i++) { - *dst++ = (src[i] >> 24); - *dst++ = (src[i] >> 16) & 0xff; - *dst++ = (src[i] >> 8) & 0xff; - *dst++ = (src[i] >> 0) & 0xff; - } - pad[0] = (src[nwords] >> 24); - pad[1] = (src[nwords] >> 16) & 0xff; - pad[2] = (src[nwords] >> 8) & 0xff; - pad[3] = (src[nwords] >> 0) & 0xff; - memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); -} - -/** - * 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. - */ -void encode_bch(struct bch_control *bch, const uint8_t *data, - unsigned int len, uint8_t *ecc) -{ - const unsigned int l = BCH_ECC_WORDS(bch)-1; - unsigned int i, mlen; - unsigned long m; - uint32_t w, r[l+1]; - const uint32_t * const tab0 = bch->mod8_tab; - const uint32_t * const tab1 = tab0 + 256*(l+1); - const uint32_t * const tab2 = tab1 + 256*(l+1); - const uint32_t * const tab3 = tab2 + 256*(l+1); - const uint32_t *pdata, *p0, *p1, *p2, *p3; - - if (ecc) { - /* load ecc parity bytes into internal 32-bit buffer */ - load_ecc8(bch, bch->ecc_buf, ecc); - } else { - memset(bch->ecc_buf, 0, sizeof(r)); - } - - /* process first unaligned data bytes */ - m = ((unsigned long)data) & 3; - if (m) { - mlen = (len < (4-m)) ? len : 4-m; - encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); - data += mlen; - len -= mlen; - } - - /* process 32-bit aligned data words */ - pdata = (uint32_t *)data; - mlen = len/4; - data += 4*mlen; - len -= 4*mlen; - memcpy(r, bch->ecc_buf, sizeof(r)); - - /* - * split each 32-bit word into 4 polynomials of weight 8 as follows: - * - * 31 ...24 23 ...16 15 ... 8 7 ... 0 - * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt - * tttttttt mod g = r0 (precomputed) - * zzzzzzzz 00000000 mod g = r1 (precomputed) - * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) - * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) - * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 - */ - while (mlen--) { - /* input data is read in big-endian format */ - w = r[0]^CPU_TO_BE32(*pdata++); - p0 = tab0 + (l+1)*((w >> 0) & 0xff); - p1 = tab1 + (l+1)*((w >> 8) & 0xff); - p2 = tab2 + (l+1)*((w >> 16) & 0xff); - p3 = tab3 + (l+1)*((w >> 24) & 0xff); - - for (i = 0; i < l; i++) - r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; - - r[l] = p0[l]^p1[l]^p2[l]^p3[l]; - } - memcpy(bch->ecc_buf, r, sizeof(r)); - - /* process last unaligned bytes */ - if (len) - encode_bch_unaligned(bch, data, len, bch->ecc_buf); - - /* store ecc parity bytes into original parity buffer */ - if (ecc) - store_ecc8(bch, ecc, bch->ecc_buf); -} - -static inline int modulo(struct bch_control *bch, unsigned int v) -{ - const unsigned int n = GF_N(bch); - while (v >= n) { - v -= n; - v = (v & n) + (v >> GF_M(bch)); - } - return v; -} - -/* - * shorter and faster modulo function, only works when v < 2N. - */ -static inline int mod_s(struct bch_control *bch, unsigned int v) -{ - const unsigned int n = GF_N(bch); - return (v < n) ? v : v-n; -} - -static inline int deg(unsigned int poly) -{ - /* polynomial degree is the most-significant bit index */ - return FLS(poly)-1; -} - -static inline int parity(unsigned int x) -{ - /* - * public domain code snippet, lifted from - * http://www-graphics.stanford.edu/~seander/bithacks.html - */ - x ^= x >> 1; - x ^= x >> 2; - x = (x & 0x11111111U) * 0x11111111U; - return (x >> 28) & 1; -} - -/* Galois field basic operations: multiply, divide, inverse, etc. */ - -static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, - unsigned int b) -{ - return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ - bch->a_log_tab[b])] : 0; -} - -static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) -{ - return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; -} - -static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, - unsigned int b) -{ - return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ - GF_N(bch)-bch->a_log_tab[b])] : 0; -} - -static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) -{ - return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; -} - -static inline unsigned int a_pow(struct bch_control *bch, int i) -{ - return bch->a_pow_tab[modulo(bch, i)]; -} - -static inline int a_log(struct bch_control *bch, unsigned int x) -{ - return bch->a_log_tab[x]; -} - -static inline int a_ilog(struct bch_control *bch, unsigned int x) -{ - return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); -} - -/* - * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t - */ -static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, - unsigned int *syn) -{ - int i, j, s; - unsigned int m; - uint32_t poly; - const int t = GF_T(bch); - - s = bch->ecc_bits; - - /* make sure extra bits in last ecc word are cleared */ - m = ((unsigned int)s) & 31; - if (m) - ecc[s/32] &= ~((1u << (32-m))-1); - memset(syn, 0, 2*t*sizeof(*syn)); - - /* compute v(a^j) for j=1 .. 2t-1 */ - do { - poly = *ecc++; - s -= 32; - while (poly) { - i = deg(poly); - for (j = 0; j < 2*t; j += 2) - syn[j] ^= a_pow(bch, (j+1)*(i+s)); - - poly ^= (1 << i); - } - } while (s > 0); - - /* v(a^(2j)) = v(a^j)^2 */ - for (j = 0; j < t; j++) - syn[2*j+1] = gf_sqr(bch, syn[j]); -} - -static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) -{ - memcpy(dst, src, GF_POLY_SZ(src->deg)); -} - -static int compute_error_locator_polynomial(struct bch_control *bch, - const unsigned int *syn) -{ - const unsigned int t = GF_T(bch); - const unsigned int n = GF_N(bch); - unsigned int i, j, tmp, l, pd = 1, d = syn[0]; - struct gf_poly *elp = bch->elp; - struct gf_poly *pelp = bch->poly_2t[0]; - struct gf_poly *elp_copy = bch->poly_2t[1]; - int k, pp = -1; - - memset(pelp, 0, GF_POLY_SZ(2*t)); - memset(elp, 0, GF_POLY_SZ(2*t)); - - pelp->deg = 0; - pelp->c[0] = 1; - elp->deg = 0; - elp->c[0] = 1; - - /* use simplified binary Berlekamp-Massey algorithm */ - for (i = 0; (i < t) && (elp->deg <= t); i++) { - if (d) { - k = 2*i-pp; - gf_poly_copy(elp_copy, elp); - /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ - tmp = a_log(bch, d)+n-a_log(bch, pd); - for (j = 0; j <= pelp->deg; j++) { - if (pelp->c[j]) { - l = a_log(bch, pelp->c[j]); - elp->c[j+k] ^= a_pow(bch, tmp+l); - } - } - /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ - tmp = pelp->deg+k; - if (tmp > elp->deg) { - elp->deg = tmp; - gf_poly_copy(pelp, elp_copy); - pd = d; - pp = 2*i; - } - } - /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ - if (i < t-1) { - d = syn[2*i+2]; - for (j = 1; j <= elp->deg; j++) - d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); - } - } - dbg("elp=%s\n", gf_poly_str(elp)); - return (elp->deg > t) ? -1 : (int)elp->deg; -} - -/* - * solve a m x m linear system in GF(2) with an expected number of solutions, - * and return the number of found solutions - */ -static int solve_linear_system(struct bch_control *bch, unsigned int *rows, - unsigned int *sol, int nsol) -{ - const int m = GF_M(bch); - unsigned int tmp, mask; - int rem, c, r, p, k, param[m]; - - k = 0; - mask = 1 << m; - - /* Gaussian elimination */ - for (c = 0; c < m; c++) { - rem = 0; - p = c-k; - /* find suitable row for elimination */ - for (r = p; r < m; r++) { - if (rows[r] & mask) { - if (r != p) { - tmp = rows[r]; - rows[r] = rows[p]; - rows[p] = tmp; - } - rem = r+1; - break; - } - } - if (rem) { - /* perform elimination on remaining rows */ - tmp = rows[p]; - for (r = rem; r < m; r++) { - if (rows[r] & mask) - rows[r] ^= tmp; - } - } else { - /* elimination not needed, store defective row index */ - param[k++] = c; - } - mask >>= 1; - } - /* rewrite system, inserting fake parameter rows */ - if (k > 0) { - p = k; - for (r = m-1; r >= 0; r--) { - if ((r > m-1-k) && rows[r]) - /* system has no solution */ - return 0; - - rows[r] = (p && (r == param[p-1])) ? - p--, 1u << (m-r) : rows[r-p]; - } - } - - if (nsol != (1 << k)) - /* unexpected number of solutions */ - return 0; - - for (p = 0; p < nsol; p++) { - /* set parameters for p-th solution */ - for (c = 0; c < k; c++) - rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); - - /* compute unique solution */ - tmp = 0; - for (r = m-1; r >= 0; r--) { - mask = rows[r] & (tmp|1); - tmp |= parity(mask) << (m-r); - } - sol[p] = tmp >> 1; - } - return nsol; -} - -/* - * this function builds and solves a linear system for finding roots of a degree - * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). - */ -static int find_affine4_roots(struct bch_control *bch, unsigned int a, - unsigned int b, unsigned int c, - unsigned int *roots) -{ - int i, j, k; - const int m = GF_M(bch); - unsigned int mask = 0xff, t, rows[16] = {0,}; - - j = a_log(bch, b); - k = a_log(bch, a); - rows[0] = c; - - /* buid linear system to solve X^4+aX^2+bX+c = 0 */ - for (i = 0; i < m; i++) { - rows[i+1] = bch->a_pow_tab[4*i]^ - (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ - (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); - j++; - k += 2; - } - /* - * transpose 16x16 matrix before passing it to linear solver - * warning: this code assumes m < 16 - */ - for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { - for (k = 0; k < 16; k = (k+j+1) & ~j) { - t = ((rows[k] >> j)^rows[k+j]) & mask; - rows[k] ^= (t << j); - rows[k+j] ^= t; - } - } - return solve_linear_system(bch, rows, roots, 4); -} - -/* - * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) - */ -static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int n = 0; - - if (poly->c[0]) - /* poly[X] = bX+c with c!=0, root=c/b */ - roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ - bch->a_log_tab[poly->c[1]]); - return n; -} - -/* - * compute roots of a degree 2 polynomial over GF(2^m) - */ -static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int n = 0, i, l0, l1, l2; - unsigned int u, v, r; - - if (poly->c[0] && poly->c[1]) { - - l0 = bch->a_log_tab[poly->c[0]]; - l1 = bch->a_log_tab[poly->c[1]]; - l2 = bch->a_log_tab[poly->c[2]]; - - /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ - u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); - /* - * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): - * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = - * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) - * i.e. r and r+1 are roots iff Tr(u)=0 - */ - r = 0; - v = u; - while (v) { - i = deg(v); - r ^= bch->xi_tab[i]; - v ^= (1 << i); - } - /* verify root */ - if ((gf_sqr(bch, r)^r) == u) { - /* reverse z=a/bX transformation and compute log(1/r) */ - roots[n++] = modulo(bch, 2*GF_N(bch)-l1- - bch->a_log_tab[r]+l2); - roots[n++] = modulo(bch, 2*GF_N(bch)-l1- - bch->a_log_tab[r^1]+l2); - } - } - return n; -} - -/* - * compute roots of a degree 3 polynomial over GF(2^m) - */ -static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int i, n = 0; - unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; - - if (poly->c[0]) { - /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ - e3 = poly->c[3]; - c2 = gf_div(bch, poly->c[0], e3); - b2 = gf_div(bch, poly->c[1], e3); - a2 = gf_div(bch, poly->c[2], e3); - - /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ - c = gf_mul(bch, a2, c2); /* c = a2c2 */ - b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ - a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ - - /* find the 4 roots of this affine polynomial */ - if (find_affine4_roots(bch, a, b, c, tmp) == 4) { - /* remove a2 from final list of roots */ - for (i = 0; i < 4; i++) { - if (tmp[i] != a2) - roots[n++] = a_ilog(bch, tmp[i]); - } - } - } - return n; -} - -/* - * compute roots of a degree 4 polynomial over GF(2^m) - */ -static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int i, l, n = 0; - unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; - - if (poly->c[0] == 0) - return 0; - - /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ - e4 = poly->c[4]; - d = gf_div(bch, poly->c[0], e4); - c = gf_div(bch, poly->c[1], e4); - b = gf_div(bch, poly->c[2], e4); - a = gf_div(bch, poly->c[3], e4); - - /* use Y=1/X transformation to get an affine polynomial */ - if (a) { - /* first, eliminate cX by using z=X+e with ae^2+c=0 */ - if (c) { - /* compute e such that e^2 = c/a */ - f = gf_div(bch, c, a); - l = a_log(bch, f); - l += (l & 1) ? GF_N(bch) : 0; - e = a_pow(bch, l/2); - /* - * use transformation z=X+e: - * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d - * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d - * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d - * z^4 + az^3 + b'z^2 + d' - */ - d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; - b = gf_mul(bch, a, e)^b; - } - /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ - if (d == 0) - /* assume all roots have multiplicity 1 */ - return 0; - - c2 = gf_inv(bch, d); - b2 = gf_div(bch, a, d); - a2 = gf_div(bch, b, d); - } else { - /* polynomial is already affine */ - c2 = d; - b2 = c; - a2 = b; - } - /* find the 4 roots of this affine polynomial */ - if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { - for (i = 0; i < 4; i++) { - /* post-process roots (reverse transformations) */ - f = a ? gf_inv(bch, roots[i]) : roots[i]; - roots[i] = a_ilog(bch, f^e); - } - n = 4; - } - return n; -} - -/* - * build monic, log-based representation of a polynomial - */ -static void gf_poly_logrep(struct bch_control *bch, - const struct gf_poly *a, int *rep) -{ - int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); - - /* represent 0 values with -1; warning, rep[d] is not set to 1 */ - for (i = 0; i < d; i++) - rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; -} - -/* - * compute polynomial Euclidean division remainder in GF(2^m)[X] - */ -static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, - const struct gf_poly *b, int *rep) -{ - int la, p, m; - unsigned int i, j, *c = a->c; - const unsigned int d = b->deg; - - if (a->deg < d) - return; - - /* reuse or compute log representation of denominator */ - if (!rep) { - rep = bch->cache; - gf_poly_logrep(bch, b, rep); - } - - for (j = a->deg; j >= d; j--) { - if (c[j]) { - la = a_log(bch, c[j]); - p = j-d; - for (i = 0; i < d; i++, p++) { - m = rep[i]; - if (m >= 0) - c[p] ^= bch->a_pow_tab[mod_s(bch, - m+la)]; - } - } - } - a->deg = d-1; - while (!c[a->deg] && a->deg) - a->deg--; -} - -/* - * compute polynomial Euclidean division quotient in GF(2^m)[X] - */ -static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, - const struct gf_poly *b, struct gf_poly *q) -{ - if (a->deg >= b->deg) { - q->deg = a->deg-b->deg; - /* compute a mod b (modifies a) */ - gf_poly_mod(bch, a, b, NULL); - /* quotient is stored in upper part of polynomial a */ - memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); - } else { - q->deg = 0; - q->c[0] = 0; - } -} - -/* - * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] - */ -static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, - struct gf_poly *b) -{ - struct gf_poly *tmp; - - dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); - - if (a->deg < b->deg) { - tmp = b; - b = a; - a = tmp; - } - - while (b->deg > 0) { - gf_poly_mod(bch, a, b, NULL); - tmp = b; - b = a; - a = tmp; - } - - dbg("%s\n", gf_poly_str(a)); - - return a; -} - -/* - * Given a polynomial f and an integer k, compute Tr(a^kX) mod f - * This is used in Berlekamp Trace algorithm for splitting polynomials - */ -static void compute_trace_bk_mod(struct bch_control *bch, int k, - const struct gf_poly *f, struct gf_poly *z, - struct gf_poly *out) -{ - const int m = GF_M(bch); - int i, j; - - /* z contains z^2j mod f */ - z->deg = 1; - z->c[0] = 0; - z->c[1] = bch->a_pow_tab[k]; - - out->deg = 0; - memset(out, 0, GF_POLY_SZ(f->deg)); - - /* compute f log representation only once */ - gf_poly_logrep(bch, f, bch->cache); - - for (i = 0; i < m; i++) { - /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ - for (j = z->deg; j >= 0; j--) { - out->c[j] ^= z->c[j]; - z->c[2*j] = gf_sqr(bch, z->c[j]); - z->c[2*j+1] = 0; - } - if (z->deg > out->deg) - out->deg = z->deg; - - if (i < m-1) { - z->deg *= 2; - /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ - gf_poly_mod(bch, z, f, bch->cache); - } - } - while (!out->c[out->deg] && out->deg) - out->deg--; - - dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); -} - -/* - * factor a polynomial using Berlekamp Trace algorithm (BTA) - */ -static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, - struct gf_poly **g, struct gf_poly **h) -{ - struct gf_poly *f2 = bch->poly_2t[0]; - struct gf_poly *q = bch->poly_2t[1]; - struct gf_poly *tk = bch->poly_2t[2]; - struct gf_poly *z = bch->poly_2t[3]; - struct gf_poly *gcd; - - dbg("factoring %s...\n", gf_poly_str(f)); - - *g = f; - *h = NULL; - - /* tk = Tr(a^k.X) mod f */ - compute_trace_bk_mod(bch, k, f, z, tk); - - if (tk->deg > 0) { - /* compute g = gcd(f, tk) (destructive operation) */ - gf_poly_copy(f2, f); - gcd = gf_poly_gcd(bch, f2, tk); - if (gcd->deg < f->deg) { - /* compute h=f/gcd(f,tk); this will modify f and q */ - gf_poly_div(bch, f, gcd, q); - /* store g and h in-place (clobbering f) */ - *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; - gf_poly_copy(*g, gcd); - gf_poly_copy(*h, q); - } - } -} - -/* - * find roots of a polynomial, using BTZ algorithm; see the beginning of this - * file for details - */ -static int find_poly_roots(struct bch_control *bch, unsigned int k, - struct gf_poly *poly, unsigned int *roots) -{ - int cnt; - struct gf_poly *f1, *f2; - - switch (poly->deg) { - /* handle low degree polynomials with ad hoc techniques */ - case 1: - cnt = find_poly_deg1_roots(bch, poly, roots); - break; - case 2: - cnt = find_poly_deg2_roots(bch, poly, roots); - break; - case 3: - cnt = find_poly_deg3_roots(bch, poly, roots); - break; - case 4: - cnt = find_poly_deg4_roots(bch, poly, roots); - break; - default: - /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ - cnt = 0; - if (poly->deg && (k <= GF_M(bch))) { - factor_polynomial(bch, k, poly, &f1, &f2); - if (f1) - cnt += find_poly_roots(bch, k+1, f1, roots); - if (f2) - cnt += find_poly_roots(bch, k+1, f2, roots+cnt); - } - break; - } - return cnt; -} - -#if defined(USE_CHIEN_SEARCH) -/* - * exhaustive root search (Chien) implementation - not used, included only for - * reference/comparison tests - */ -static int chien_search(struct bch_control *bch, unsigned int len, - struct gf_poly *p, unsigned int *roots) -{ - int m; - unsigned int i, j, syn, syn0, count = 0; - const unsigned int k = 8*len+bch->ecc_bits; - - /* use a log-based representation of polynomial */ - gf_poly_logrep(bch, p, bch->cache); - bch->cache[p->deg] = 0; - syn0 = gf_div(bch, p->c[0], p->c[p->deg]); - - for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { - /* compute elp(a^i) */ - for (j = 1, syn = syn0; j <= p->deg; j++) { - m = bch->cache[j]; - if (m >= 0) - syn ^= a_pow(bch, m+j*i); - } - if (syn == 0) { - roots[count++] = GF_N(bch)-i; - if (count == p->deg) - break; - } - } - return (count == p->deg) ? count : 0; -} -#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) -#endif /* USE_CHIEN_SEARCH */ - -/** - * 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. - */ -int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, - const uint8_t *recv_ecc, const uint8_t *calc_ecc, - const unsigned int *syn, unsigned int *errloc) -{ - const unsigned int ecc_words = BCH_ECC_WORDS(bch); - unsigned int nbits; - int i, err, nroots; - uint32_t sum; - - /* sanity check: make sure data length can be handled */ - if ( len > ((bch->n-bch->ecc_bits+7)/8)) - return -EINVAL; - - /* if caller does not provide syndromes, compute them */ - if (!syn) { - if (!calc_ecc) { - /* compute received data ecc into an internal buffer */ - if (!data || !recv_ecc) - return -EINVAL; - encode_bch(bch, data, len, NULL); - } else { - /* load provided calculated ecc */ - load_ecc8(bch, bch->ecc_buf, calc_ecc); - } - /* load received ecc or assume it was XORed in calc_ecc */ - if (recv_ecc) { - load_ecc8(bch, bch->ecc_buf2, recv_ecc); - /* XOR received and calculated ecc */ - for (i = 0, sum = 0; i < (int)ecc_words; i++) { - bch->ecc_buf[i] ^= bch->ecc_buf2[i]; - sum |= bch->ecc_buf[i]; - } - if (!sum) - /* no error found */ - return 0; - } - compute_syndromes(bch, bch->ecc_buf, bch->syn); - syn = bch->syn; - } - - err = compute_error_locator_polynomial(bch, syn); - if (err > 0) { - nroots = find_poly_roots(bch, 1, bch->elp, errloc); - if (err != nroots) - err = -1; - } - if (err > 0) { - /* post-process raw error locations for easier correction */ - nbits = (len*8)+bch->ecc_bits; - for (i = 0; i < err; i++) { - if (errloc[i] >= nbits) { - err = -1; - break; - } - errloc[i] = nbits-1-errloc[i]; - errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); - } - } - return (err >= 0) ? err : -EBADMSG; -} - -/* - * generate Galois field lookup tables - */ -static int build_gf_tables(struct bch_control *bch, unsigned int poly) -{ - unsigned int i, x = 1; - const unsigned int k = 1 << deg(poly); - - /* primitive polynomial must be of degree m */ - if (k != (1u << GF_M(bch))) - return -1; - - for (i = 0; i < GF_N(bch); i++) { - bch->a_pow_tab[i] = x; - bch->a_log_tab[x] = i; - if (i && (x == 1)) - /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; - bch->a_log_tab[0] = 0; - - return 0; -} - -/* - * compute generator polynomial remainder tables for fast encoding - */ -static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) -{ - int i, j, b, d; - uint32_t data, hi, lo, *tab; - const int l = BCH_ECC_WORDS(bch); - const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); - const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); - - memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); - - for (i = 0; i < 256; i++) { - /* p(X)=i is a small polynomial of weight <= 8 */ - for (b = 0; b < 4; b++) { - /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ - tab = bch->mod8_tab + (b*256+i)*l; - data = i << (8*b); - while (data) { - d = deg(data); - /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ - data ^= g[0] >> (31-d); - for (j = 0; j < ecclen; j++) { - hi = (d < 31) ? g[j] << (d+1) : 0; - lo = (j+1 < plen) ? - g[j+1] >> (31-d) : 0; - tab[j] ^= hi|lo; - } - } - } - } -} - -/* - * build a base for factoring degree 2 polynomials - */ -static int build_deg2_base(struct bch_control *bch) -{ - const int m = GF_M(bch); - int i, j, r; - unsigned int sum, x, y, remaining, ak = 0, xi[m]; - - /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ - for (i = 0; i < m; i++) { - for (j = 0, sum = 0; j < m; j++) - sum ^= a_pow(bch, i*(1 << j)); - - if (sum) { - ak = bch->a_pow_tab[i]; - break; - } - } - /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ - remaining = m; - memset(xi, 0, sizeof(xi)); - - for (x = 0; (x <= GF_N(bch)) && remaining; x++) { - y = gf_sqr(bch, x)^x; - for (i = 0; i < 2; i++) { - r = a_log(bch, y); - if (y && (r < m) && !xi[r]) { - bch->xi_tab[r] = x; - xi[r] = 1; - remaining--; - dbg("x%d = %x\n", r, x); - break; - } - y ^= ak; - } - } - /* should not happen but check anyway */ - return remaining ? -1 : 0; -} - -static void *bch_alloc(size_t size, int *err) -{ - void *ptr; - - ptr = malloc(size); - if (ptr == NULL) - *err = 1; - return ptr; -} - -/* - * compute generator polynomial for given (m,t) parameters. - */ -static uint32_t *compute_generator_polynomial(struct bch_control *bch) -{ - const unsigned int m = GF_M(bch); - const unsigned int t = GF_T(bch); - int n, err = 0; - unsigned int i, j, nbits, r, word, *roots; - struct gf_poly *g; - uint32_t *genpoly; - - g = (struct gf_poly*)bch_alloc(GF_POLY_SZ(m*t), &err); - roots = (unsigned int*)bch_alloc((bch->n+1)*sizeof(*roots), &err); - genpoly = (uint32_t*)bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); - - if (err) { - free(genpoly); - genpoly = NULL; - goto finish; - } - - /* enumerate all roots of g(X) */ - memset(roots , 0, (bch->n+1)*sizeof(*roots)); - for (i = 0; i < t; i++) { - for (j = 0, r = 2*i+1; j < m; j++) { - roots[r] = 1; - r = mod_s(bch, 2*r); - } - } - /* build generator polynomial g(X) */ - g->deg = 0; - g->c[0] = 1; - for (i = 0; i < GF_N(bch); i++) { - if (roots[i]) { - /* multiply g(X) by (X+root) */ - r = bch->a_pow_tab[i]; - g->c[g->deg+1] = 1; - for (j = g->deg; j > 0; j--) - g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; - - g->c[0] = gf_mul(bch, g->c[0], r); - g->deg++; - } - } - /* store left-justified binary representation of g(X) */ - n = g->deg+1; - i = 0; - - while (n > 0) { - nbits = (n > 32) ? 32 : n; - for (j = 0, word = 0; j < nbits; j++) { - if (g->c[n-1-j]) - word |= 1u << (31-j); - } - genpoly[i++] = word; - n -= nbits; - } - bch->ecc_bits = g->deg; - -finish: - free(g); - free(roots); - - return genpoly; -} - -/** - * 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. - */ -struct bch_control *init_bch(int m, int t, unsigned int prim_poly) -{ - int err = 0; - unsigned int i, words; - uint32_t *genpoly; - struct bch_control *bch = NULL; - - const int min_m = 5; - const int max_m = 15; - - /* default primitive polynomials */ - static const unsigned int prim_poly_tab[] = { - 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, - 0x402b, 0x8003, - }; - - if ((m < min_m) || (m > max_m)) - /* - * values of m greater than 15 are not currently supported; - * supporting m > 15 would require changing table base type - * (uint16_t) and a small patch in matrix transposition - */ - goto fail; - - /* sanity checks */ - if ((t < 1) || (m*t >= ((1 << m)-1))) - /* invalid t value */ - goto fail; - - /* select a primitive polynomial for generating GF(2^m) */ - if (prim_poly == 0) - prim_poly = prim_poly_tab[m-min_m]; - - bch = (struct bch_control*)malloc(sizeof(*bch)); - if (bch == NULL) - goto fail; - memset(bch,0,sizeof(*bch)); - - bch->m = m; - bch->t = t; - bch->n = (1 << m)-1; - words = DIV_ROUND_UP(m*t, 32); - bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); - bch->a_pow_tab = (uint16_t*)bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); - bch->a_log_tab = (uint16_t*)bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); - bch->mod8_tab = (uint32_t*)bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); - bch->ecc_buf = (uint32_t*)bch_alloc(words*sizeof(*bch->ecc_buf), &err); - bch->ecc_buf2 = (uint32_t*)bch_alloc(words*sizeof(*bch->ecc_buf2), &err); - bch->xi_tab = (unsigned int*)bch_alloc(m*sizeof(*bch->xi_tab), &err); - bch->syn = (unsigned int*)bch_alloc(2*t*sizeof(*bch->syn), &err); - bch->cache = (int*)bch_alloc(2*t*sizeof(*bch->cache), &err); - bch->elp = (struct gf_poly*)bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); - - for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) - bch->poly_2t[i] = (struct gf_poly*)bch_alloc(GF_POLY_SZ(2*t), &err); - - if (err) - goto fail; - - err = build_gf_tables(bch, prim_poly); - if (err) - goto fail; - - /* use generator polynomial for computing encoding tables */ - genpoly = compute_generator_polynomial(bch); - if (genpoly == NULL) - goto fail; - - build_mod8_tables(bch, genpoly); - free(genpoly); - - err = build_deg2_base(bch); - if (err) - goto fail; - - return bch; - -fail: - free_bch(bch); - return NULL; -} - -/** - * free_bch - free the BCH control structure - * @bch: BCH control structure to release - */ -void free_bch(struct bch_control *bch) -{ - unsigned int i; - - if (bch) { - free(bch->a_pow_tab); - free(bch->a_log_tab); - free(bch->mod8_tab); - free(bch->ecc_buf); - free(bch->ecc_buf2); - free(bch->xi_tab); - free(bch->syn); - free(bch->cache); - free(bch->elp); - - for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) - free(bch->poly_2t[i]); - - free(bch->databuf); - - free(bch); - } -} - -static void check_databuf(struct bch_control *bch) -{ - if (bch->databuf == NULL) - bch->databuf = (uint8_t*)malloc( ((bch->n - bch->ecc_bits)+7)/8 + bch->ecc_bytes ); -} - -static int pack_databuf( struct bch_control *bch , const uint8_t *data) -{ - const int K = bch->n - bch->ecc_bits; - int k; - int ndatabytes = (K+7)/8; - int nPad=ndatabytes*8 - K; - uint8_t * bytes; - check_databuf(bch); - bytes = bch->databuf; - memset(bytes,0,ndatabytes); - for (k=0;k>3] |= mask; - } - return ndatabytes; -} - -/* - * - * */ -static void unpack_eccbits( struct bch_control *bch , uint8_t * ecc) -{ - int k; - uint8_t * ecc_bytes; - check_databuf(bch); - ecc_bytes = bch->databuf + ((bch->n - bch->ecc_bits)+7)/8; - // expand ecc bytes to bits - for (k=0;kecc_bits;++k) - ecc[k] = (ecc_bytes[k>>3] & (1<<(7-(k&7))))>0; -} - -static void pack_eccbits(struct bch_control *bch ,const uint8_t * ecc) -{ - int k; - uint8_t * ecc_bytes; - check_databuf(bch); - ecc_bytes = bch->databuf + ((bch->n - bch->ecc_bits)+7)/8; - // expand ecc bytes to bits - memset(ecc_bytes,0,bch->ecc_bytes); - for (k=0;kecc_bits;++k) { - int bit = (ecc[k]&1)!=0; // use only the LSB (can allow sloppy but nice feature of sending in ASCII '0' and '1') - uint8_t mask = (1<<(7-(k&7))); - if (bit) - ecc_bytes[k>>3] |= mask; - } -} - - -/** - * encodebits_bch - calculate BCH ecc parity of data - * @bch: BCH control structure - * @data: data bits to encode , length= bch->n - bch->ecc_bits - * @ecc: output ecc parity bits, length = bch->ecc_bits - * - * 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. - */ -void encodebits_bch(struct bch_control *bch, const uint8_t *data, uint8_t *ecc) -{ - int ndatabytes = pack_databuf(bch,data); - uint8_t * ecc_bytes = bch->databuf + ndatabytes; - memset(ecc_bytes,0,bch->ecc_bytes); - encode_bch(bch,bch->databuf,ndatabytes,ecc_bytes); - unpack_eccbits(bch,ecc); -} - -/** - * decodebits_bch - decode received codeword bits and find error locations - * @bch: BCH control structure - * @databits: received data, length = bch->n - bch->ecc_bits - * @recv_ecc_bits: received ecc, length = bch->ecc_bits - * @errloc: output array of error locations - * - * Returns: - * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if - * invalid parameters were provided - * - * if (errloc[i] < bch->n - bch->ecc_bits ), then - * databits[errloc[i]] is in error - * otherwise - * the i-th error is located in ecc (no need for data correction) - * - * Note that this function does not perform any data correction by itself, it - * merely indicates error locations. - */ -int decodebits_bch(struct bch_control *bch, const uint8_t *data, const uint8_t *recv_ecc, unsigned int *errloc) -{ - int nbytes; - int nerr; - - if ( (data==NULL) ||(recv_ecc==NULL)) { - return -EINVAL; // TODO handle the same calling conventions as decode_bch - } - - nbytes = pack_databuf(bch,data); - - pack_eccbits(bch,recv_ecc); - - nerr = decode_bch(bch, bch->databuf, nbytes, bch->databuf + nbytes,NULL,NULL,errloc); - if (nerr>0) { - const int K = bch->n - bch->ecc_bits; - int nPad=((K+7)/8)*8 - K; - // correct the errloc positions - int k; - for (k=0;k>3) < len) - data[bi>>3] ^= (1<<(bi&7)); - } - -} - -/** - * correctbits_bch - correct error locations as found in decodebits_bch - * @bch,@databits,@errloc: same as a previous call to decodebits_bch - * @nerr: returned from decodebits_bch - */ -void correctbits_bch(struct bch_control *bch, uint8_t *databits, unsigned int *errloc, int nerr) -{ - const int m = bch->n - bch->ecc_bits; - int i; - for (i=0;i - * - * Description: - * - * This library provides runtime configurable encoding/decoding of binary - * Bose-Chaudhuri-Hocquenghem (BCH) codes. -*/ -#ifndef _BCH_H -#define _BCH_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * 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 - */ -struct bch_control { - unsigned int m; - unsigned int n; - unsigned int t; - unsigned int ecc_bits; - unsigned int ecc_bytes; -/* private: */ - uint16_t *a_pow_tab; - uint16_t *a_log_tab; - uint32_t *mod8_tab; - uint32_t *ecc_buf; - uint32_t *ecc_buf2; - unsigned int *xi_tab; - unsigned int *syn; - int *cache; - struct gf_poly *elp; - struct gf_poly *poly_2t[4]; - uint8_t *databuf; -}; - -struct bch_control *init_bch(int m, int t, unsigned int prim_poly); - -void free_bch(struct bch_control *bch); - -void encode_bch(struct bch_control *bch, const uint8_t *data, - unsigned int len, uint8_t *ecc); - -void encodebits_bch(struct bch_control *bch, const uint8_t *data, uint8_t *ecc); - -int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, - const uint8_t *recv_ecc, const uint8_t *calc_ecc, - const unsigned int *syn, unsigned int *errloc); - -int decodebits_bch(struct bch_control *bch, const uint8_t *data, - const uint8_t *recv_ecc, unsigned int *errloc); - - -void correct_bch(struct bch_control *bch, uint8_t *data,unsigned int len, unsigned int *errloc, int nerr); - -void correctbits_bch(struct bch_control *bch, uint8_t *databits, unsigned int *errloc, int nerr); - - -#ifdef __cplusplus -} -#endif - -#endif /* _BCH_H */ diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 47eaa43..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - unsafe { - let _c = init_bch(5, 2, 37); - } - } -} -- cgit v1.3.1