/* Copyright (c) 2016-2020 Peter Antypas This file is part of the MAIANAâ„¢ transponder firmware. The firmware 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, either version 3 of the License, or (at your option) any later version. 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 */ #include #include "Configuration.hpp" #include "Utils.hpp" #include "config.h" #include "EventQueue.hpp" #include #include // These are not defined in ANY CMSIS or HAL header, WTF??? #define OTP_ADDRESS 0x1FFF7000 #define OTP_SIZE 0x00000400 #define CONFIG_FLAGS_MAGIC 0x2092ED2C #define XO_TRIM_MAGIC 0x8112AAAA //static StationDataPage __page; Configuration &Configuration::instance() { static Configuration __instance; return __instance; } Configuration::Configuration() { } void Configuration::init() { bool cliBootMode = *(uint32_t*)BOOTMODE_ADDRESS == CLI_FLAG_MAGIC; if ( !cliBootMode ) { reportSystemData(); reportStationData(); } bsp_read_config_flags(&mFlags); } void Configuration::enableTX() { // For now, the only flag in use is a TX switch bit which is set to 0 mFlags = {CONFIG_FLAGS_MAGIC, 0, {0}}; bsp_write_config_flags(mFlags); } void Configuration::disableTX() { // For now, the only flag in use is a TX switch bit which is set to 1 mFlags = {CONFIG_FLAGS_MAGIC, 0, {0}}; mFlags.flags[0] = 0x01; bsp_write_config_flags(mFlags); } bool Configuration::isTXEnabled() { if ( mFlags.magic != CONFIG_FLAGS_MAGIC ) return true; // Bit 0 in word 0 inhibits transmission return (mFlags.flags[0] & 0x01) == 0; } const char *Configuration::hwRev() { const OTPData *otp = readOTP(); if ( otp ) return otp->hwrev; else return BSP_HW_REV; } const char *Configuration::serNum() { const OTPData *otp = readOTP(); if ( otp ) return otp->serialnum; else return ""; } const char *Configuration::mcuType() { #ifdef STM32L422xx return "STM32L422"; #elif defined STM32L432xx return "STM32L432"; #elif defined STM32L412xx return "STM32L412"; #else return ""; #endif } const char *Configuration::breakoutType() { #if LEGACY_BREAKOUTS == 0 return "1"; #else return "0"; #endif } bool Configuration::isBootloaderPresent() { #ifndef VECT_TAB_OFFSET return false; #else return true; #endif } void Configuration::reportSystemData() { Event *e = EventPool::instance().newEvent(PROPR_NMEA_SENTENCE); if ( !e ) return; sprintf(e->nmeaBuffer.sentence, "$PAISYS,%s,%s,%s,%s,%s,%d*", hwRev(), FW_REV, serNum(), mcuType(), breakoutType(), isBootloaderPresent()); Utils::completeNMEA(e->nmeaBuffer.sentence); EventQueue::instance().push(e); } void Configuration::reportStationData() { StationData d; if ( !readStationData(d) ) memset(&d, 0, sizeof d); Event *e = EventPool::instance().newEvent(PROPR_NMEA_SENTENCE); if ( !e ) return; sprintf(e->nmeaBuffer.sentence, "$PAISTN,%lu,%s,%s,%d,%d,%d,%d,%d*", d.mmsi, d.name, d.callsign, d.type, d.len, d.beam, d.portOffset, d.bowOffset); Utils::completeNMEA(e->nmeaBuffer.sentence); EventQueue::instance().push(e); } bool Configuration::isStationDataProvisioned() { return bsp_is_station_data_provisioned(); } void Configuration::eraseStationData() { bsp_erase_station_data(); } #if OTP_DATA void Configuration::reportOTPData() { const OTPData *data = readOTP(); Event *e = EventPool::instance().newEvent(PROPR_NMEA_SENTENCE); if ( !e ) return; if ( data ) { sprintf(e->nmeaBuffer.sentence, "$PAIOTP,%s,%s*", data->serialnum, data->hwrev); } else { strcpy(e->nmeaBuffer.sentence, "$PAIOTP,,*"); } Utils::completeNMEA(e->nmeaBuffer.sentence); EventQueue::instance().push(e); } #endif void Configuration::reportXOTrimValue() { uint8_t value = 0; if ( this->isXOTrimmed() ) { value = this->getXOTrimValue(); } Event *e = EventPool::instance().newEvent(PROPR_NMEA_SENTENCE); if ( !e ) return; sprintf(e->nmeaBuffer.sentence, "$PAIXOTR,%d*", value); Utils::completeNMEA(e->nmeaBuffer.sentence); EventQueue::instance().push(e); } void Configuration::factoryReset() { bsp_erase_station_data(); mFlags = {0}; bsp_erase_config_flags(); bsp_erase_xo_trim(); } bool Configuration::writeStationData(const StationData &data) { bsp_write_station_data(data); reportStationData(); return true; } bool Configuration::readStationData(StationData &data) { bsp_read_station_data(&data); return data.magic == STATION_DATA_MAGIC; } bool Configuration::isXOTrimmed() { XOTrim x; bsp_read_xo_trim(&x); return x.magic == XO_TRIM_MAGIC; } void Configuration::setXOTrimValue(uint8_t value) { XOTrim x = {XO_TRIM_MAGIC, value, {0}}; bsp_write_xo_trim(x); } uint8_t Configuration::getXOTrimValue() { XOTrim x; bsp_read_xo_trim(&x); return x.value; } #if OTP_DATA const OTPData *Configuration::readOTP() { uint32_t address = nextAvailableOTPSlot(); if ( address == OTP_ADDRESS ) // There's nothing written! return nullptr; address -= sizeof(OTPData); if ( IS_FLASH_OTP_ADDRESS(address) ) return (const OTPData*)address; return nullptr; } bool Configuration::writeOTP(const OTPData &data) { uint32_t address = nextAvailableOTPSlot(); if ( !IS_FLASH_OTP_ADDRESS(address) ) return false; uint64_t *d = (uint64_t*)&data; __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); HAL_FLASH_Unlock(); HAL_StatusTypeDef status = HAL_OK; for ( uint32_t dw = 0; dw < sizeof data/8; ++dw, ++d ) { status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address + dw*8, *d); if ( status != HAL_OK ) break; } HAL_FLASH_Lock(); return true; } uint32_t Configuration::nextAvailableOTPSlot() { for ( uint32_t p = OTP_ADDRESS; p < OTP_ADDRESS+OTP_SIZE; p += sizeof (OTPData) ) { OTPData *d = (OTPData*)p; if ( d->magic == 0xFFFFFFFF ) return p; } return OTP_ADDRESS+OTP_SIZE; } #endif