18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Generic Reed Solomon encoder / decoder library 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2002, Phil Karn, KA9Q 68c2ecf20Sopenharmony_ci * May be used under the terms of the GNU General Public License (GPL) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de) 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Generic data width independent code which is included by the wrappers. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct rs_codec *rs = rsc->codec; 148c2ecf20Sopenharmony_ci int deg_lambda, el, deg_omega; 158c2ecf20Sopenharmony_ci int i, j, r, k, pad; 168c2ecf20Sopenharmony_ci int nn = rs->nn; 178c2ecf20Sopenharmony_ci int nroots = rs->nroots; 188c2ecf20Sopenharmony_ci int fcr = rs->fcr; 198c2ecf20Sopenharmony_ci int prim = rs->prim; 208c2ecf20Sopenharmony_ci int iprim = rs->iprim; 218c2ecf20Sopenharmony_ci uint16_t *alpha_to = rs->alpha_to; 228c2ecf20Sopenharmony_ci uint16_t *index_of = rs->index_of; 238c2ecf20Sopenharmony_ci uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error; 248c2ecf20Sopenharmony_ci int count = 0; 258c2ecf20Sopenharmony_ci int num_corrected; 268c2ecf20Sopenharmony_ci uint16_t msk = (uint16_t) rs->nn; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* 298c2ecf20Sopenharmony_ci * The decoder buffers are in the rs control struct. They are 308c2ecf20Sopenharmony_ci * arrays sized [nroots + 1] 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci uint16_t *lambda = rsc->buffers + RS_DECODE_LAMBDA * (nroots + 1); 338c2ecf20Sopenharmony_ci uint16_t *syn = rsc->buffers + RS_DECODE_SYN * (nroots + 1); 348c2ecf20Sopenharmony_ci uint16_t *b = rsc->buffers + RS_DECODE_B * (nroots + 1); 358c2ecf20Sopenharmony_ci uint16_t *t = rsc->buffers + RS_DECODE_T * (nroots + 1); 368c2ecf20Sopenharmony_ci uint16_t *omega = rsc->buffers + RS_DECODE_OMEGA * (nroots + 1); 378c2ecf20Sopenharmony_ci uint16_t *root = rsc->buffers + RS_DECODE_ROOT * (nroots + 1); 388c2ecf20Sopenharmony_ci uint16_t *reg = rsc->buffers + RS_DECODE_REG * (nroots + 1); 398c2ecf20Sopenharmony_ci uint16_t *loc = rsc->buffers + RS_DECODE_LOC * (nroots + 1); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* Check length parameter for validity */ 428c2ecf20Sopenharmony_ci pad = nn - nroots - len; 438c2ecf20Sopenharmony_ci BUG_ON(pad < 0 || pad >= nn - nroots); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Does the caller provide the syndrome ? */ 468c2ecf20Sopenharmony_ci if (s != NULL) { 478c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) { 488c2ecf20Sopenharmony_ci /* The syndrome is in index form, 498c2ecf20Sopenharmony_ci * so nn represents zero 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci if (s[i] != nn) 528c2ecf20Sopenharmony_ci goto decode; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* syndrome is zero, no errors to correct */ 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* form the syndromes; i.e., evaluate data(x) at roots of 608c2ecf20Sopenharmony_ci * g(x) */ 618c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) 628c2ecf20Sopenharmony_ci syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci for (j = 1; j < len; j++) { 658c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) { 668c2ecf20Sopenharmony_ci if (syn[i] == 0) { 678c2ecf20Sopenharmony_ci syn[i] = (((uint16_t) data[j]) ^ 688c2ecf20Sopenharmony_ci invmsk) & msk; 698c2ecf20Sopenharmony_ci } else { 708c2ecf20Sopenharmony_ci syn[i] = ((((uint16_t) data[j]) ^ 718c2ecf20Sopenharmony_ci invmsk) & msk) ^ 728c2ecf20Sopenharmony_ci alpha_to[rs_modnn(rs, index_of[syn[i]] + 738c2ecf20Sopenharmony_ci (fcr + i) * prim)]; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (j = 0; j < nroots; j++) { 798c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) { 808c2ecf20Sopenharmony_ci if (syn[i] == 0) { 818c2ecf20Sopenharmony_ci syn[i] = ((uint16_t) par[j]) & msk; 828c2ecf20Sopenharmony_ci } else { 838c2ecf20Sopenharmony_ci syn[i] = (((uint16_t) par[j]) & msk) ^ 848c2ecf20Sopenharmony_ci alpha_to[rs_modnn(rs, index_of[syn[i]] + 858c2ecf20Sopenharmony_ci (fcr+i)*prim)]; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci s = syn; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Convert syndromes to index form, checking for nonzero condition */ 928c2ecf20Sopenharmony_ci syn_error = 0; 938c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) { 948c2ecf20Sopenharmony_ci syn_error |= s[i]; 958c2ecf20Sopenharmony_ci s[i] = index_of[s[i]]; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (!syn_error) { 998c2ecf20Sopenharmony_ci /* if syndrome is zero, data[] is a codeword and there are no 1008c2ecf20Sopenharmony_ci * errors to correct. So return data[] unmodified 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci decode: 1068c2ecf20Sopenharmony_ci memset(&lambda[1], 0, nroots * sizeof(lambda[0])); 1078c2ecf20Sopenharmony_ci lambda[0] = 1; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (no_eras > 0) { 1108c2ecf20Sopenharmony_ci /* Init lambda to be the erasure locator polynomial */ 1118c2ecf20Sopenharmony_ci lambda[1] = alpha_to[rs_modnn(rs, 1128c2ecf20Sopenharmony_ci prim * (nn - 1 - (eras_pos[0] + pad)))]; 1138c2ecf20Sopenharmony_ci for (i = 1; i < no_eras; i++) { 1148c2ecf20Sopenharmony_ci u = rs_modnn(rs, prim * (nn - 1 - (eras_pos[i] + pad))); 1158c2ecf20Sopenharmony_ci for (j = i + 1; j > 0; j--) { 1168c2ecf20Sopenharmony_ci tmp = index_of[lambda[j - 1]]; 1178c2ecf20Sopenharmony_ci if (tmp != nn) { 1188c2ecf20Sopenharmony_ci lambda[j] ^= 1198c2ecf20Sopenharmony_ci alpha_to[rs_modnn(rs, u + tmp)]; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for (i = 0; i < nroots + 1; i++) 1268c2ecf20Sopenharmony_ci b[i] = index_of[lambda[i]]; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Begin Berlekamp-Massey algorithm to determine error+erasure 1308c2ecf20Sopenharmony_ci * locator polynomial 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci r = no_eras; 1338c2ecf20Sopenharmony_ci el = no_eras; 1348c2ecf20Sopenharmony_ci while (++r <= nroots) { /* r is the step number */ 1358c2ecf20Sopenharmony_ci /* Compute discrepancy at the r-th step in poly-form */ 1368c2ecf20Sopenharmony_ci discr_r = 0; 1378c2ecf20Sopenharmony_ci for (i = 0; i < r; i++) { 1388c2ecf20Sopenharmony_ci if ((lambda[i] != 0) && (s[r - i - 1] != nn)) { 1398c2ecf20Sopenharmony_ci discr_r ^= 1408c2ecf20Sopenharmony_ci alpha_to[rs_modnn(rs, 1418c2ecf20Sopenharmony_ci index_of[lambda[i]] + 1428c2ecf20Sopenharmony_ci s[r - i - 1])]; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci discr_r = index_of[discr_r]; /* Index form */ 1468c2ecf20Sopenharmony_ci if (discr_r == nn) { 1478c2ecf20Sopenharmony_ci /* 2 lines below: B(x) <-- x*B(x) */ 1488c2ecf20Sopenharmony_ci memmove (&b[1], b, nroots * sizeof (b[0])); 1498c2ecf20Sopenharmony_ci b[0] = nn; 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */ 1528c2ecf20Sopenharmony_ci t[0] = lambda[0]; 1538c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) { 1548c2ecf20Sopenharmony_ci if (b[i] != nn) { 1558c2ecf20Sopenharmony_ci t[i + 1] = lambda[i + 1] ^ 1568c2ecf20Sopenharmony_ci alpha_to[rs_modnn(rs, discr_r + 1578c2ecf20Sopenharmony_ci b[i])]; 1588c2ecf20Sopenharmony_ci } else 1598c2ecf20Sopenharmony_ci t[i + 1] = lambda[i + 1]; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci if (2 * el <= r + no_eras - 1) { 1628c2ecf20Sopenharmony_ci el = r + no_eras - el; 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * 2 lines below: B(x) <-- inv(discr_r) * 1658c2ecf20Sopenharmony_ci * lambda(x) 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci for (i = 0; i <= nroots; i++) { 1688c2ecf20Sopenharmony_ci b[i] = (lambda[i] == 0) ? nn : 1698c2ecf20Sopenharmony_ci rs_modnn(rs, index_of[lambda[i]] 1708c2ecf20Sopenharmony_ci - discr_r + nn); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } else { 1738c2ecf20Sopenharmony_ci /* 2 lines below: B(x) <-- x*B(x) */ 1748c2ecf20Sopenharmony_ci memmove(&b[1], b, nroots * sizeof(b[0])); 1758c2ecf20Sopenharmony_ci b[0] = nn; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci memcpy(lambda, t, (nroots + 1) * sizeof(t[0])); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Convert lambda to index form and compute deg(lambda(x)) */ 1828c2ecf20Sopenharmony_ci deg_lambda = 0; 1838c2ecf20Sopenharmony_ci for (i = 0; i < nroots + 1; i++) { 1848c2ecf20Sopenharmony_ci lambda[i] = index_of[lambda[i]]; 1858c2ecf20Sopenharmony_ci if (lambda[i] != nn) 1868c2ecf20Sopenharmony_ci deg_lambda = i; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (deg_lambda == 0) { 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * deg(lambda) is zero even though the syndrome is non-zero 1928c2ecf20Sopenharmony_ci * => uncorrectable error detected 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci return -EBADMSG; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Find roots of error+erasure locator polynomial by Chien search */ 1988c2ecf20Sopenharmony_ci memcpy(®[1], &lambda[1], nroots * sizeof(reg[0])); 1998c2ecf20Sopenharmony_ci count = 0; /* Number of roots of lambda(x) */ 2008c2ecf20Sopenharmony_ci for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) { 2018c2ecf20Sopenharmony_ci q = 1; /* lambda[0] is always 0 */ 2028c2ecf20Sopenharmony_ci for (j = deg_lambda; j > 0; j--) { 2038c2ecf20Sopenharmony_ci if (reg[j] != nn) { 2048c2ecf20Sopenharmony_ci reg[j] = rs_modnn(rs, reg[j] + j); 2058c2ecf20Sopenharmony_ci q ^= alpha_to[reg[j]]; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci if (q != 0) 2098c2ecf20Sopenharmony_ci continue; /* Not a root */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (k < pad) { 2128c2ecf20Sopenharmony_ci /* Impossible error location. Uncorrectable error. */ 2138c2ecf20Sopenharmony_ci return -EBADMSG; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* store root (index-form) and error location number */ 2178c2ecf20Sopenharmony_ci root[count] = i; 2188c2ecf20Sopenharmony_ci loc[count] = k; 2198c2ecf20Sopenharmony_ci /* If we've already found max possible roots, 2208c2ecf20Sopenharmony_ci * abort the search to save time 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci if (++count == deg_lambda) 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci if (deg_lambda != count) { 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * deg(lambda) unequal to number of roots => uncorrectable 2288c2ecf20Sopenharmony_ci * error detected 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci return -EBADMSG; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo 2348c2ecf20Sopenharmony_ci * x**nroots). in index form. Also find deg(omega). 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci deg_omega = deg_lambda - 1; 2378c2ecf20Sopenharmony_ci for (i = 0; i <= deg_omega; i++) { 2388c2ecf20Sopenharmony_ci tmp = 0; 2398c2ecf20Sopenharmony_ci for (j = i; j >= 0; j--) { 2408c2ecf20Sopenharmony_ci if ((s[i - j] != nn) && (lambda[j] != nn)) 2418c2ecf20Sopenharmony_ci tmp ^= 2428c2ecf20Sopenharmony_ci alpha_to[rs_modnn(rs, s[i - j] + lambda[j])]; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci omega[i] = index_of[tmp]; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = 2498c2ecf20Sopenharmony_ci * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form 2508c2ecf20Sopenharmony_ci * Note: we reuse the buffer for b to store the correction pattern 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci num_corrected = 0; 2538c2ecf20Sopenharmony_ci for (j = count - 1; j >= 0; j--) { 2548c2ecf20Sopenharmony_ci num1 = 0; 2558c2ecf20Sopenharmony_ci for (i = deg_omega; i >= 0; i--) { 2568c2ecf20Sopenharmony_ci if (omega[i] != nn) 2578c2ecf20Sopenharmony_ci num1 ^= alpha_to[rs_modnn(rs, omega[i] + 2588c2ecf20Sopenharmony_ci i * root[j])]; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (num1 == 0) { 2628c2ecf20Sopenharmony_ci /* Nothing to correct at this position */ 2638c2ecf20Sopenharmony_ci b[j] = 0; 2648c2ecf20Sopenharmony_ci continue; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)]; 2688c2ecf20Sopenharmony_ci den = 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* lambda[i+1] for i even is the formal derivative 2718c2ecf20Sopenharmony_ci * lambda_pr of lambda[i] */ 2728c2ecf20Sopenharmony_ci for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) { 2738c2ecf20Sopenharmony_ci if (lambda[i + 1] != nn) { 2748c2ecf20Sopenharmony_ci den ^= alpha_to[rs_modnn(rs, lambda[i + 1] + 2758c2ecf20Sopenharmony_ci i * root[j])]; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci b[j] = alpha_to[rs_modnn(rs, index_of[num1] + 2808c2ecf20Sopenharmony_ci index_of[num2] + 2818c2ecf20Sopenharmony_ci nn - index_of[den])]; 2828c2ecf20Sopenharmony_ci num_corrected++; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * We compute the syndrome of the 'error' and check that it matches 2878c2ecf20Sopenharmony_ci * the syndrome of the received word 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) { 2908c2ecf20Sopenharmony_ci tmp = 0; 2918c2ecf20Sopenharmony_ci for (j = 0; j < count; j++) { 2928c2ecf20Sopenharmony_ci if (b[j] == 0) 2938c2ecf20Sopenharmony_ci continue; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci k = (fcr + i) * prim * (nn-loc[j]-1); 2968c2ecf20Sopenharmony_ci tmp ^= alpha_to[rs_modnn(rs, index_of[b[j]] + k)]; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (tmp != alpha_to[s[i]]) 3008c2ecf20Sopenharmony_ci return -EBADMSG; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * Store the error correction pattern, if a 3058c2ecf20Sopenharmony_ci * correction buffer is available 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci if (corr && eras_pos) { 3088c2ecf20Sopenharmony_ci j = 0; 3098c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 3108c2ecf20Sopenharmony_ci if (b[i]) { 3118c2ecf20Sopenharmony_ci corr[j] = b[i]; 3128c2ecf20Sopenharmony_ci eras_pos[j++] = loc[i] - pad; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci } else if (data && par) { 3168c2ecf20Sopenharmony_ci /* Apply error to data and parity */ 3178c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 3188c2ecf20Sopenharmony_ci if (loc[i] < (nn - nroots)) 3198c2ecf20Sopenharmony_ci data[loc[i] - pad] ^= b[i]; 3208c2ecf20Sopenharmony_ci else 3218c2ecf20Sopenharmony_ci par[loc[i] - pad - len] ^= b[i]; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return num_corrected; 3268c2ecf20Sopenharmony_ci} 327