18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Tests for Generic Reed Solomon encoder / decoder library 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Written by Ferdinand Blomqvist 68c2ecf20Sopenharmony_ci * Based on previous work by Phil Karn, KA9Q 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/rslib.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 128c2ecf20Sopenharmony_ci#include <linux/random.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cienum verbosity { 168c2ecf20Sopenharmony_ci V_SILENT, 178c2ecf20Sopenharmony_ci V_PROGRESS, 188c2ecf20Sopenharmony_ci V_CSUMMARY 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cienum method { 228c2ecf20Sopenharmony_ci CORR_BUFFER, 238c2ecf20Sopenharmony_ci CALLER_SYNDROME, 248c2ecf20Sopenharmony_ci IN_PLACE 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define __param(type, name, init, msg) \ 288c2ecf20Sopenharmony_ci static type name = init; \ 298c2ecf20Sopenharmony_ci module_param(name, type, 0444); \ 308c2ecf20Sopenharmony_ci MODULE_PARM_DESC(name, msg) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci__param(int, v, V_PROGRESS, "Verbosity level"); 338c2ecf20Sopenharmony_ci__param(int, ewsc, 1, "Erasures without symbol corruption"); 348c2ecf20Sopenharmony_ci__param(int, bc, 1, "Test for correct behaviour beyond error correction capacity"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct etab { 378c2ecf20Sopenharmony_ci int symsize; 388c2ecf20Sopenharmony_ci int genpoly; 398c2ecf20Sopenharmony_ci int fcs; 408c2ecf20Sopenharmony_ci int prim; 418c2ecf20Sopenharmony_ci int nroots; 428c2ecf20Sopenharmony_ci int ntrials; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* List of codes to test */ 468c2ecf20Sopenharmony_cistatic struct etab Tab[] = { 478c2ecf20Sopenharmony_ci {2, 0x7, 1, 1, 1, 100000 }, 488c2ecf20Sopenharmony_ci {3, 0xb, 1, 1, 2, 100000 }, 498c2ecf20Sopenharmony_ci {3, 0xb, 1, 1, 3, 100000 }, 508c2ecf20Sopenharmony_ci {3, 0xb, 2, 1, 4, 100000 }, 518c2ecf20Sopenharmony_ci {4, 0x13, 1, 1, 4, 10000 }, 528c2ecf20Sopenharmony_ci {5, 0x25, 1, 1, 6, 1000 }, 538c2ecf20Sopenharmony_ci {6, 0x43, 3, 1, 8, 1000 }, 548c2ecf20Sopenharmony_ci {7, 0x89, 1, 1, 14, 500 }, 558c2ecf20Sopenharmony_ci {8, 0x11d, 1, 1, 30, 100 }, 568c2ecf20Sopenharmony_ci {8, 0x187, 112, 11, 32, 100 }, 578c2ecf20Sopenharmony_ci {9, 0x211, 1, 1, 33, 80 }, 588c2ecf20Sopenharmony_ci {0, 0, 0, 0, 0, 0}, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct estat { 638c2ecf20Sopenharmony_ci int dwrong; 648c2ecf20Sopenharmony_ci int irv; 658c2ecf20Sopenharmony_ci int wepos; 668c2ecf20Sopenharmony_ci int nwords; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct bcstat { 708c2ecf20Sopenharmony_ci int rfail; 718c2ecf20Sopenharmony_ci int rsuccess; 728c2ecf20Sopenharmony_ci int noncw; 738c2ecf20Sopenharmony_ci int nwords; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct wspace { 778c2ecf20Sopenharmony_ci uint16_t *c; /* sent codeword */ 788c2ecf20Sopenharmony_ci uint16_t *r; /* received word */ 798c2ecf20Sopenharmony_ci uint16_t *s; /* syndrome */ 808c2ecf20Sopenharmony_ci uint16_t *corr; /* correction buffer */ 818c2ecf20Sopenharmony_ci int *errlocs; 828c2ecf20Sopenharmony_ci int *derrlocs; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct pad { 868c2ecf20Sopenharmony_ci int mult; 878c2ecf20Sopenharmony_ci int shift; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct pad pad_coef[] = { 918c2ecf20Sopenharmony_ci { 0, 0 }, 928c2ecf20Sopenharmony_ci { 1, 2 }, 938c2ecf20Sopenharmony_ci { 1, 1 }, 948c2ecf20Sopenharmony_ci { 3, 2 }, 958c2ecf20Sopenharmony_ci { 1, 0 }, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void free_ws(struct wspace *ws) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci if (!ws) 1018c2ecf20Sopenharmony_ci return; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci kfree(ws->errlocs); 1048c2ecf20Sopenharmony_ci kfree(ws->c); 1058c2ecf20Sopenharmony_ci kfree(ws); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic struct wspace *alloc_ws(struct rs_codec *rs) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci int nroots = rs->nroots; 1118c2ecf20Sopenharmony_ci struct wspace *ws; 1128c2ecf20Sopenharmony_ci int nn = rs->nn; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ws = kzalloc(sizeof(*ws), GFP_KERNEL); 1158c2ecf20Sopenharmony_ci if (!ws) 1168c2ecf20Sopenharmony_ci return NULL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ws->c = kmalloc_array(2 * (nn + nroots), 1198c2ecf20Sopenharmony_ci sizeof(uint16_t), GFP_KERNEL); 1208c2ecf20Sopenharmony_ci if (!ws->c) 1218c2ecf20Sopenharmony_ci goto err; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ws->r = ws->c + nn; 1248c2ecf20Sopenharmony_ci ws->s = ws->r + nn; 1258c2ecf20Sopenharmony_ci ws->corr = ws->s + nroots; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ws->errlocs = kmalloc_array(nn + nroots, sizeof(int), GFP_KERNEL); 1288c2ecf20Sopenharmony_ci if (!ws->errlocs) 1298c2ecf20Sopenharmony_ci goto err; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ws->derrlocs = ws->errlocs + nn; 1328c2ecf20Sopenharmony_ci return ws; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cierr: 1358c2ecf20Sopenharmony_ci free_ws(ws); 1368c2ecf20Sopenharmony_ci return NULL; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * Generates a random codeword and stores it in c. Generates random errors and 1428c2ecf20Sopenharmony_ci * erasures, and stores the random word with errors in r. Erasure positions are 1438c2ecf20Sopenharmony_ci * stored in derrlocs, while errlocs has one of three values in every position: 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * 0 if there is no error in this position; 1468c2ecf20Sopenharmony_ci * 1 if there is a symbol error in this position; 1478c2ecf20Sopenharmony_ci * 2 if there is an erasure without symbol corruption. 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Returns the number of corrupted symbols. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int get_rcw_we(struct rs_control *rs, struct wspace *ws, 1528c2ecf20Sopenharmony_ci int len, int errs, int eras) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int nroots = rs->codec->nroots; 1558c2ecf20Sopenharmony_ci int *derrlocs = ws->derrlocs; 1568c2ecf20Sopenharmony_ci int *errlocs = ws->errlocs; 1578c2ecf20Sopenharmony_ci int dlen = len - nroots; 1588c2ecf20Sopenharmony_ci int nn = rs->codec->nn; 1598c2ecf20Sopenharmony_ci uint16_t *c = ws->c; 1608c2ecf20Sopenharmony_ci uint16_t *r = ws->r; 1618c2ecf20Sopenharmony_ci int errval; 1628c2ecf20Sopenharmony_ci int errloc; 1638c2ecf20Sopenharmony_ci int i; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Load c with random data and encode */ 1668c2ecf20Sopenharmony_ci for (i = 0; i < dlen; i++) 1678c2ecf20Sopenharmony_ci c[i] = prandom_u32() & nn; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci memset(c + dlen, 0, nroots * sizeof(*c)); 1708c2ecf20Sopenharmony_ci encode_rs16(rs, c, dlen, c + dlen, 0); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Make copyand add errors and erasures */ 1738c2ecf20Sopenharmony_ci memcpy(r, c, len * sizeof(*r)); 1748c2ecf20Sopenharmony_ci memset(errlocs, 0, len * sizeof(*errlocs)); 1758c2ecf20Sopenharmony_ci memset(derrlocs, 0, nroots * sizeof(*derrlocs)); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Generating random errors */ 1788c2ecf20Sopenharmony_ci for (i = 0; i < errs; i++) { 1798c2ecf20Sopenharmony_ci do { 1808c2ecf20Sopenharmony_ci /* Error value must be nonzero */ 1818c2ecf20Sopenharmony_ci errval = prandom_u32() & nn; 1828c2ecf20Sopenharmony_ci } while (errval == 0); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci do { 1858c2ecf20Sopenharmony_ci /* Must not choose the same location twice */ 1868c2ecf20Sopenharmony_ci errloc = prandom_u32() % len; 1878c2ecf20Sopenharmony_ci } while (errlocs[errloc] != 0); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci errlocs[errloc] = 1; 1908c2ecf20Sopenharmony_ci r[errloc] ^= errval; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Generating random erasures */ 1948c2ecf20Sopenharmony_ci for (i = 0; i < eras; i++) { 1958c2ecf20Sopenharmony_ci do { 1968c2ecf20Sopenharmony_ci /* Must not choose the same location twice */ 1978c2ecf20Sopenharmony_ci errloc = prandom_u32() % len; 1988c2ecf20Sopenharmony_ci } while (errlocs[errloc] != 0); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci derrlocs[i] = errloc; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (ewsc && (prandom_u32() & 1)) { 2038c2ecf20Sopenharmony_ci /* Erasure with the symbol intact */ 2048c2ecf20Sopenharmony_ci errlocs[errloc] = 2; 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci /* Erasure with corrupted symbol */ 2078c2ecf20Sopenharmony_ci do { 2088c2ecf20Sopenharmony_ci /* Error value must be nonzero */ 2098c2ecf20Sopenharmony_ci errval = prandom_u32() & nn; 2108c2ecf20Sopenharmony_ci } while (errval == 0); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci errlocs[errloc] = 1; 2138c2ecf20Sopenharmony_ci r[errloc] ^= errval; 2148c2ecf20Sopenharmony_ci errs++; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return errs; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void fix_err(uint16_t *data, int nerrs, uint16_t *corr, int *errlocs) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int i; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci for (i = 0; i < nerrs; i++) 2268c2ecf20Sopenharmony_ci data[errlocs[i]] ^= corr[i]; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void compute_syndrome(struct rs_control *rsc, uint16_t *data, 2308c2ecf20Sopenharmony_ci int len, uint16_t *syn) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct rs_codec *rs = rsc->codec; 2338c2ecf20Sopenharmony_ci uint16_t *alpha_to = rs->alpha_to; 2348c2ecf20Sopenharmony_ci uint16_t *index_of = rs->index_of; 2358c2ecf20Sopenharmony_ci int nroots = rs->nroots; 2368c2ecf20Sopenharmony_ci int prim = rs->prim; 2378c2ecf20Sopenharmony_ci int fcr = rs->fcr; 2388c2ecf20Sopenharmony_ci int i, j; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Calculating syndrome */ 2418c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) { 2428c2ecf20Sopenharmony_ci syn[i] = data[0]; 2438c2ecf20Sopenharmony_ci for (j = 1; j < len; j++) { 2448c2ecf20Sopenharmony_ci if (syn[i] == 0) { 2458c2ecf20Sopenharmony_ci syn[i] = data[j]; 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci syn[i] = data[j] ^ 2488c2ecf20Sopenharmony_ci alpha_to[rs_modnn(rs, index_of[syn[i]] 2498c2ecf20Sopenharmony_ci + (fcr + i) * prim)]; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Convert to index form */ 2558c2ecf20Sopenharmony_ci for (i = 0; i < nroots; i++) 2568c2ecf20Sopenharmony_ci syn[i] = rs->index_of[syn[i]]; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* Test up to error correction capacity */ 2608c2ecf20Sopenharmony_cistatic void test_uc(struct rs_control *rs, int len, int errs, 2618c2ecf20Sopenharmony_ci int eras, int trials, struct estat *stat, 2628c2ecf20Sopenharmony_ci struct wspace *ws, int method) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int dlen = len - rs->codec->nroots; 2658c2ecf20Sopenharmony_ci int *derrlocs = ws->derrlocs; 2668c2ecf20Sopenharmony_ci int *errlocs = ws->errlocs; 2678c2ecf20Sopenharmony_ci uint16_t *corr = ws->corr; 2688c2ecf20Sopenharmony_ci uint16_t *c = ws->c; 2698c2ecf20Sopenharmony_ci uint16_t *r = ws->r; 2708c2ecf20Sopenharmony_ci uint16_t *s = ws->s; 2718c2ecf20Sopenharmony_ci int derrs, nerrs; 2728c2ecf20Sopenharmony_ci int i, j; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci for (j = 0; j < trials; j++) { 2758c2ecf20Sopenharmony_ci nerrs = get_rcw_we(rs, ws, len, errs, eras); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci switch (method) { 2788c2ecf20Sopenharmony_ci case CORR_BUFFER: 2798c2ecf20Sopenharmony_ci derrs = decode_rs16(rs, r, r + dlen, dlen, 2808c2ecf20Sopenharmony_ci NULL, eras, derrlocs, 0, corr); 2818c2ecf20Sopenharmony_ci fix_err(r, derrs, corr, derrlocs); 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci case CALLER_SYNDROME: 2848c2ecf20Sopenharmony_ci compute_syndrome(rs, r, len, s); 2858c2ecf20Sopenharmony_ci derrs = decode_rs16(rs, NULL, NULL, dlen, 2868c2ecf20Sopenharmony_ci s, eras, derrlocs, 0, corr); 2878c2ecf20Sopenharmony_ci fix_err(r, derrs, corr, derrlocs); 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case IN_PLACE: 2908c2ecf20Sopenharmony_ci derrs = decode_rs16(rs, r, r + dlen, dlen, 2918c2ecf20Sopenharmony_ci NULL, eras, derrlocs, 0, NULL); 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci default: 2948c2ecf20Sopenharmony_ci continue; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (derrs != nerrs) 2988c2ecf20Sopenharmony_ci stat->irv++; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (method != IN_PLACE) { 3018c2ecf20Sopenharmony_ci for (i = 0; i < derrs; i++) { 3028c2ecf20Sopenharmony_ci if (errlocs[derrlocs[i]] != 1) 3038c2ecf20Sopenharmony_ci stat->wepos++; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (memcmp(r, c, len * sizeof(*r))) 3088c2ecf20Sopenharmony_ci stat->dwrong++; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci stat->nwords += trials; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int ex_rs_helper(struct rs_control *rs, struct wspace *ws, 3148c2ecf20Sopenharmony_ci int len, int trials, int method) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci static const char * const desc[] = { 3178c2ecf20Sopenharmony_ci "Testing correction buffer interface...", 3188c2ecf20Sopenharmony_ci "Testing with caller provided syndrome...", 3198c2ecf20Sopenharmony_ci "Testing in-place interface..." 3208c2ecf20Sopenharmony_ci }; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci struct estat stat = {0, 0, 0, 0}; 3238c2ecf20Sopenharmony_ci int nroots = rs->codec->nroots; 3248c2ecf20Sopenharmony_ci int errs, eras, retval; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (v >= V_PROGRESS) 3278c2ecf20Sopenharmony_ci pr_info(" %s\n", desc[method]); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci for (errs = 0; errs <= nroots / 2; errs++) 3308c2ecf20Sopenharmony_ci for (eras = 0; eras <= nroots - 2 * errs; eras++) 3318c2ecf20Sopenharmony_ci test_uc(rs, len, errs, eras, trials, &stat, ws, method); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (v >= V_CSUMMARY) { 3348c2ecf20Sopenharmony_ci pr_info(" Decodes wrong: %d / %d\n", 3358c2ecf20Sopenharmony_ci stat.dwrong, stat.nwords); 3368c2ecf20Sopenharmony_ci pr_info(" Wrong return value: %d / %d\n", 3378c2ecf20Sopenharmony_ci stat.irv, stat.nwords); 3388c2ecf20Sopenharmony_ci if (method != IN_PLACE) 3398c2ecf20Sopenharmony_ci pr_info(" Wrong error position: %d\n", stat.wepos); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci retval = stat.dwrong + stat.wepos + stat.irv; 3438c2ecf20Sopenharmony_ci if (retval && v >= V_PROGRESS) 3448c2ecf20Sopenharmony_ci pr_warn(" FAIL: %d decoding failures!\n", retval); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return retval; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int exercise_rs(struct rs_control *rs, struct wspace *ws, 3508c2ecf20Sopenharmony_ci int len, int trials) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci int retval = 0; 3548c2ecf20Sopenharmony_ci int i; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (v >= V_PROGRESS) 3578c2ecf20Sopenharmony_ci pr_info("Testing up to error correction capacity...\n"); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci for (i = 0; i <= IN_PLACE; i++) 3608c2ecf20Sopenharmony_ci retval |= ex_rs_helper(rs, ws, len, trials, i); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return retval; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/* Tests for correct behaviour beyond error correction capacity */ 3668c2ecf20Sopenharmony_cistatic void test_bc(struct rs_control *rs, int len, int errs, 3678c2ecf20Sopenharmony_ci int eras, int trials, struct bcstat *stat, 3688c2ecf20Sopenharmony_ci struct wspace *ws) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci int nroots = rs->codec->nroots; 3718c2ecf20Sopenharmony_ci int dlen = len - nroots; 3728c2ecf20Sopenharmony_ci int *derrlocs = ws->derrlocs; 3738c2ecf20Sopenharmony_ci uint16_t *corr = ws->corr; 3748c2ecf20Sopenharmony_ci uint16_t *r = ws->r; 3758c2ecf20Sopenharmony_ci int derrs, j; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci for (j = 0; j < trials; j++) { 3788c2ecf20Sopenharmony_ci get_rcw_we(rs, ws, len, errs, eras); 3798c2ecf20Sopenharmony_ci derrs = decode_rs16(rs, r, r + dlen, dlen, 3808c2ecf20Sopenharmony_ci NULL, eras, derrlocs, 0, corr); 3818c2ecf20Sopenharmony_ci fix_err(r, derrs, corr, derrlocs); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (derrs >= 0) { 3848c2ecf20Sopenharmony_ci stat->rsuccess++; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* 3878c2ecf20Sopenharmony_ci * We check that the returned word is actually a 3888c2ecf20Sopenharmony_ci * codeword. The obious way to do this would be to 3898c2ecf20Sopenharmony_ci * compute the syndrome, but we don't want to replicate 3908c2ecf20Sopenharmony_ci * that code here. However, all the codes are in 3918c2ecf20Sopenharmony_ci * systematic form, and therefore we can encode the 3928c2ecf20Sopenharmony_ci * returned word, and see whether the parity changes or 3938c2ecf20Sopenharmony_ci * not. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci memset(corr, 0, nroots * sizeof(*corr)); 3968c2ecf20Sopenharmony_ci encode_rs16(rs, r, dlen, corr, 0); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (memcmp(r + dlen, corr, nroots * sizeof(*corr))) 3998c2ecf20Sopenharmony_ci stat->noncw++; 4008c2ecf20Sopenharmony_ci } else { 4018c2ecf20Sopenharmony_ci stat->rfail++; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci stat->nwords += trials; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int exercise_rs_bc(struct rs_control *rs, struct wspace *ws, 4088c2ecf20Sopenharmony_ci int len, int trials) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct bcstat stat = {0, 0, 0, 0}; 4118c2ecf20Sopenharmony_ci int nroots = rs->codec->nroots; 4128c2ecf20Sopenharmony_ci int errs, eras, cutoff; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (v >= V_PROGRESS) 4158c2ecf20Sopenharmony_ci pr_info("Testing beyond error correction capacity...\n"); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (errs = 1; errs <= nroots; errs++) { 4188c2ecf20Sopenharmony_ci eras = nroots - 2 * errs + 1; 4198c2ecf20Sopenharmony_ci if (eras < 0) 4208c2ecf20Sopenharmony_ci eras = 0; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci cutoff = nroots <= len - errs ? nroots : len - errs; 4238c2ecf20Sopenharmony_ci for (; eras <= cutoff; eras++) 4248c2ecf20Sopenharmony_ci test_bc(rs, len, errs, eras, trials, &stat, ws); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (v >= V_CSUMMARY) { 4288c2ecf20Sopenharmony_ci pr_info(" decoder gives up: %d / %d\n", 4298c2ecf20Sopenharmony_ci stat.rfail, stat.nwords); 4308c2ecf20Sopenharmony_ci pr_info(" decoder returns success: %d / %d\n", 4318c2ecf20Sopenharmony_ci stat.rsuccess, stat.nwords); 4328c2ecf20Sopenharmony_ci pr_info(" not a codeword: %d / %d\n", 4338c2ecf20Sopenharmony_ci stat.noncw, stat.rsuccess); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (stat.noncw && v >= V_PROGRESS) 4378c2ecf20Sopenharmony_ci pr_warn(" FAIL: %d silent failures!\n", stat.noncw); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return stat.noncw; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int run_exercise(struct etab *e) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci int nn = (1 << e->symsize) - 1; 4458c2ecf20Sopenharmony_ci int kk = nn - e->nroots; 4468c2ecf20Sopenharmony_ci struct rs_control *rsc; 4478c2ecf20Sopenharmony_ci int retval = -ENOMEM; 4488c2ecf20Sopenharmony_ci int max_pad = kk - 1; 4498c2ecf20Sopenharmony_ci int prev_pad = -1; 4508c2ecf20Sopenharmony_ci struct wspace *ws; 4518c2ecf20Sopenharmony_ci int i; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci rsc = init_rs(e->symsize, e->genpoly, e->fcs, e->prim, e->nroots); 4548c2ecf20Sopenharmony_ci if (!rsc) 4558c2ecf20Sopenharmony_ci return retval; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci ws = alloc_ws(rsc->codec); 4588c2ecf20Sopenharmony_ci if (!ws) 4598c2ecf20Sopenharmony_ci goto err; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci retval = 0; 4628c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pad_coef); i++) { 4638c2ecf20Sopenharmony_ci int pad = (pad_coef[i].mult * max_pad) >> pad_coef[i].shift; 4648c2ecf20Sopenharmony_ci int len = nn - pad; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (pad == prev_pad) 4678c2ecf20Sopenharmony_ci continue; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci prev_pad = pad; 4708c2ecf20Sopenharmony_ci if (v >= V_PROGRESS) { 4718c2ecf20Sopenharmony_ci pr_info("Testing (%d,%d)_%d code...\n", 4728c2ecf20Sopenharmony_ci len, kk - pad, nn + 1); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci retval |= exercise_rs(rsc, ws, len, e->ntrials); 4768c2ecf20Sopenharmony_ci if (bc) 4778c2ecf20Sopenharmony_ci retval |= exercise_rs_bc(rsc, ws, len, e->ntrials); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci free_ws(ws); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cierr: 4838c2ecf20Sopenharmony_ci free_rs(rsc); 4848c2ecf20Sopenharmony_ci return retval; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int __init test_rslib_init(void) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci int i, fail = 0; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci for (i = 0; Tab[i].symsize != 0 ; i++) { 4928c2ecf20Sopenharmony_ci int retval; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci retval = run_exercise(Tab + i); 4958c2ecf20Sopenharmony_ci if (retval < 0) 4968c2ecf20Sopenharmony_ci return -ENOMEM; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci fail |= retval; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (fail) 5028c2ecf20Sopenharmony_ci pr_warn("rslib: test failed\n"); 5038c2ecf20Sopenharmony_ci else 5048c2ecf20Sopenharmony_ci pr_info("rslib: test ok\n"); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return -EAGAIN; /* Fail will directly unload the module */ 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic void __exit test_rslib_exit(void) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cimodule_init(test_rslib_init) 5148c2ecf20Sopenharmony_cimodule_exit(test_rslib_exit) 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ferdinand Blomqvist"); 5188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Reed-Solomon library test"); 519