blob: 26a769560fe57b97371ea8199b077419586dd6ec [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#include "fcs.h"
namespace bt::l2cap {
namespace {
// Subtrahend for each iteration of the linear feedback shift register (LFSR) representing the
// polynomial D**16 + D**15 + D**2 + D**0, written in LSb-left form with the (implicit) D**16
// coefficient omitted.
constexpr uint16_t kPolynomial = 0b1010'0000'0000'0001;
// Compute the remainder of |b| divided by |kPolynomial| in which each bit position is a mod 2
// coefficient of a polynomial, with the least significant bit as coefficient of the greatest power.
// This performs the same function as loading LFSR bits [8:15] (v5.0, Vol 3, Part A, Section 3.3.5,
// Figure 3.4) with |b| bits [7:0], then operating it for eight cycles, and returns the value in the
// register (i.e. as if the Figure 3.4 switch were set to position 2).
//
// Note: polynomial mod 2 arithmetic implies that addition and subtraction do not carry. Hence both
// are equivalent to XOR, and the use of "add" or "subtract" are analogies to regular long division.
//
// Memoization is left up to the compiler, not explicitly declared as a lookup table.
[[gnu::const]] constexpr uint16_t RemainderForOctet(const uint8_t b) {
uint16_t accumulator = b;
for (size_t i = 0; i < 8; i++) {
// Subtract the divisor if accumulator is greater than it. Polynomial mod 2 comparison only
// looks at the most powerful coefficient (which is the LSb).
const bool subtract = accumulator & 0b1;
// Because data shifts in LSb-first (Figure 3.4), this shifts in the MSb-to-LSb direction.
accumulator >>= 1;
if (subtract) {
accumulator ^= kPolynomial;
}
}
return accumulator;
}
} // namespace
// First principles derivation of the Bluetooth FCS specification referenced "A Painless Guide to
// CRC Error Detection Algorithms" by Ross Williams, accessed at
// https://archive.org/stream/PainlessCRC/crc_v3.txt
FrameCheckSequence ComputeFcs(BufferView view, FrameCheckSequence initial_value) {
// Initial state of the accumulation register is all zeroes per Figure 3.5.
uint16_t remainder = initial_value.fcs;
// Each iteration operates the LFSR for eight cycles, shifting in an octet of message.
for (const uint8_t byte : view) {
// Add the remainder to the next eight bits of message.
const uint8_t dividend = byte ^ remainder;
// Because only the least significant eight bits are fed back into the LFSR, the other bits can
// be shifted within the accumulation register without modification.
const uint8_t unused_remainder = remainder >> (8 * (sizeof(uint16_t) - 1));
// Perform eight rounds of division. Add the remainder produced to the bits of the remainder
// that we haven't used (the above shifting is associative wrt this addition).
remainder = RemainderForOctet(dividend) ^ unused_remainder;
}
return FrameCheckSequence{remainder};
}
} // namespace bt::l2cap