Skip to content

Commit f77c1d5

Browse files
nzmichaelhAnas Nashif
authored andcommitted
crc: add a faster, chainable version of CRC16/CCITT.
The existing version of crc16_ccitt() is actually CRC-16/AUG-CCITT and gives different results to Linux, Contiki, and the CRC unit in the SAM0 SOC. This version matches Linux. Note that this is an incompatible API change. Signed-off-by: Michael Hope <[email protected]>
1 parent 88b1ad1 commit f77c1d5

File tree

3 files changed

+79
-10
lines changed

3 files changed

+79
-10
lines changed

include/crc16.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,30 @@ u16_t crc16(const u8_t *src, size_t len, u16_t polynomial,
4343
u16_t initial_value, bool pad);
4444

4545
/**
46-
* @brief Compute CCITT variant of CRC 16
46+
* @brief Compute the CRC-16/CCITT checksum of a buffer.
4747
*
48-
* CCITT variant of CRC 16 is using 0x1021 as its polynomial with the initial
49-
* value set to 0xffff.
48+
* See ITU-T Recommendation V.41 (November 1988). Uses 0x1021 as the
49+
* polynomial, reflects the input, and reflects the output.
50+
*
51+
* To calculate the CRC across non-contigious blocks use the return
52+
* value from block N-1 as the seed for block N.
53+
*
54+
* For CRC-16/CCITT, use 0 as the initial seed. Other checksums in
55+
* the same family can be calculated by changing the seed and/or
56+
* XORing the final value. Examples include:
57+
*
58+
* - CCIITT-FALSE: seed=0xffff
59+
* - X-25 (used in PPP): seed=0xffff, xor=0xffff, residual=0xf0b8
5060
*
61+
* @note API changed in Zephyr 1.11.
62+
*
63+
* @param seed Value to seed the CRC with
5164
* @param src Input bytes for the computation
5265
* @param len Length of the input in bytes
5366
*
5467
* @return The computed CRC16 value
5568
*/
56-
static inline u16_t crc16_ccitt(const u8_t *src, size_t len)
57-
{
58-
return crc16(src, len, 0x1021, 0xffff, true);
59-
}
69+
u16_t crc16_ccitt(u16_t seed, const u8_t *src, size_t len);
6070

6171
/**
6272
* @brief Compute ANSI variant of CRC 16

lib/crc/crc16_sw.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,16 @@ u16_t crc16(const u8_t *src, size_t len, u16_t polynomial,
3434

3535
return crc;
3636
}
37+
38+
u16_t crc16_ccitt(u16_t seed, const u8_t *src, size_t len)
39+
{
40+
for (; len > 0; len--) {
41+
u8_t e, f;
42+
43+
e = seed ^ *src++;
44+
f = e ^ (e << 4);
45+
seed = (seed >> 8) ^ (f << 8) ^ (f << 3) ^ (f >> 4);
46+
}
47+
48+
return seed;
49+
}

tests/unit/lib/crc/main.c

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ void test_crc16(void)
1515
u8_t test1[] = { 'A' };
1616
u8_t test2[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
1717

18-
zassert(crc16_ccitt(test0, sizeof(test0)) == 0x1d0f, "pass", "fail");
19-
zassert(crc16_ccitt(test1, sizeof(test1)) == 0x9479, "pass", "fail");
20-
zassert(crc16_ccitt(test2, sizeof(test2)) == 0xe5cc, "pass", "fail");
18+
zassert_equal(crc16(test0, sizeof(test0), 0x1021, 0xffff, true),
19+
0x1d0f, NULL);
20+
zassert_equal(crc16(test1, sizeof(test1), 0x1021, 0xffff, true),
21+
0x9479, NULL);
22+
zassert_equal(crc16(test2, sizeof(test2), 0x1021, 0xffff, true),
23+
0xe5cc, NULL);
2124
}
2225

2326
void test_crc16_ansi(void)
@@ -31,6 +34,47 @@ void test_crc16_ansi(void)
3134
zassert(crc16_ansi(test2, sizeof(test2)) == 0x9ecf, "pass", "fail");
3235
}
3336

37+
void test_crc16_ccitt(void)
38+
{
39+
u8_t test0[] = { };
40+
u8_t test1[] = { 'A' };
41+
u8_t test2[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
42+
u8_t test3[] = { 'Z', 'e', 'p', 'h', 'y', 'r', 0, 0 };
43+
u16_t crc;
44+
45+
zassert_equal(crc16_ccitt(0, test0, sizeof(test0)), 0x0, NULL);
46+
zassert_equal(crc16_ccitt(0, test1, sizeof(test1)), 0x538d, NULL);
47+
zassert_equal(crc16_ccitt(0, test2, sizeof(test2)), 0x2189, NULL);
48+
49+
/* Appending the CRC to a buffer and computing the CRC over
50+
* the extended buffer leaves a residual of zero.
51+
*/
52+
crc = crc16_ccitt(0, test3, sizeof(test3) - sizeof(u16_t));
53+
test3[sizeof(test3)-2] = (u8_t)(crc >> 0);
54+
test3[sizeof(test3)-1] = (u8_t)(crc >> 8);
55+
56+
zassert_equal(crc16_ccitt(0, test3, sizeof(test3)), 0, NULL);
57+
}
58+
59+
void test_crc16_ccitt_for_ppp(void)
60+
{
61+
/* Example capture including FCS from
62+
* https://www.horo.ch/techno/ppp-fcs/examples_en.html
63+
*/
64+
u8_t test0[] = {
65+
0xff, 0x03, 0xc0, 0x21, 0x01, 0x01, 0x00, 0x17,
66+
0x02, 0x06, 0x00, 0x0a, 0x00, 0x00, 0x05, 0x06,
67+
0x00, 0x2a, 0x2b, 0x78, 0x07, 0x02, 0x08, 0x02,
68+
0x0d, 0x03, 0x06, 0xa5, 0xf8
69+
};
70+
u8_t test2[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
71+
72+
zassert_equal(crc16_ccitt(0xffff, test0, sizeof(test0)),
73+
0xf0b8, NULL);
74+
zassert_equal(crc16_ccitt(0xffff, test2, sizeof(test2)) ^ 0xFFFF,
75+
0x906e, NULL);
76+
}
77+
3478
void test_crc8_ccitt(void)
3579
{
3680
u8_t test0[] = { 0 };
@@ -50,6 +94,8 @@ void test_main(void)
5094
ztest_test_suite(test_crc,
5195
ztest_unit_test(test_crc16),
5296
ztest_unit_test(test_crc16_ansi),
97+
ztest_unit_test(test_crc16_ccitt),
98+
ztest_unit_test(test_crc16_ccitt_for_ppp),
5399
ztest_unit_test(test_crc8_ccitt));
54100
ztest_run_test_suite(test_crc);
55101
}

0 commit comments

Comments
 (0)