Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,128 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "util/legacy_checksum.h"
#include <stdint.h>
#include <string.h>
// Software implementation of the legacy checksum. This emulates the behaviour
// of the CRC peripheral in the STM32F2/F4 series MCUs and the bugs in the
// legacy CRC driver implementation. While the raw throughput of the hardware
// CRC peripheral is greater, it is not reentrant and so a mutex is required
// to prevent concurrent access to the peripheral by multiple tasks. The
// overhead of locking and unlocking the mutex, while not yet benchmarked, is
// potentially significant enough to cancel out the increased throughput of
// the hardware acceleration. There is also no evidence (for or against) that
// checksums are a performance bottleneck in the first place. Given the added
// complexity of the hardware-accelerated implementation, and the dubious
// performance improvement of using it, hardware acceleration of checksums is a
// case of premature optimization.
//
// That being said, the API for the legacy checksum is fully capable of
// supporting a non-reentrant implementation. Simply acquire the mutex in
// legacy_defective_checksum_init and release it in
// legacy_defective_checksum_finish.
// The implementation is based on a nybble-wide table driven CRC.
// The CRC implementation (but not the checksum based on it) has the
// model parameters:
// Width: 32
// Poly: 04C11DB7
// Init: FFFFFFFF
// RefIn: False
// RefOut: False
// XorOut: 00000000
//
// The CRC lookup table was generated by legacy_checksum_crc_table.py
static const uint32_t s_lookup_table[] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
};
static uint32_t prv_crc_byte(uint32_t crc, uint8_t input) {
crc = (crc << 4) ^ s_lookup_table[((crc >> 28) ^ (input >> 4)) & 0x0f];
crc = (crc << 4) ^ s_lookup_table[((crc >> 28) ^ (input >> 0)) & 0x0f];
return crc;
}
void legacy_defective_checksum_init(LegacyChecksum *checksum) {
*checksum = (LegacyChecksum) {
.reg = 0xffffffff,
};
}
void legacy_defective_checksum_update(
LegacyChecksum * restrict checksum,
const void * restrict data, size_t length) {
const char * restrict data_bytes = data;
uint32_t * restrict reg = &checksum->reg;
uint8_t * restrict accumulator = checksum->accumulator;
uint8_t * restrict accumulated_length = &checksum->accumulated_length;
if (*accumulated_length) {
for (; *accumulated_length < 3 && length; length--) {
accumulator[(*accumulated_length)++] = *data_bytes++;
}
if (*accumulated_length == 3 && length) {
*reg = prv_crc_byte(*reg, *data_bytes++);
length--;
*reg = prv_crc_byte(*reg, accumulator[2]);
*reg = prv_crc_byte(*reg, accumulator[1]);
*reg = prv_crc_byte(*reg, accumulator[0]);
*accumulated_length = 0;
}
}
for (; length >= 4; length -= 4) {
*reg = prv_crc_byte(*reg, data_bytes[3]);
*reg = prv_crc_byte(*reg, data_bytes[2]);
*reg = prv_crc_byte(*reg, data_bytes[1]);
*reg = prv_crc_byte(*reg, data_bytes[0]);
data_bytes += 4;
}
for (; length; length--) {
accumulator[(*accumulated_length)++] = *data_bytes++;
}
}
uint32_t legacy_defective_checksum_finish(LegacyChecksum *checksum) {
if (checksum->accumulated_length) {
// CRC the final bytes forwards (reversed relative to the normal checksum)
// padded on the left(!) with null bytes.
for (int padding = 4 - checksum->accumulated_length; padding; padding--) {
checksum->reg = prv_crc_byte(checksum->reg, 0);
}
for (int i = 0; i < checksum->accumulated_length; ++i) {
checksum->reg = prv_crc_byte(checksum->reg, checksum->accumulator[i]);
}
}
return checksum->reg;
}
uint32_t legacy_defective_checksum_memory(const void * restrict data,
size_t length) {
LegacyChecksum checksum;
legacy_defective_checksum_init(&checksum);
legacy_defective_checksum_update(&checksum, data, length);
return legacy_defective_checksum_finish(&checksum);
}