deskhop/src/utils.c

244 lines
7.3 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This file is part of DeskHop (https://github.com/hrvach/deskhop).
* Copyright (c) 2024 Hrvoje Cavrak
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "main.h"
/**================================================== *
* ============== Checksum Functions ============== *
* ================================================== */
uint8_t calc_checksum(const uint8_t *data, int length) {
uint8_t checksum = 0;
for (int i = 0; i < length; i++) {
checksum ^= data[i];
}
return checksum;
}
bool verify_checksum(const uart_packet_t *packet) {
uint8_t checksum = calc_checksum(packet->data, PACKET_DATA_LENGTH);
return checksum == packet->checksum;
}
uint32_t crc32_iter(uint32_t crc, const uint8_t byte) {
return crc32_lookup_table[(byte ^ crc) & 0xff] ^ (crc >> 8);
}
/* TODO - use DMA sniffer's built-in CRC32 */
uint32_t calc_crc32(const uint8_t *s, size_t n) {
uint32_t crc = 0xffffffff;
for(size_t i=0; i < n; i++) {
crc = crc32_iter(crc, s[i]);
}
return ~crc;
}
uint32_t calculate_firmware_crc32(void) {
return calc_crc32(ADDR_FW_RUNNING, STAGING_IMAGE_SIZE - FLASH_SECTOR_SIZE);
}
/* ================================================== *
* Flash and config functions
* ================================================== */
void wipe_config(void) {
uint32_t ints = save_and_disable_interrupts();
flash_range_erase((uint32_t)ADDR_CONFIG - XIP_BASE, FLASH_SECTOR_SIZE);
restore_interrupts(ints);
}
void write_flash_page(uint32_t target_addr, uint8_t *buffer) {
/* Start of sector == first 256-byte page in a 4096 byte block */
bool is_sector_start = (target_addr & 0xf00) == 0;
uint32_t ints = save_and_disable_interrupts();
if (is_sector_start)
flash_range_erase(target_addr, FLASH_SECTOR_SIZE);
flash_range_program(target_addr, buffer, FLASH_PAGE_SIZE);
restore_interrupts(ints);
}
void load_config(device_t *state) {
const config_t *config = ADDR_CONFIG;
config_t *running_config = &state->config;
/* Load the flash config first, including the checksum */
memcpy(running_config, config, sizeof(config_t));
/* Calculate and update checksum, size without checksum */
uint8_t checksum = calc_crc32((uint8_t *)running_config, sizeof(config_t) - sizeof(uint32_t));
/* We expect a certain byte to start the config header */
bool magic_header_fail = (running_config->magic_header != 0xB00B1E5);
/* We expect the checksum to match */
bool checksum_fail = (running_config->checksum != checksum);
/* We expect the config version to match exactly, to avoid erroneous values */
bool version_fail = (running_config->version != CURRENT_CONFIG_VERSION);
/* On any condition failing, we fall back to default config */
if (magic_header_fail || checksum_fail || version_fail)
memcpy(running_config, &default_config, sizeof(config_t));
}
void save_config(device_t *state) {
uint8_t *raw_config = (uint8_t *)&state->config;
/* Calculate and update checksum, size without checksum */
uint8_t checksum = calc_crc32(raw_config, sizeof(config_t) - sizeof(uint32_t));
state->config.checksum = checksum;
/* Copy the config to buffer and pad the rest with zeros */
memcpy(state->page_buffer, raw_config, sizeof(config_t));
memset(state->page_buffer + sizeof(config_t), 0, FLASH_PAGE_SIZE - sizeof(config_t));
/* Write the new config to flash */
write_flash_page((uint32_t)ADDR_CONFIG - XIP_BASE, state->page_buffer);
}
void reset_config_timer(device_t *state) {
/* Once this is reached, we leave the config mode */
state->config_mode_timer = time_us_64() + CONFIG_MODE_TIMEOUT;
}
void _configure_flash_cs(enum gpio_override gpo, uint pin_index) {
hw_write_masked(&ioqspi_hw->io[pin_index].ctrl,
gpo << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
}
bool is_bootsel_pressed(void) {
const uint CS_PIN_INDEX = 1;
uint32_t flags = save_and_disable_interrupts();
/* Set chip select to high impedance */
_configure_flash_cs(GPIO_OVERRIDE_LOW, CS_PIN_INDEX);
sleep_us(20);
/* Button pressed pulls pin DOWN, so invert */
bool button_pressed = !(sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX));
/* Restore chip select state */
_configure_flash_cs(GPIO_OVERRIDE_NORMAL, CS_PIN_INDEX);
restore_interrupts(flags);
return button_pressed;
}
void request_byte(device_t *state, uint32_t address) {
uart_packet_t packet = {
.data32[0] = address,
.type = REQUEST_BYTE_MSG,
};
state->fw.byte_done = false;
queue_try_add(&global_state.uart_tx_queue, &packet);
}
void reboot(void) {
*((volatile uint32_t*)(PPB_BASE + 0x0ED0C)) = 0x5FA0004;
}
bool is_start_of_packet(device_t *state) {
return (uart_rxbuf[state->dma_ptr] == START1 && uart_rxbuf[NEXT_RING_IDX(state->dma_ptr)] == START2);
}
uint32_t get_ptr_delta(uint32_t current_pointer, device_t *state) {
uint32_t delta;
if (current_pointer >= state->dma_ptr)
delta = current_pointer - state->dma_ptr;
else
delta = DMA_RX_BUFFER_SIZE - state->dma_ptr + current_pointer;
/* Clamp to 12 bits since it can never be bigger */
delta = delta & 0x3FF;
return delta;
}
void fetch_packet(device_t *state) {
uint8_t *dst = (uint8_t *)&state->in_packet;
for (int i = 0; i < RAW_PACKET_LENGTH; i++) {
/* Skip the header preamble */
if (i >= START_LENGTH)
dst[i - START_LENGTH] = uart_rxbuf[state->dma_ptr];
state->dma_ptr = NEXT_RING_IDX(state->dma_ptr);
}
}
/* Validating any input is mandatory. Only packets of these type are allowed
to be sent to the device over configuration endpoint. */
bool validate_packet(uart_packet_t *packet) {
const enum packet_type_e ALLOWED_PACKETS[] = {
FLASH_LED_MSG,
GET_VAL_MSG,
GET_ALL_VALS_MSG,
SET_VAL_MSG,
WIPE_CONFIG_MSG,
SAVE_CONFIG_MSG,
REBOOT_MSG,
PROXY_PACKET_MSG,
};
uint8_t packet_type = packet->type;
/* Proxied packets are encapsulated in the data field, but same rules apply */
if (packet->type == PROXY_PACKET_MSG)
packet_type = packet->data[0];
for (int i = 0; i < ARRAY_SIZE(ALLOWED_PACKETS); i++) {
if (ALLOWED_PACKETS[i] == packet_type)
return true;
}
return false;
}
/* ================================================== *
* Debug functions
* ================================================== */
#ifdef DH_DEBUG
int dh_debug_printf(const char *format, ...) {
va_list args;
va_start(args, format);
char buffer[512];
int string_len = vsnprintf(buffer, 512, format, args);
tud_cdc_n_write(0, buffer, string_len);
tud_cdc_write_flush();
va_end(args);
return string_len;
}
#else
int dh_debug_printf(const char *format, ...) {
return 0;
}
#endif