1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * KUnits tests for CRC16. 4 * 5 * Copyright (C) 2024, LKCAMP 6 * Author: Vinicius Peixoto <vpeixoto@lkcamp.dev> 7 * Author: Fabricio Gasperin <fgasperin@lkcamp.dev> 8 * Author: Enzo Bertoloti <ebertoloti@lkcamp.dev> 9 */ 10 #include <kunit/test.h> 11 #include <linux/crc16.h> 12 #include <linux/prandom.h> 13 14 #define CRC16_KUNIT_DATA_SIZE 4096 15 #define CRC16_KUNIT_TEST_SIZE 100 16 #define CRC16_KUNIT_SEED 0x12345678 17 18 /** 19 * struct crc16_test - CRC16 test data 20 * @crc: initial input value to CRC16 21 * @start: Start index within the data buffer 22 * @length: Length of the data 23 */ 24 static struct crc16_test { 25 u16 crc; 26 u16 start; 27 u16 length; 28 } tests[CRC16_KUNIT_TEST_SIZE]; 29 30 u8 data[CRC16_KUNIT_DATA_SIZE]; 31 32 33 /* Naive implementation of CRC16 for validation purposes */ 34 static inline u16 _crc16_naive_byte(u16 crc, u8 data) 35 { 36 u8 i = 0; 37 38 crc ^= (u16) data; 39 for (i = 0; i < 8; i++) { 40 if (crc & 0x01) 41 crc = (crc >> 1) ^ 0xa001; 42 else 43 crc = crc >> 1; 44 } 45 46 return crc; 47 } 48 49 50 static inline u16 _crc16_naive(u16 crc, u8 *buffer, size_t len) 51 { 52 while (len--) 53 crc = _crc16_naive_byte(crc, *buffer++); 54 return crc; 55 } 56 57 58 /* Small helper for generating pseudorandom 16-bit data */ 59 static inline u16 _rand16(void) 60 { 61 static u32 rand = CRC16_KUNIT_SEED; 62 63 rand = next_pseudo_random32(rand); 64 return rand & 0xFFFF; 65 } 66 67 68 static int crc16_init_test_data(struct kunit_suite *suite) 69 { 70 size_t i; 71 72 /* Fill the data buffer with random bytes */ 73 for (i = 0; i < CRC16_KUNIT_DATA_SIZE; i++) 74 data[i] = _rand16() & 0xFF; 75 76 /* Generate random test data while ensuring the random 77 * start + length values won't overflow the 4096-byte 78 * buffer (0x7FF * 2 = 0xFFE < 0x1000) 79 */ 80 for (size_t i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) { 81 tests[i].crc = _rand16(); 82 tests[i].start = _rand16() & 0x7FF; 83 tests[i].length = _rand16() & 0x7FF; 84 } 85 86 return 0; 87 } 88 89 static void crc16_test_empty(struct kunit *test) 90 { 91 u16 crc; 92 93 /* The result for empty data should be the same as the 94 * initial crc 95 */ 96 crc = crc16(0x00, data, 0); 97 KUNIT_EXPECT_EQ(test, crc, 0); 98 crc = crc16(0xFF, data, 0); 99 KUNIT_EXPECT_EQ(test, crc, 0xFF); 100 } 101 102 static void crc16_test_correctness(struct kunit *test) 103 { 104 size_t i; 105 u16 crc, crc_naive; 106 107 for (i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) { 108 /* Compare results with the naive crc16 implementation */ 109 crc = crc16(tests[i].crc, data + tests[i].start, 110 tests[i].length); 111 crc_naive = _crc16_naive(tests[i].crc, data + tests[i].start, 112 tests[i].length); 113 KUNIT_EXPECT_EQ(test, crc, crc_naive); 114 } 115 } 116 117 118 static void crc16_test_combine(struct kunit *test) 119 { 120 size_t i, j; 121 u16 crc, crc_naive; 122 123 /* Make sure that combining two consecutive crc16 calculations 124 * yields the same result as calculating the crc16 for the whole thing 125 */ 126 for (i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) { 127 crc_naive = crc16(tests[i].crc, data + tests[i].start, tests[i].length); 128 for (j = 0; j < tests[i].length; j++) { 129 crc = crc16(tests[i].crc, data + tests[i].start, j); 130 crc = crc16(crc, data + tests[i].start + j, tests[i].length - j); 131 KUNIT_EXPECT_EQ(test, crc, crc_naive); 132 } 133 } 134 } 135 136 137 static struct kunit_case crc16_test_cases[] = { 138 KUNIT_CASE(crc16_test_empty), 139 KUNIT_CASE(crc16_test_combine), 140 KUNIT_CASE(crc16_test_correctness), 141 {}, 142 }; 143 144 static struct kunit_suite crc16_test_suite = { 145 .name = "crc16", 146 .test_cases = crc16_test_cases, 147 .suite_init = crc16_init_test_data, 148 }; 149 kunit_test_suite(crc16_test_suite); 150 151 MODULE_AUTHOR("Fabricio Gasperin <fgasperin@lkcamp.dev>"); 152 MODULE_AUTHOR("Vinicius Peixoto <vpeixoto@lkcamp.dev>"); 153 MODULE_AUTHOR("Enzo Bertoloti <ebertoloti@lkcamp.dev>"); 154 MODULE_DESCRIPTION("Unit tests for crc16"); 155 MODULE_LICENSE("GPL"); 156