mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2026-10-30 21:44:24 +00:00
Compute xn297 whitening instead of using an hardcoded array (#1201)
* Compute xn297 whitening instead of using an hardcoded array * Add function to scramble byte for XN297 interface * Refactor XN297 scrambling logic to use a global LFSR and improve byte generation * Add technical specification and implementation details for XN297 whitening and CRC
This commit is contained in:
+81
-106
@@ -6,22 +6,49 @@ bool xn297_scramble_enabled, xn297_crc, xn297_bitrate, xn297_rf;
|
||||
uint8_t xn297_addr_len, xn297_rx_packet_len;
|
||||
uint8_t xn297_tx_addr[5], xn297_rx_addr[5];
|
||||
|
||||
// xn297 address / pcf / payload scramble table
|
||||
const uint8_t xn297_scramble[] = {
|
||||
0xE3, 0xB1, 0x4B, 0xEA, 0x85, 0xBC, 0xE5, 0x66,
|
||||
0x0D, 0xAE, 0x8C, 0x88, 0x12, 0x69, 0xEE, 0x1F,
|
||||
0xC7, 0x62, 0x97, 0xD5, 0x0B, 0x79, 0xCA, 0xCC,
|
||||
0x1B, 0x5D, 0x19, 0x10, 0x24, 0xD3, 0xDC, 0x3F,
|
||||
0x8E, 0xC5, 0x2F, 0xAA, 0x16, 0xF3, 0x95 };
|
||||
// XN297 whitening: x^7 + x^3 + 1, seed 0x71, MSB first (E3 B1 4B EA 85 ...).
|
||||
// Generated a byte at a time with (x^7 + x^3 + 1)^4 = x^28 + x^12 + 1: s[i] = s[i-28] ^ s[i-12].
|
||||
// xn297_lfsr holds the next 28 bits in bits 31..4, 0 when scrambling is off.
|
||||
uint32_t xn297_lfsr;
|
||||
|
||||
static uint8_t __attribute__((unused)) XN297_scramble_byte()
|
||||
{
|
||||
uint8_t s = xn297_lfsr >> 24;
|
||||
xn297_lfsr = (xn297_lfsr << 8) | (uint8_t)(s ^ (xn297_lfsr >> 8)) << 4;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) XN297_scramble_init()
|
||||
{
|
||||
xn297_lfsr = xn297_scramble_enabled ? XN297_SCRAMBLE_SEED : 0;
|
||||
}
|
||||
|
||||
// XN297 CRC: CRC-16/CCITT (init 0xFFFF, xorout 0xFFFF) over the unscrambled
|
||||
// address, pcf and payload, sent scrambled. The emulation computes it from 0xB5D2
|
||||
// over the bits as sent: the xorout is the difference for a CRC over 'bits' bits,
|
||||
// the CRC of the scramble bits plus the scramble of the CRC bits.
|
||||
// address, pcf and payload, sent scrambled: XORed with the scramble bits that follow,
|
||||
// (xn297_lfsr >> 16) once the last byte has been scrambled.
|
||||
// Returns the scrambled byte, crc updated with the unscrambled one.
|
||||
static uint8_t __attribute__((unused)) XN297_crc_scramble(uint8_t b)
|
||||
{
|
||||
crc16_update(b, 8);
|
||||
return b ^ XN297_scramble_byte();
|
||||
}
|
||||
|
||||
// Returns the unscrambled byte, crc updated with it.
|
||||
static uint8_t __attribute__((unused)) XN297_unscramble_crc(uint8_t b)
|
||||
{
|
||||
b ^= XN297_scramble_byte();
|
||||
crc16_update(b, 8);
|
||||
return b;
|
||||
}
|
||||
|
||||
#ifdef XN297DUMP_NRF24L01_INO
|
||||
// XN297Dump computes the CRC from 0xB5D2 over the bits as sent: the xorout is the
|
||||
// difference for a CRC over 'bits' bits, the CRC of the scramble bits plus the
|
||||
// scramble of the CRC bits.
|
||||
static uint16_t __attribute__((unused)) XN297_crc_xorout(bool scramble, uint16_t bits)
|
||||
{
|
||||
uint16_t xorout = 0xFFFF ^ 0xB5D2;
|
||||
uint8_t lfsr = 0x71, s; // scramble bits (xn297_scramble[]): 1110001, then s[i] = s[i-7] ^ s[i-3]
|
||||
uint8_t lfsr = 0x71, s; // scramble bits: 1110001, then s[i] = s[i-7] ^ s[i-3]
|
||||
for(uint16_t i = 0; i < bits + 16; i++)
|
||||
{
|
||||
s = scramble ? lfsr >> 6 : 0;
|
||||
@@ -38,6 +65,7 @@ static uint16_t __attribute__((unused)) XN297_crc_xorout(bool scramble, uint16_t
|
||||
}
|
||||
return ~xorout;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool __attribute__((unused)) XN297_Configure(bool crc_en, bool scramble_en, bool bitrate, bool force_nrf)
|
||||
{
|
||||
@@ -103,13 +131,10 @@ static void __attribute__((unused)) XN297_SetTXAddr(const uint8_t* addr, uint8_t
|
||||
|
||||
static void __attribute__((unused)) XN297_SetRXAddr(const uint8_t* addr, uint8_t rx_packet_len)
|
||||
{
|
||||
//Scramble address
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
{
|
||||
xn297_rx_addr[i] = addr[i];
|
||||
if(xn297_scramble_enabled)
|
||||
xn297_rx_addr[i] ^= xn297_scramble[xn297_addr_len-i-1];
|
||||
}
|
||||
//Scramble address in transmission order
|
||||
XN297_scramble_init();
|
||||
for (uint8_t i = xn297_addr_len; i--; )
|
||||
xn297_rx_addr[i] = addr[i] ^ XN297_scramble_byte();
|
||||
|
||||
if(xn297_crc)
|
||||
rx_packet_len += 2; // Include CRC
|
||||
@@ -257,32 +282,19 @@ static void __attribute__((unused)) XN297_WritePayload(uint8_t* msg, uint8_t len
|
||||
}
|
||||
|
||||
// address
|
||||
crc = 0xFFFF;
|
||||
XN297_scramble_init();
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
{
|
||||
buf[last] = xn297_tx_addr[xn297_addr_len-i-1];
|
||||
if(xn297_scramble_enabled)
|
||||
buf[last] ^= xn297_scramble[i];
|
||||
last++;
|
||||
}
|
||||
buf[last++] = XN297_crc_scramble(xn297_tx_addr[xn297_addr_len-i-1]);
|
||||
|
||||
// payload
|
||||
for (uint8_t i = 0; i < len; ++i)
|
||||
{
|
||||
// bit-reverse bytes in packet
|
||||
buf[last] = bit_reverse(msg[i]);
|
||||
if(xn297_scramble_enabled)
|
||||
buf[last] ^= xn297_scramble[xn297_addr_len+i];
|
||||
last++;
|
||||
}
|
||||
buf[last++] = XN297_crc_scramble(bit_reverse(msg[i])); // bit-reverse bytes in packet
|
||||
|
||||
// crc
|
||||
if (xn297_crc)
|
||||
{
|
||||
uint8_t offset = (xn297_addr_len < 4 && xn297_rf == XN297_NRF) ? 1 : 0;
|
||||
crc = 0xb5d2;
|
||||
for (uint8_t i = offset; i < last; ++i)
|
||||
crc16_update( buf[i], 8);
|
||||
crc ^= XN297_crc_xorout(xn297_scramble_enabled, 8 * (xn297_addr_len + len));
|
||||
crc = ~crc ^ (xn297_lfsr >> 16);
|
||||
buf[last++] = crc >> 8;
|
||||
buf[last++] = crc & 0xff;
|
||||
}
|
||||
@@ -294,7 +306,6 @@ static void __attribute__((unused)) XN297_WritePayload(uint8_t* msg, uint8_t len
|
||||
static void __attribute__((unused)) XN297_WriteEnhancedPayload(uint8_t* msg, uint8_t len, uint8_t noack)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
uint8_t scramble_index=0;
|
||||
uint8_t last = 0;
|
||||
static uint8_t pid=0;
|
||||
|
||||
@@ -304,54 +315,37 @@ static void __attribute__((unused)) XN297_WriteEnhancedPayload(uint8_t* msg, uin
|
||||
}
|
||||
|
||||
// address
|
||||
crc = xn297_scramble_enabled ? 0xFFFF : 0xB5D2; // unscrambled: no xorout
|
||||
XN297_scramble_init();
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
{
|
||||
buf[last] = xn297_tx_addr[xn297_addr_len-i-1];
|
||||
if(xn297_scramble_enabled)
|
||||
buf[last] ^= xn297_scramble[scramble_index++];
|
||||
last++;
|
||||
}
|
||||
buf[last++] = XN297_crc_scramble(xn297_tx_addr[xn297_addr_len-i-1]);
|
||||
|
||||
// pcf
|
||||
buf[last] = (len << 1) | (pid>>1);
|
||||
if(xn297_scramble_enabled)
|
||||
buf[last] ^= xn297_scramble[scramble_index++];
|
||||
last++;
|
||||
buf[last++] = XN297_crc_scramble((len << 1) | (pid>>1));
|
||||
buf[last] = (pid << 7) | (noack << 6);
|
||||
|
||||
// payload
|
||||
if(len)
|
||||
{
|
||||
buf[last]|= bit_reverse(msg[0]) >> 2; // first 6 bit of payload
|
||||
if(xn297_scramble_enabled)
|
||||
buf[last] ^= xn297_scramble[scramble_index++];
|
||||
buf[last] = XN297_crc_scramble(buf[last] | (bit_reverse(msg[0]) >> 2)); // first 6 bit of payload
|
||||
|
||||
for (uint8_t i = 0; i < len-1; ++i)
|
||||
{
|
||||
last++;
|
||||
buf[last] = (bit_reverse(msg[i]) << 6) | (bit_reverse(msg[i+1]) >> 2);
|
||||
if(xn297_scramble_enabled)
|
||||
buf[last] ^= xn297_scramble[scramble_index++];
|
||||
buf[last] = XN297_crc_scramble((bit_reverse(msg[i]) << 6) | (bit_reverse(msg[i+1]) >> 2));
|
||||
}
|
||||
|
||||
last++;
|
||||
buf[last] = bit_reverse(msg[len-1]) << 6; // last 2 bit of payload
|
||||
}
|
||||
if(xn297_scramble_enabled)
|
||||
buf[last] ^= xn297_scramble[scramble_index++] & 0xc0;
|
||||
crc16_update( buf[last], 2);
|
||||
buf[last] ^= (xn297_lfsr >> 24) & 0xc0;
|
||||
|
||||
// crc
|
||||
if (xn297_crc)
|
||||
{
|
||||
uint8_t offset = (xn297_addr_len < 4 && xn297_rf == XN297_NRF) ? 1 : 0;
|
||||
crc = 0xb5d2;
|
||||
for (uint8_t i = offset; i < last; ++i)
|
||||
crc16_update( buf[i], 8);
|
||||
crc16_update( buf[last] & 0xc0, 2);
|
||||
if (xn297_scramble_enabled)
|
||||
crc ^= XN297_crc_xorout(true, 8 * (xn297_addr_len + len) + 10);
|
||||
//else
|
||||
// crc ^= XN297_crc_xorout(false, 8 * (xn297_addr_len + len) + 10);
|
||||
crc = ~crc ^ (xn297_lfsr >> 14);
|
||||
|
||||
buf[last++] |= (crc >> 8) >> 2;
|
||||
buf[last++] = ((crc >> 8) << 6) | ((crc & 0xff) >> 2);
|
||||
@@ -408,28 +402,18 @@ static bool __attribute__((unused)) XN297_ReadPayload(uint8_t* msg, uint8_t len)
|
||||
// Read payload
|
||||
XN297_ReceivePayload(buf, len);
|
||||
|
||||
// Decode payload
|
||||
// Decode payload, calculate CRC
|
||||
crc = 0xFFFF;
|
||||
XN297_scramble_init();
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
XN297_unscramble_crc(xn297_rx_addr[xn297_addr_len-i-1]);
|
||||
for(uint8_t i=0; i<len; i++)
|
||||
{
|
||||
uint8_t b_in=buf[i];
|
||||
if(xn297_scramble_enabled)
|
||||
b_in ^= xn297_scramble[i+xn297_addr_len];
|
||||
msg[i] = bit_reverse(b_in);
|
||||
}
|
||||
msg[i] = bit_reverse(XN297_unscramble_crc(buf[i]));
|
||||
|
||||
if (!xn297_crc)
|
||||
return true; // No CRC so OK by default...
|
||||
|
||||
// Calculate CRC
|
||||
crc = 0xb5d2;
|
||||
//process address
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
crc16_update( xn297_rx_addr[xn297_addr_len-i-1], 8);
|
||||
//process payload
|
||||
for (uint8_t i = 0; i < len; ++i)
|
||||
crc16_update( buf[i], 8);
|
||||
//xorout
|
||||
crc ^= XN297_crc_xorout(xn297_scramble_enabled, 8 * (xn297_addr_len + len));
|
||||
crc = ~crc ^ (xn297_lfsr >> 16);
|
||||
//test
|
||||
if( (crc >> 8) == buf[len] && (crc & 0xff) == buf[len+1])
|
||||
return true; // CRC OK
|
||||
@@ -443,40 +427,31 @@ static uint8_t __attribute__((unused)) XN297_ReadEnhancedPayload(uint8_t* msg, u
|
||||
|
||||
// Read payload
|
||||
XN297_ReceivePayload(buffer, len+2); // Read pcf + payload + CRC
|
||||
// Decode payload
|
||||
pcf_size = buffer[0];
|
||||
if(xn297_scramble_enabled)
|
||||
pcf_size ^= xn297_scramble[xn297_addr_len];
|
||||
pcf_size = pcf_size >> 1;
|
||||
// Decode payload, calculate CRC
|
||||
#ifdef XN297DUMP_NRF24L01_INO
|
||||
bool xorout = true;
|
||||
#else
|
||||
bool xorout = xn297_scramble_enabled; // unscrambled: no xorout
|
||||
#endif
|
||||
crc = xorout ? 0xFFFF : 0xB5D2;
|
||||
XN297_scramble_init();
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
XN297_unscramble_crc(xn297_rx_addr[xn297_addr_len-i-1]);
|
||||
pcf_size = XN297_unscramble_crc(buffer[0]) >> 1;
|
||||
if(pcf_size>32)
|
||||
return 255; // Error
|
||||
for(uint8_t i=0; i< pcf_size; i++)
|
||||
{
|
||||
msg[i] = bit_reverse((buffer[i+1] << 2) | (buffer[i+2] >> 6));
|
||||
if(xn297_scramble_enabled)
|
||||
msg[i] ^= bit_reverse((xn297_scramble[xn297_addr_len+i+1] << 2) |
|
||||
(xn297_scramble[xn297_addr_len+i+2] >> 6));
|
||||
{ // payload is shifted by 2 bits: unscramble with bits 29..22 of the next scramble bits
|
||||
msg[i] = bit_reverse(((buffer[i+1] << 2) | (buffer[i+2] >> 6)) ^ (xn297_lfsr >> 22));
|
||||
XN297_unscramble_crc(buffer[i+1]);
|
||||
}
|
||||
crc16_update( (buffer[pcf_size+1] ^ (xn297_lfsr >> 24)) & 0xc0, 2);
|
||||
|
||||
if (!xn297_crc)
|
||||
return pcf_size; // No CRC so OK by default...
|
||||
|
||||
// Calculate CRC
|
||||
crc = 0xb5d2;
|
||||
//process address
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
crc16_update( xn297_rx_addr[xn297_addr_len-i-1], 8);
|
||||
//process payload
|
||||
for (uint8_t i = 0; i < pcf_size+1; ++i)
|
||||
crc16_update( buffer[i], 8);
|
||||
crc16_update( buffer[pcf_size+1] & 0xc0, 2);
|
||||
//xorout
|
||||
if (xn297_scramble_enabled)
|
||||
crc ^= XN297_crc_xorout(true, 8 * (xn297_addr_len + pcf_size) + 10);
|
||||
#ifdef XN297DUMP_NRF24L01_INO
|
||||
else
|
||||
crc ^= XN297_crc_xorout(false, 8 * (xn297_addr_len + pcf_size) + 10);
|
||||
#endif
|
||||
if (xorout)
|
||||
crc = ~crc ^ (xn297_lfsr >> 14);
|
||||
uint16_t crcxored=(buffer[pcf_size+1]<<10)|(buffer[pcf_size+2]<<2)|(buffer[pcf_size+3]>>6) ;
|
||||
if( crc == crcxored)
|
||||
return pcf_size; // CRC OK
|
||||
|
||||
Reference in New Issue
Block a user