2010-02-21 08:12:52 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
|
|
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
|
|
|
// the license.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// CRC16
|
|
|
|
//-----------------------------------------------------------------------------
|
2010-02-21 05:56:33 +08:00
|
|
|
#include "crc16.h"
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-08-08 22:57:33 +08:00
|
|
|
#include <string.h>
|
|
|
|
#include "commonutil.h"
|
|
|
|
|
2018-01-29 20:42:02 +08:00
|
|
|
static uint16_t crc_table[256];
|
|
|
|
static bool crc_table_init = false;
|
2019-04-07 03:46:00 +08:00
|
|
|
static CrcType_t current_crc_type = CRC_NONE;
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-04-07 03:46:00 +08:00
|
|
|
void init_table(CrcType_t crctype) {
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
// same crc algo, and initialised already
|
2019-04-07 03:46:00 +08:00
|
|
|
if (crctype == current_crc_type && crc_table_init)
|
2019-03-10 02:19:50 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// not the same crc algo. reset table.
|
2019-04-07 03:46:00 +08:00
|
|
|
if (crctype != current_crc_type)
|
2019-03-10 02:19:50 +08:00
|
|
|
reset_table();
|
|
|
|
|
2019-04-07 03:46:00 +08:00
|
|
|
current_crc_type = crctype;
|
2019-03-10 02:19:50 +08:00
|
|
|
|
2019-04-07 03:46:00 +08:00
|
|
|
switch (crctype) {
|
2019-03-10 02:19:50 +08:00
|
|
|
case CRC_14443_A:
|
|
|
|
case CRC_14443_B:
|
|
|
|
case CRC_15693:
|
2019-03-10 07:00:59 +08:00
|
|
|
case CRC_ICLASS:
|
2020-08-21 20:11:24 +08:00
|
|
|
case CRC_CRYPTORF:
|
2019-03-10 07:00:59 +08:00
|
|
|
generate_table(CRC16_POLY_CCITT, true);
|
|
|
|
break;
|
|
|
|
case CRC_FELICA:
|
2019-08-05 02:05:30 +08:00
|
|
|
case CRC_XMODEM:
|
2019-03-10 07:00:59 +08:00
|
|
|
generate_table(CRC16_POLY_CCITT, false);
|
|
|
|
break;
|
|
|
|
case CRC_LEGIC:
|
|
|
|
generate_table(CRC16_POLY_LEGIC, true);
|
|
|
|
break;
|
|
|
|
case CRC_CCITT:
|
|
|
|
generate_table(CRC16_POLY_CCITT, false);
|
|
|
|
break;
|
|
|
|
case CRC_KERMIT:
|
|
|
|
generate_table(CRC16_POLY_CCITT, true);
|
|
|
|
break;
|
2019-10-30 22:44:57 +08:00
|
|
|
case CRC_11784:
|
|
|
|
generate_table(CRC16_POLY_CCITT, false);
|
|
|
|
break;
|
2019-04-14 04:46:08 +08:00
|
|
|
case CRC_NONE:
|
2019-03-10 02:19:50 +08:00
|
|
|
crc_table_init = false;
|
2019-04-07 03:46:00 +08:00
|
|
|
current_crc_type = CRC_NONE;
|
2019-03-10 02:19:50 +08:00
|
|
|
break;
|
|
|
|
}
|
2018-01-29 20:42:02 +08:00
|
|
|
}
|
|
|
|
|
2019-03-10 18:20:22 +08:00
|
|
|
void generate_table(uint16_t polynomial, bool refin) {
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-06-08 00:41:39 +08:00
|
|
|
for (uint16_t i = 0; i < 256; i++) {
|
|
|
|
uint16_t c, crc = 0;
|
2019-03-10 02:19:50 +08:00
|
|
|
if (refin)
|
|
|
|
c = reflect8(i) << 8;
|
|
|
|
else
|
|
|
|
c = i << 8;
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-06-08 00:41:39 +08:00
|
|
|
for (uint16_t j = 0; j < 8; j++) {
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-03-10 07:00:59 +08:00
|
|
|
if ((crc ^ c) & 0x8000)
|
|
|
|
crc = (crc << 1) ^ polynomial;
|
2018-01-29 20:42:02 +08:00
|
|
|
else
|
2019-03-10 02:19:50 +08:00
|
|
|
crc = crc << 1;
|
2018-01-29 20:42:02 +08:00
|
|
|
|
|
|
|
c = c << 1;
|
|
|
|
}
|
2019-03-10 02:19:50 +08:00
|
|
|
if (refin)
|
|
|
|
crc = reflect16(crc);
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
crc_table[i] = crc;
|
|
|
|
}
|
2018-01-29 20:42:02 +08:00
|
|
|
crc_table_init = true;
|
|
|
|
}
|
|
|
|
|
2019-03-10 18:20:22 +08:00
|
|
|
void reset_table(void) {
|
2019-03-10 02:19:50 +08:00
|
|
|
memset(crc_table, 0, sizeof(crc_table));
|
|
|
|
crc_table_init = false;
|
2019-04-07 03:46:00 +08:00
|
|
|
current_crc_type = CRC_NONE;
|
2018-01-29 20:42:02 +08:00
|
|
|
}
|
|
|
|
|
2018-01-29 22:55:56 +08:00
|
|
|
// table lookup LUT solution
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_fast(uint8_t const *d, size_t n, uint16_t initval, bool refin, bool refout) {
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
// fast lookup table algorithm without augmented zero bytes, e.g. used in pkzip.
|
|
|
|
// only usable with polynom orders of 8, 16, 24 or 32.
|
|
|
|
if (n == 0)
|
2018-01-29 20:42:02 +08:00
|
|
|
return (~initval);
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
uint16_t crc = initval;
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
if (refin)
|
|
|
|
crc = reflect16(crc);
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
if (!refin)
|
2019-03-10 07:00:59 +08:00
|
|
|
while (n--) crc = (crc << 8) ^ crc_table[((crc >> 8) ^ *d++) & 0xFF ];
|
2019-03-10 02:19:50 +08:00
|
|
|
else
|
2019-03-10 07:00:59 +08:00
|
|
|
while (n--) crc = (crc >> 8) ^ crc_table[(crc & 0xFF) ^ *d++];
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-03-10 07:00:59 +08:00
|
|
|
if (refout ^ refin)
|
2019-03-10 02:19:50 +08:00
|
|
|
crc = reflect16(crc);
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
return crc;
|
2018-01-29 20:42:02 +08:00
|
|
|
}
|
|
|
|
|
2018-02-01 22:19:47 +08:00
|
|
|
// bit looped solution TODO REMOVED
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t update_crc16_ex(uint16_t crc, uint8_t c, uint16_t polynomial) {
|
2019-06-08 00:41:39 +08:00
|
|
|
uint16_t tmp = 0;
|
|
|
|
uint16_t v = (crc ^ c) & 0xff;
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-06-08 00:41:39 +08:00
|
|
|
for (uint16_t i = 0; i < 8; i++) {
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 07:00:59 +08:00
|
|
|
if ((tmp ^ v) & 1)
|
|
|
|
tmp = (tmp >> 1) ^ polynomial;
|
2019-03-10 02:19:50 +08:00
|
|
|
else
|
|
|
|
tmp >>= 1;
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
v >>= 1;
|
|
|
|
}
|
|
|
|
return ((crc >> 8) ^ tmp) & 0xffff;
|
2018-01-28 17:59:57 +08:00
|
|
|
}
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t update_crc16(uint16_t crc, uint8_t c) {
|
2019-03-10 07:00:59 +08:00
|
|
|
return update_crc16_ex(crc, c, CRC16_POLY_CCITT);
|
2010-02-21 05:56:33 +08:00
|
|
|
}
|
2014-11-13 06:18:46 +08:00
|
|
|
|
2018-01-29 20:42:02 +08:00
|
|
|
// two ways. msb or lsb loop.
|
2019-04-09 16:12:15 +08:00
|
|
|
uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) {
|
2019-03-10 02:19:50 +08:00
|
|
|
if (length == 0)
|
2014-11-13 06:18:46 +08:00
|
|
|
return (~remainder);
|
2018-01-28 17:59:57 +08:00
|
|
|
|
2016-07-29 03:41:44 +08:00
|
|
|
for (uint32_t i = 0; i < length; ++i) {
|
2019-06-08 00:41:39 +08:00
|
|
|
uint8_t c = d[i];
|
2019-03-10 02:19:50 +08:00
|
|
|
if (refin) c = reflect8(c);
|
2018-01-28 17:59:57 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
// xor in at msb
|
2018-01-28 17:59:57 +08:00
|
|
|
remainder ^= (c << 8);
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
// 8 iteration loop
|
2018-01-28 17:59:57 +08:00
|
|
|
for (uint8_t j = 8; j; --j) {
|
2014-11-13 06:18:46 +08:00
|
|
|
if (remainder & 0x8000) {
|
|
|
|
remainder = (remainder << 1) ^ polynomial;
|
|
|
|
} else {
|
2018-01-28 17:59:57 +08:00
|
|
|
remainder <<= 1;
|
2014-11-13 06:18:46 +08:00
|
|
|
}
|
2019-03-09 15:59:13 +08:00
|
|
|
}
|
2014-11-13 06:18:46 +08:00
|
|
|
}
|
2019-03-10 02:19:50 +08:00
|
|
|
if (refout)
|
|
|
|
remainder = reflect16(remainder);
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2014-11-13 06:18:46 +08:00
|
|
|
return remainder;
|
|
|
|
}
|
2018-01-29 20:42:02 +08:00
|
|
|
|
2019-03-10 18:20:22 +08:00
|
|
|
void compute_crc(CrcType_t ct, const uint8_t *d, size_t n, uint8_t *first, uint8_t *second) {
|
2018-02-01 16:33:34 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
// can't calc a crc on less than 1 byte
|
2019-03-10 07:00:59 +08:00
|
|
|
if (n == 0) return;
|
2019-03-10 02:19:50 +08:00
|
|
|
|
|
|
|
init_table(ct);
|
|
|
|
|
|
|
|
uint16_t crc = 0;
|
|
|
|
switch (ct) {
|
2019-03-10 07:00:59 +08:00
|
|
|
case CRC_14443_A:
|
|
|
|
crc = crc16_a(d, n);
|
|
|
|
break;
|
2020-08-21 20:11:24 +08:00
|
|
|
case CRC_CRYPTORF:
|
2019-03-10 02:19:50 +08:00
|
|
|
case CRC_14443_B:
|
2019-03-10 07:00:59 +08:00
|
|
|
case CRC_15693:
|
|
|
|
crc = crc16_x25(d, n);
|
|
|
|
break;
|
|
|
|
case CRC_ICLASS:
|
|
|
|
crc = crc16_iclass(d, n);
|
|
|
|
break;
|
|
|
|
case CRC_FELICA:
|
2019-08-05 02:05:30 +08:00
|
|
|
case CRC_XMODEM:
|
2019-03-10 07:00:59 +08:00
|
|
|
crc = crc16_xmodem(d, n);
|
|
|
|
break;
|
|
|
|
case CRC_CCITT:
|
|
|
|
crc = crc16_ccitt(d, n);
|
|
|
|
break;
|
|
|
|
case CRC_KERMIT:
|
|
|
|
crc = crc16_kermit(d, n);
|
|
|
|
break;
|
2019-10-30 22:44:57 +08:00
|
|
|
case CRC_11784:
|
2020-10-12 04:38:52 +08:00
|
|
|
crc = crc16_fdxb(d, n);
|
2019-10-30 22:44:57 +08:00
|
|
|
break;
|
2019-04-14 04:46:08 +08:00
|
|
|
case CRC_LEGIC:
|
|
|
|
// TODO
|
|
|
|
return;
|
|
|
|
case CRC_NONE:
|
|
|
|
return;
|
2019-03-10 02:19:50 +08:00
|
|
|
}
|
|
|
|
*first = (crc & 0xFF);
|
2018-01-30 10:31:11 +08:00
|
|
|
*second = ((crc >> 8) & 0xFF);
|
|
|
|
}
|
2019-04-09 16:12:15 +08:00
|
|
|
uint16_t Crc16ex(CrcType_t ct, const uint8_t *d, size_t n) {
|
2018-02-01 22:19:47 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
// can't calc a crc on less than 3 byte. (1byte + 2 crc bytes)
|
2019-03-10 07:00:59 +08:00
|
|
|
if (n < 3) return 0;
|
2019-03-10 02:19:50 +08:00
|
|
|
|
|
|
|
init_table(ct);
|
|
|
|
switch (ct) {
|
2019-03-10 07:00:59 +08:00
|
|
|
case CRC_14443_A:
|
|
|
|
return crc16_a(d, n);
|
2020-08-21 20:11:24 +08:00
|
|
|
case CRC_CRYPTORF:
|
2019-03-10 02:19:50 +08:00
|
|
|
case CRC_14443_B:
|
2019-03-10 07:00:59 +08:00
|
|
|
case CRC_15693:
|
|
|
|
return crc16_x25(d, n);
|
|
|
|
case CRC_ICLASS:
|
|
|
|
return crc16_iclass(d, n);
|
|
|
|
case CRC_FELICA:
|
2019-08-05 02:05:30 +08:00
|
|
|
case CRC_XMODEM:
|
2019-03-10 07:00:59 +08:00
|
|
|
return crc16_xmodem(d, n);
|
|
|
|
case CRC_CCITT:
|
|
|
|
return crc16_ccitt(d, n);
|
|
|
|
case CRC_KERMIT:
|
|
|
|
return crc16_kermit(d, n);
|
2019-10-30 22:44:57 +08:00
|
|
|
case CRC_11784:
|
2020-10-12 04:38:52 +08:00
|
|
|
return crc16_fdxb(d, n);
|
2019-04-14 04:46:08 +08:00
|
|
|
case CRC_LEGIC:
|
|
|
|
// TODO
|
|
|
|
return 0;
|
2019-10-27 01:41:30 +08:00
|
|
|
case CRC_NONE:
|
2019-03-10 07:00:59 +08:00
|
|
|
default:
|
|
|
|
break;
|
2019-03-10 02:19:50 +08:00
|
|
|
}
|
|
|
|
return 0;
|
2018-02-01 22:19:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// check CRC
|
|
|
|
// ct crc type
|
2019-03-10 02:19:50 +08:00
|
|
|
// d buffer with data
|
|
|
|
// n length (including crc)
|
2019-03-09 15:59:13 +08:00
|
|
|
//
|
2018-02-01 22:19:47 +08:00
|
|
|
// This function uses the message + crc bytes in order to compare the "residue" afterwards.
|
2019-10-27 01:37:36 +08:00
|
|
|
// crc16 algos like CRC-A become 0x0000
|
2018-02-01 22:19:47 +08:00
|
|
|
// while CRC-15693 become 0x0F47
|
|
|
|
// If calculated with crc bytes, the residue should be 0xF0B8
|
2019-03-10 18:20:22 +08:00
|
|
|
bool check_crc(CrcType_t ct, const uint8_t *d, size_t n) {
|
2018-02-01 22:19:47 +08:00
|
|
|
|
2019-03-10 02:19:50 +08:00
|
|
|
// can't calc a crc on less than 3 byte. (1byte + 2 crc bytes)
|
2019-03-10 07:00:59 +08:00
|
|
|
if (n < 3) return false;
|
2019-03-10 02:19:50 +08:00
|
|
|
|
|
|
|
init_table(ct);
|
|
|
|
|
|
|
|
switch (ct) {
|
2019-03-10 07:00:59 +08:00
|
|
|
case CRC_14443_A:
|
|
|
|
return (crc16_a(d, n) == 0);
|
2020-08-21 20:11:24 +08:00
|
|
|
case CRC_CRYPTORF:
|
2019-03-10 07:00:59 +08:00
|
|
|
case CRC_14443_B:
|
|
|
|
return (crc16_x25(d, n) == X25_CRC_CHECK);
|
|
|
|
case CRC_15693:
|
|
|
|
return (crc16_x25(d, n) == X25_CRC_CHECK);
|
|
|
|
case CRC_ICLASS:
|
|
|
|
return (crc16_iclass(d, n) == 0);
|
|
|
|
case CRC_FELICA:
|
2019-08-05 02:05:30 +08:00
|
|
|
case CRC_XMODEM:
|
2019-03-10 07:00:59 +08:00
|
|
|
return (crc16_xmodem(d, n) == 0);
|
|
|
|
case CRC_CCITT:
|
|
|
|
return (crc16_ccitt(d, n) == 0);
|
2019-10-27 01:37:36 +08:00
|
|
|
case CRC_KERMIT:
|
|
|
|
return (crc16_kermit(d, n) == 0);
|
2019-10-30 22:44:57 +08:00
|
|
|
case CRC_11784:
|
2020-10-12 04:38:52 +08:00
|
|
|
return (crc16_fdxb(d, n) == 0);
|
2019-04-14 04:46:08 +08:00
|
|
|
case CRC_LEGIC:
|
|
|
|
// TODO
|
|
|
|
return false;
|
2019-10-27 01:41:30 +08:00
|
|
|
case CRC_NONE:
|
2019-03-10 07:00:59 +08:00
|
|
|
default:
|
|
|
|
break;
|
2019-03-10 02:19:50 +08:00
|
|
|
}
|
|
|
|
return false;
|
2018-02-01 22:19:47 +08:00
|
|
|
}
|
2018-01-30 10:31:11 +08:00
|
|
|
|
2018-02-01 22:19:47 +08:00
|
|
|
// poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 residue=0x0000 name="CRC-16/CCITT-FALSE"
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_ccitt(uint8_t const *d, size_t n) {
|
2019-03-10 02:19:50 +08:00
|
|
|
return crc16_fast(d, n, 0xffff, false, false);
|
2014-11-13 06:18:46 +08:00
|
|
|
}
|
2018-02-01 16:33:34 +08:00
|
|
|
|
2019-10-30 22:44:57 +08:00
|
|
|
// FDX-B ISO11784/85) uses KERMIT/CCITT
|
|
|
|
// poly 0x xx init=0x000 refin=false refout=true xorout=0x0000 ...
|
2020-10-12 04:38:52 +08:00
|
|
|
uint16_t crc16_fdxb(uint8_t const *d, size_t n) {
|
2019-10-30 22:44:57 +08:00
|
|
|
return crc16_fast(d, n, 0x0000, false, true);
|
|
|
|
}
|
|
|
|
|
2018-02-01 22:19:47 +08:00
|
|
|
// poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 name="KERMIT"
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_kermit(uint8_t const *d, size_t n) {
|
2019-03-10 02:19:50 +08:00
|
|
|
return crc16_fast(d, n, 0x0000, true, true);
|
2018-01-28 17:59:57 +08:00
|
|
|
}
|
2018-02-01 16:33:34 +08:00
|
|
|
|
2018-01-29 20:42:02 +08:00
|
|
|
// FeliCa uses XMODEM
|
2018-02-01 22:19:47 +08:00
|
|
|
// poly=0x1021 init=0x0000 refin=false refout=false xorout=0x0000 name="XMODEM"
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_xmodem(uint8_t const *d, size_t n) {
|
2019-03-10 02:19:50 +08:00
|
|
|
return crc16_fast(d, n, 0x0000, false, false);
|
2018-01-28 17:59:57 +08:00
|
|
|
}
|
2018-01-29 20:42:02 +08:00
|
|
|
|
|
|
|
// Following standards uses X-25
|
|
|
|
// ISO 15693,
|
|
|
|
// ISO 14443 CRC-B
|
|
|
|
// ISO/IEC 13239 (formerly ISO/IEC 3309)
|
2018-02-01 22:19:47 +08:00
|
|
|
// poly=0x1021 init=0xffff refin=true refout=true xorout=0xffff name="X-25"
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_x25(uint8_t const *d, size_t n) {
|
2019-03-10 02:19:50 +08:00
|
|
|
uint16_t crc = crc16_fast(d, n, 0xffff, true, true);
|
|
|
|
crc = ~crc;
|
|
|
|
return crc;
|
2015-06-07 17:35:49 +08:00
|
|
|
}
|
2018-02-01 22:19:47 +08:00
|
|
|
// CRC-A (14443-3)
|
|
|
|
// poly=0x1021 init=0xc6c6 refin=true refout=true xorout=0x0000 name="CRC-A"
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_a(uint8_t const *d, size_t n) {
|
2019-03-10 02:19:50 +08:00
|
|
|
return crc16_fast(d, n, 0xC6C6, true, true);
|
2018-01-28 17:59:57 +08:00
|
|
|
}
|
|
|
|
|
2018-01-29 20:42:02 +08:00
|
|
|
// iClass crc
|
|
|
|
// initvalue 0x4807 reflected 0xE012
|
|
|
|
// poly 0x1021 reflected 0x8408
|
|
|
|
// poly=0x1021 init=0x4807 refin=true refout=true xorout=0x0BC3 check=0xF0B8 name="CRC-16/ICLASS"
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_iclass(uint8_t const *d, size_t n) {
|
2019-03-10 02:19:50 +08:00
|
|
|
return crc16_fast(d, n, 0x4807, true, true);
|
2018-01-29 20:42:02 +08:00
|
|
|
}
|
|
|
|
|
2019-03-09 15:59:13 +08:00
|
|
|
// This CRC-16 is used in Legic Advant systems.
|
2018-01-29 20:42:02 +08:00
|
|
|
// poly=0xB400, init=depends refin=true refout=true xorout=0x0000 check= name="CRC-16/LEGIC"
|
2019-03-10 18:20:22 +08:00
|
|
|
uint16_t crc16_legic(uint8_t const *d, size_t n, uint8_t uidcrc) {
|
2019-03-10 02:19:50 +08:00
|
|
|
uint16_t initial = uidcrc << 8 | uidcrc;
|
|
|
|
return crc16_fast(d, n, initial, true, true);
|
2018-01-29 20:42:02 +08:00
|
|
|
}
|
2020-08-21 20:11:24 +08:00
|
|
|
|