From 65fc4b0f3ffe18c73f08f51d025e294b9242ed28 Mon Sep 17 00:00:00 2001 From: Teufelchen1 Date: Mon, 16 Jun 2025 16:18:50 +0200 Subject: [PATCH] checksum: Add crc16-fcs / IBM-SDLC --- sys/checksum/crc16_ccitt.c | 15 +++ sys/include/checksum/crc16_ccitt.h | 65 +++++++++ .../tests-checksum-crc16-ccitt-false.c | 2 +- .../tests-checksum-crc16-ccitt-fcs.c | 126 ++++++++++++++++++ .../tests-checksum-crc16-ccitt-kermit.c | 2 +- .../tests-checksum-crc16-ccitt-mcrf4xx.c | 2 +- .../unittests/tests-checksum/tests-checksum.c | 1 + .../unittests/tests-checksum/tests-checksum.h | 7 + 8 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c diff --git a/sys/checksum/crc16_ccitt.c b/sys/checksum/crc16_ccitt.c index d6b3cdb6a7e4..5414a3e38f0d 100644 --- a/sys/checksum/crc16_ccitt.c +++ b/sys/checksum/crc16_ccitt.c @@ -123,6 +123,21 @@ uint16_t crc16_ccitt_mcrf4xx_calc(const unsigned char *buf, size_t len) return crc16_ccitt_kermit_update(0xFFFF, buf, len); } +uint16_t crc16_ccitt_fcs_start(const unsigned char *buf, size_t len) +{ + return crc16_ccitt_kermit_update(0xFFFF, buf, len); +} + +uint16_t crc16_ccitt_fcs_finish(uint16_t crc, const unsigned char *buf, size_t len) +{ + return crc16_ccitt_kermit_update(crc, buf, len) ^ 0xFFFFU; +} + +uint16_t crc16_ccitt_fcs_calc(const unsigned char *buf, size_t len) +{ + return crc16_ccitt_fcs_start(buf, len) ^ 0xFFFFU; +} + uint16_t crc16_ccitt_false_update(uint16_t crc, const unsigned char *buf, size_t len) { while (len--) { diff --git a/sys/include/checksum/crc16_ccitt.h b/sys/include/checksum/crc16_ccitt.h index 4ebe0c920dd2..d9d845b65d69 100644 --- a/sys/include/checksum/crc16_ccitt.h +++ b/sys/include/checksum/crc16_ccitt.h @@ -71,6 +71,71 @@ uint16_t crc16_ccitt_kermit_update(uint16_t crc, const unsigned char *buf, size_ */ uint16_t crc16_ccitt_kermit_calc(const unsigned char *buf, size_t len); +/** + * @brief Start a CRC16-CCITT-FCS / IBM-SDLC calculation + * + * @param[in] buf Start of the memory area to checksum + * @param[in] len Number of bytes to checksum + * + * @return Partial checksum of the specified memory area. To be finalised + * with crc16_ccitt_fcs_finish() + */ +uint16_t crc16_ccitt_fcs_start(const unsigned char *buf, size_t len); + +/** + * @brief Update CRC16-CCITT-FCS / IBM-SDLC + * + * @param[in] crc A start value for the CRC calculation, usually the + * return value of a previous call to + * crc16_ccitt_fcs_start() or crc16_ccitt_fcs_update() + * @param[in] buf Start of the memory area to checksum + * @param[in] len Number of bytes to checksum + * + * @return Partial checksum of the specified memory area based on the + * given start value. To be finalised with crc16_ccitt_fcs_finish() + */ +static inline uint16_t crc16_ccitt_fcs_update(uint16_t crc, const unsigned char *buf, + size_t len) +{ + /* Since CCITT-KERMIT and CCITT-FCS only differ in the starting + * seed, we wrap around crc16_ccitt_kermit_update() for updating */ + return crc16_ccitt_kermit_update(crc, buf, len); +} + +/** + * @brief Finalise CRC16-CCITT-FCS / IBM-SDLC + * + * @param[in] crc A start value for the CRC calculation, usually the + * return value of a previous call to + * crc16_ccitt_fcs_start() or crc16_ccitt_fcs_update() + * @param[in] buf Start of the memory area to checksum + * @param[in] len Number of bytes to checksum + * + * @return Checksum of the specified memory area based on the + * given start value. + */ +uint16_t crc16_ccitt_fcs_finish(uint16_t crc, const unsigned char *buf, size_t len); + +/** + * @brief Calculate CRC16-CCITT-FCS / IBM-SDLC in one pass. + * + * Parameter | Value + * --------: | :---- + * Polynom | `0x1021` + * Init | `0xffff` + * Refin | `true` + * Refout | `true` + * Xorout | `0xffff` + * Check | `0x906e` + * Residue | `0xf0b8` + * + * @param[in] buf Start of the memory area to checksum + * @param[in] len Number of bytes to checksum + * + * @return Checksum of the specified memory area + */ +uint16_t crc16_ccitt_fcs_calc(const unsigned char *buf, size_t len); + /** * @brief Update CRC16-CCITT-MCRF4XX * diff --git a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-false.c b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-false.c index 35fef9df1fb0..598e39d9aef1 100644 --- a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-false.c +++ b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-false.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 Bennet Blischke + * Copyright 2022 Bennet Hattesen * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level diff --git a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c new file mode 100644 index 000000000000..4c8506dd14e1 --- /dev/null +++ b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c @@ -0,0 +1,126 @@ +/* + * Copyright 2025 Bennet Hattesen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +#include +#include + +#include "embUnit/embUnit.h" + +#include "checksum/crc16_ccitt.h" + +#include "tests-checksum.h" + +static int calc_and_compare_crc16_ccitt_fcs_with_update(const unsigned char *buf, + size_t len, size_t split, + uint16_t expected) +{ + uint16_t result = crc16_ccitt_fcs_start(buf, split); + + result = crc16_ccitt_fcs_finish(result, buf + split, len - split); + return result == expected; +} + +static int calc_and_compare_crc16_ccitt_fcs(const unsigned char *buf, size_t len, + uint16_t expected) +{ + uint16_t result = crc16_ccitt_fcs_calc(buf, len); + + return result == expected; +} + +static void test_checksum_crc16_ccitt_fcs_sequence_empty(void) +{ + unsigned char buf[] = ""; + uint16_t expect = 0x0000; + + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs(buf, sizeof(buf) - 1, expect)); + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs_with_update(buf, sizeof(buf) - 1, + (sizeof(buf) - 1) / 2, expect)); +} + +static void test_checksum_crc16_ccitt_fcs_sequence_1a(void) +{ + unsigned char buf[] = "A"; + uint16_t expect = 0xA3F5; + + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs(buf, sizeof(buf) - 1, expect)); + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs_with_update(buf, sizeof(buf) - 1, + (sizeof(buf) - 1) / 2, expect)); +} + +static void test_checksum_crc16_ccitt_fcs_sequence_256a(void) +{ + unsigned char buf[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAA"; + uint16_t expect = 0x0F9F; + + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs(buf, sizeof(buf) - 1, expect)); + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs_with_update(buf, sizeof(buf) - 1, + (sizeof(buf) - 1) / 2, expect)); +} + +static void test_checksum_crc16_ccitt_fcs_sequence_1to9(void) +{ + unsigned char buf[] = "123456789"; + uint16_t expect = 0x906E; + + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs(buf, sizeof(buf) - 1, expect)); + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs_with_update(buf, sizeof(buf) + - 1, (sizeof(buf) - 1) / 2, + expect)); +} + +static void test_checksum_crc16_ccitt_fcs_sequence_4bytes(void) +{ + unsigned char buf[] = { 0x12, 0x34, 0x56, 0x78 }; + uint16_t expect = 0x9B2E; + + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs(buf, sizeof(buf), expect)); + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs_with_update(buf, sizeof(buf), + sizeof(buf) / 2, expect)); +} + +static void test_checksum_crc16_ccitt_fcs_sequence_6bytes(void) +{ + unsigned char buf[] = { 0x00, 0x01, 0x02, 0x03 }; + uint16_t expect = 0xA729; + + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs(buf, sizeof(buf), expect)); + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs_with_update(buf, sizeof(buf), + sizeof(buf) / 2, expect)); + + unsigned char buf2[] = { 0x00, 0x01, 0x02, 0x03, 0x29, 0xA7 }; + uint16_t expect2 = 0x0F47; + + // Residue test + TEST_ASSERT(crc16_ccitt_fcs_start(buf2, sizeof(buf2)) == 0xF0B8); + + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs(buf2, sizeof(buf2), expect2)); + TEST_ASSERT(calc_and_compare_crc16_ccitt_fcs_with_update(buf2, sizeof(buf2), + sizeof(buf2) / 2, expect2)); +} + +Test *tests_checksum_crc16_ccitt_fcs_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_checksum_crc16_ccitt_fcs_sequence_empty), + new_TestFixture(test_checksum_crc16_ccitt_fcs_sequence_1a), + new_TestFixture(test_checksum_crc16_ccitt_fcs_sequence_256a), + new_TestFixture(test_checksum_crc16_ccitt_fcs_sequence_1to9), + new_TestFixture(test_checksum_crc16_ccitt_fcs_sequence_4bytes), + new_TestFixture(test_checksum_crc16_ccitt_fcs_sequence_6bytes), + }; + + EMB_UNIT_TESTCALLER(checksum_crc16_ccitt_fcs_tests, NULL, NULL, fixtures); + + return (Test *)&checksum_crc16_ccitt_fcs_tests; +} diff --git a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-kermit.c b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-kermit.c index 5879a6837442..42173da7bfd6 100644 --- a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-kermit.c +++ b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-kermit.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 Bennet Blischke + * Copyright 2022 Bennet Hattesen * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level diff --git a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-mcrf4xx.c b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-mcrf4xx.c index c5bd2a5f553f..26e07cd1ac12 100644 --- a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-mcrf4xx.c +++ b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-mcrf4xx.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 Bennet Blischke + * Copyright 2022 Bennet Hattesen * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level diff --git a/tests/unittests/tests-checksum/tests-checksum.c b/tests/unittests/tests-checksum/tests-checksum.c index b7967837cee9..6e9562370ba4 100644 --- a/tests/unittests/tests-checksum/tests-checksum.c +++ b/tests/unittests/tests-checksum/tests-checksum.c @@ -13,6 +13,7 @@ void tests_checksum(void) TESTS_RUN(tests_checksum_crc8_tests()); TESTS_RUN(tests_checksum_crc8_lsb_tests()); TESTS_RUN(tests_checksum_crc16_ccitt_kermit_tests()); + TESTS_RUN(tests_checksum_crc16_ccitt_fcs_tests()); TESTS_RUN(tests_checksum_crc16_ccitt_mcrf4xx_tests()); TESTS_RUN(tests_checksum_crc16_ccitt_aug_tests()); TESTS_RUN(tests_checksum_crc16_ccitt_false_tests()); diff --git a/tests/unittests/tests-checksum/tests-checksum.h b/tests/unittests/tests-checksum/tests-checksum.h index 5a0eef038928..848f8974cd36 100644 --- a/tests/unittests/tests-checksum/tests-checksum.h +++ b/tests/unittests/tests-checksum/tests-checksum.h @@ -50,6 +50,13 @@ Test *tests_checksum_crc8_lsb_tests(void); */ Test *tests_checksum_crc16_ccitt_kermit_tests(void); +/** + * @brief Generates tests for crc16-ccitt-fcs from checksum/crc16_ccitt.h + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_checksum_crc16_ccitt_fcs_tests(void); + /** * @brief Generates tests for crc16-ccitt-mcrf4xx from checksum/crc16_ccitt.h *