From dc250a25d6f51706fea8c172cabf14dfddb25066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Izzo?= Date: Tue, 29 Mar 2022 18:31:39 +0200 Subject: [PATCH] Implement new codeplug interface The new codeplug interface was implemented for linux and retrofitted to all the existing OpenRTX platforms. Limited unit testing was also implemented. TG-428 --- .gitignore | 3 + meson.build | 20 +- openrtx/include/core/cps.h | 29 +- openrtx/include/core/state.h | 2 +- openrtx/include/core/utils.h | 6 + openrtx/include/interfaces/cps_io.h | 46 ++- openrtx/src/core/cps_io_libc.c | 147 +++++++++ openrtx/src/core/state.c | 23 +- openrtx/src/core/utils.c | 12 + openrtx/src/ui/ui.c | 34 +- openrtx/src/ui/ui_main.c | 9 +- openrtx/src/ui/ui_menu.c | 4 +- .../{NVM/nvmData_GDx.h => CPS/cps_data_GDx.h} | 0 .../nvmData_MD3x0.h => CPS/cps_data_MD3x0.h} | 0 .../cps_data_MDUV3x0.h} | 0 platform/drivers/CPS/cps_io_native_GDx.c | 276 ++++++++++++++++ platform/drivers/CPS/cps_io_native_MD3x0.c | 229 ++++++++++++++ platform/drivers/CPS/cps_io_native_MD9600.c | 260 ++++++++++++++++ platform/drivers/CPS/cps_io_native_MDUV3x0.c | 262 ++++++++++++++++ platform/drivers/CPS/cps_io_native_Mod17.c | 75 +++++ platform/drivers/NVM/nvmem_GDx.c | 294 +----------------- platform/drivers/NVM/nvmem_MD3x0.c | 186 +---------- platform/drivers/NVM/nvmem_MD9600.c | 219 +------------ platform/drivers/NVM/nvmem_MDUV3x0.c | 234 +------------- platform/drivers/NVM/nvmem_Mod17.c | 21 -- platform/drivers/NVM/nvmem_linux.c | 37 --- tests/unit/cps.c | 19 ++ 27 files changed, 1417 insertions(+), 1030 deletions(-) create mode 100644 openrtx/src/core/cps_io_libc.c rename platform/drivers/{NVM/nvmData_GDx.h => CPS/cps_data_GDx.h} (100%) rename platform/drivers/{NVM/nvmData_MD3x0.h => CPS/cps_data_MD3x0.h} (100%) rename platform/drivers/{NVM/nvmData_MDUV3x0.h => CPS/cps_data_MDUV3x0.h} (100%) create mode 100644 platform/drivers/CPS/cps_io_native_GDx.c create mode 100644 platform/drivers/CPS/cps_io_native_MD3x0.c create mode 100644 platform/drivers/CPS/cps_io_native_MD9600.c create mode 100644 platform/drivers/CPS/cps_io_native_MDUV3x0.c create mode 100644 platform/drivers/CPS/cps_io_native_Mod17.c create mode 100644 tests/unit/cps.c diff --git a/.gitignore b/.gitignore index 4791dce5..176dd856 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,6 @@ subprojects/tinyusb # Compiled script scripts/get_demod_log + +# OpenRTX Codeplugs +*.rtxc diff --git a/meson.build b/meson.build index 7eeefd95..ebea3c74 100644 --- a/meson.build +++ b/meson.build @@ -155,6 +155,7 @@ gdx_src = ['openrtx/src/core/xmodem.c', 'platform/drivers/NVM/AT24Cx_GDx.c', 'platform/drivers/NVM/spiFlash_GDx.c', 'platform/drivers/NVM/nvmem_GDx.c', + 'platform/drivers/CPS/cps_io_native_GDx.c', 'platform/drivers/ADC/ADC0_GDx.c', 'platform/drivers/backlight/backlight_GDx.c', 'platform/drivers/baseband/radio_GDx.cpp', @@ -250,7 +251,9 @@ linux_platform_src = ['platform/targets/linux/emulator/emulator.c', 'platform/drivers/audio/audio_linux.c', 'platform/drivers/audio/inputStream_linux.c', 'platform/drivers/audio/outputStream_linux.c', - 'platform/targets/linux/platform.c'] + 'platform/targets/linux/platform.c', + 'openrtx/src/core/cps_io_libc.c'] + linux_src = src + linux_platform_src @@ -274,6 +277,7 @@ endif ## TYT MD-3x0 family ## md3x0_src = src + mdx_src + stm32f405_src + ['platform/drivers/NVM/nvmem_MD3x0.c', + 'platform/drivers/CPS/cps_io_native_MD3x0.c', 'platform/drivers/NVM/spiFlash_MD3x.c', 'platform/drivers/baseband/SKY72310.c', 'platform/drivers/baseband/radio_MD3x0.cpp', @@ -290,6 +294,7 @@ md3x0_def = def + stm32f405_def + {'PLATFORM_MD3x0': '', 'timegm': 'mktime'} ## mduv3x0_src = src + mdx_src + stm32f405_src + ['platform/drivers/NVM/nvmem_MDUV3x0.c', 'platform/drivers/NVM/spiFlash_MD3x.c', + 'platform/drivers/CPS/cps_io_native_MDUV3x0.c', 'platform/targets/MD-UV3x0/platform.c', 'platform/drivers/keyboard/keyboard_MD3x.c', 'platform/drivers/display/HX8353_MD3x.cpp', @@ -310,7 +315,8 @@ md9600_src = src + mdx_src + stm32f405_src + ['platform/targets/MD-9600/platform 'platform/drivers/chSelector/chSelector_MD9600.c', 'platform/drivers/baseband/radio_MD9600.cpp', 'platform/drivers/NVM/nvmem_MD9600.c', - 'platform/drivers/NVM/spiFlash_MD9600.c'] + 'platform/drivers/NVM/spiFlash_MD9600.c', + 'platform/drivers/CPS/cps_io_native_MD9600.c'] md9600_inc = inc + stm32f405_inc + ['platform/targets/MD-9600'] md9600_def = def + stm32f405_def + {'PLATFORM_MD9600': ''} @@ -339,6 +345,7 @@ mod17_src = src + stm32f405_src + ['platform/targets/Module17/platform.c', 'platform/drivers/ADC/ADC1_Mod17.c', 'platform/drivers/keyboard/keyboard_Mod17.c', 'platform/drivers/NVM/nvmem_Mod17.c', + 'platform/drivers/CPS/cps_io_native_Mod17.c', 'platform/drivers/baseband/radio_Mod17.cpp', 'platform/drivers/audio/inputStream_Mod17.cpp', 'platform/drivers/audio/outputStream_Mod17.cpp', @@ -635,7 +642,12 @@ m17_rrc_test = executable('m17_rrc_test', sources: unit_test_src + ['tests/unit/M17_rrc.cpp'], kwargs: unit_test_opts) +cps_test = executable('cps_test', + sources : unit_test_src + ['tests/unit/cps.c'], + kwargs : unit_test_opts) + test('M17 Golay Unit Test', m17_golay_test) test('M17 Viterbi Unit Test', m17_viterbi_test) -test('M17 Demodulator Test', m17_demodulator_test) -test('M17 RRC Test', m17_rrc_test) +test('M17 Demodulator Test', m17_demodulator_test) +test('M17 RRC Test', m17_rrc_test) +test('Codeplug Test', cps_test) diff --git a/openrtx/include/core/cps.h b/openrtx/include/core/cps.h index 66377870..eba6ace6 100644 --- a/openrtx/include/core/cps.h +++ b/openrtx/include/core/cps.h @@ -27,11 +27,12 @@ #include // Magic number to identify the binary file -#define CPS_MAGIC 0x52545843 +#define CPS_MAGIC 0x43585452 // Codeplug version v0.1 #define CPS_VERSION_MAJOR 0 #define CPS_VERSION_MINOR 1 #define CPS_VERSION_NUMBER (CPS_VERSION_MAJOR << 8) | CPS_VERSION_MINOR +#define CPS_STR_SIZE 32 /****************************************************************************** @@ -202,8 +203,8 @@ typedef struct uint8_t scanList_index; //< Scan List: None, ScanList1...250 uint8_t groupList_index; //< Group List: None, GroupList1...128 - char name[16]; //< Channel name - char descr[16]; //< Description of the channel + char name[CPS_STR_SIZE]; //< Channel name + char descr[CPS_STR_SIZE]; //< Description of the channel geo_t ch_location; //< Transmitter geolocation union @@ -220,7 +221,7 @@ __attribute__((packed)) channel_t; // 59B */ typedef struct { - char name[16]; //< Display name of the contact + char name[CPS_STR_SIZE]; //< Display name of the contact uint8_t mode; //< Operating mode union @@ -232,28 +233,18 @@ typedef struct } __attribute__((packed)) contact_t; // 23B -/** - * Data structure containing all the information of a bank. - * Legacy data structure for brackwards compatibility. - */ -typedef struct -{ - char name[16]; //< Bank name - uint16_t member[64]; //< Channel indexes -} -__attribute__((packed)) bank_t; - /** * Data structure describing a bank header. + * A bank is a variable size structure composed of a bank header and a + * variable length array of uint32_t each representing a channel index. */ typedef struct { - char name[16]; + char name[CPS_STR_SIZE]; uint16_t ch_count; //< Count of all the channels in this bank } __attribute__((packed)) bankHdr_t; // 18B + 2 * ch_count - /** * The codeplug binary structure is composed by: * - A header struct @@ -266,8 +257,8 @@ typedef struct { uint64_t magic; //< Magic number "RTXC" uint16_t version_number; //< Version number for the cps structure - char author[16]; //< Author of the codeplug - char descr[16]; //< Description of the codeplug + char author[CPS_STR_SIZE]; //< Author of the codeplug + char descr[CPS_STR_SIZE]; //< Description of the codeplug uint64_t timestamp; //< unix timestamp of the codeplug uint16_t ct_count; //< Number of stored contacts diff --git a/openrtx/include/core/state.h b/openrtx/include/core/state.h index 691c642a..11070f5d 100644 --- a/openrtx/include/core/state.h +++ b/openrtx/include/core/state.h @@ -57,7 +57,7 @@ typedef struct channel_t channel; channel_t vfo_channel; bool bank_enabled; - bank_t bank; + uint16_t bank; uint8_t rtxStatus; bool rtxShutdown; diff --git a/openrtx/include/core/utils.h b/openrtx/include/core/utils.h index 5075177f..b69c26fc 100644 --- a/openrtx/include/core/utils.h +++ b/openrtx/include/core/utils.h @@ -57,6 +57,12 @@ uint8_t interpCalParameter(const freq_t freq, const freq_t *calPoints, */ float dBmToWatt(const uint8_t n); +/** + * \internal Utility function to convert 4 byte BCD values into a 32-bit + * unsigned integer ones. + */ +uint32_t bcd2bin(uint32_t bcd); + #ifdef __cplusplus } #endif diff --git a/openrtx/include/interfaces/cps_io.h b/openrtx/include/interfaces/cps_io.h index b350ab1e..eeedc091 100644 --- a/openrtx/include/interfaces/cps_io.h +++ b/openrtx/include/interfaces/cps_io.h @@ -32,6 +32,35 @@ extern "C" { * Interface for codeplug memory management. */ +/** + * Open a codeplug identified by its name. + * + * @param name: name of the codeplug, if omitted the default codeplug is used + * @return 0 on success, -1 on failure + */ +int cps_open(char *cps_name); + +/** + * Close a codeplug. + */ +void cps_close(); + +/** + * Initializes a new cps or clears cps data if one already exists. + * + * @param cps_name: codeplug name, if empty the default cps is initialized + * @return 0 on success, -1 on failure + */ +int cps_create(char *cps_name); + +/** + * Read one contact from table stored in nonvolatile memory. + * + * @param contact: pointer to the contact_t data structure to be populated. + * @param pos: position, inside the bank table, from which read data. + * @return 0 on success, -1 on failure + */ +int cps_readContactData(contact_t *contact, uint16_t pos); /** * Read one channel entry from table stored in nonvolatile memory. @@ -43,23 +72,22 @@ extern "C" { int cps_readChannelData(channel_t *channel, uint16_t pos); /** - * Read one bank from the codeplug stored in the radio's filesystem. + * Read one bank header from the codeplug stored in the radio's filesystem. * - * @param bank: pointer to the struct to be populated with the bank data. + * @param b_header: pointer to the struct to be populated with the bank header. * @param pos: position, inside the bank table, from which read data. * @return 0 on success, -1 on failure */ -int cps_readBankData(bank_t *bank, uint16_t pos); +int cps_readBankHeader(bankHdr_t *b_header, uint16_t pos); /** - * Read one contact from table stored in nonvolatile memory. + * Read one channel index from a bank of the codeplug stored in NVM. * - * @param contact: pointer to the contact_t data structure to be populated. - * @param pos: position, inside the bank table, from which read data. - * @return 0 on success, -1 on failure + * @param bank_pos: position of the bank inside the cps. + * @param ch_pos: position of the channel index inside the bank. + * @return the retrieved channel index on success, -1 on failure */ -int cps_readContactData(contact_t *contact, uint16_t pos); - +int32_t cps_readBankData(uint16_t bank_pos, uint16_t ch_pos); #ifdef __cplusplus } diff --git a/openrtx/src/core/cps_io_libc.c b/openrtx/src/core/cps_io_libc.c new file mode 100644 index 00000000..a073511f --- /dev/null +++ b/openrtx/src/core/cps_io_libc.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include + +static FILE *cps_file = NULL; +const char *default_author = "Codeplug author."; +const char *default_descr = "Codeplug description."; + +/** + * Internal: read and validate codeplug header + * + * @param header: pointer to the header struct to be populated + * @return 0 on success, -1 on failure + */ +bool _readHeader(cps_header_t *header) { + fseek(cps_file, 0L, SEEK_SET); + fread(header, sizeof(cps_header_t), 1, cps_file); + // Validate magic number + if(header->magic != CPS_MAGIC) + return -1; + // Validate version number + if(((header->version_number & 0xff00) >> 8) != CPS_VERSION_MAJOR || + (header->version_number & 0x00ff) > CPS_VERSION_MINOR) + return -1; + return 0; +} + +/** + * Internal: push down data at a given offset by a given amount + * + * @param offset: offset at which to start to push down data + * @param amount: amount of free space to be created + * @return 0 on success, -1 on failure + */ +int _pushDown(uint32_t offset, uint32_t amount) { + + +} + +int cps_open(char *cps_name) { + if (!cps_name) + cps_name = "default.rtxc"; + cps_file = fopen(cps_name, "r+"); + if (!cps_file) + return -1; + return 0; +} + +void cps_close() { + fclose(cps_file); +} + +int cps_create(char *cps_name) { + // Clear or create cps file + FILE *new_cps = NULL; + if (!cps_name) + cps_name = "default.rtxc"; + new_cps = fopen(cps_name, "w"); + if (!new_cps) + return -1; + // Write new header + cps_header_t header = { 0 }; + header.magic = CPS_MAGIC; + header.version_number = CPS_VERSION_MAJOR << 8 | CPS_VERSION_MINOR; + strncpy(header.author, default_author, 17); + strncpy(header.descr, default_descr, 23); + // TODO: Implement unix timestamp in Miosix + header.timestamp = time(NULL); + header.ct_count = 0; + header.ch_count = 0; + header.b_count = 0; + fwrite(&header, sizeof(cps_header_t), 1, new_cps); + fclose(new_cps); + return 0; +} + +int cps_readContactData(contact_t *contact, uint16_t pos) { + cps_header_t header = { 0 }; + if (!_readHeader(&header)) + return -1; + if (pos >= header.ct_count) + return -1; + fseek(cps_file, pos * sizeof(contact_t), SEEK_CUR); + fread(contact, sizeof(contact_t), 1, cps_file); + return 0; +} + +int cps_readChannelData(channel_t *channel, uint16_t pos) { + cps_header_t header = { 0 }; + if (!_readHeader(&header)) + return -1; + if (pos >= header.ch_count) + return -1; + fseek(cps_file, + header.ct_count * sizeof(contact_t) + + pos * sizeof(channel_t), + SEEK_CUR); + fread(channel, sizeof(channel_t), 1, cps_file); + return 0; +} + +int cps_readBankHeader(bankHdr_t *b_header, uint16_t pos) { + cps_header_t header = { 0 }; + if (!_readHeader(&header)) + return -1; + if (pos >= header.b_count) + return -1; + fseek(cps_file, + header.ct_count * sizeof(contact_t) + + header.ch_count * sizeof(channel_t) + + pos * sizeof(uint32_t), + SEEK_CUR); + uint32_t offset = 0; + fread(&offset, sizeof(uint32_t), 1, cps_file); + fseek(cps_file, header.b_count - pos * sizeof(uint32_t) + offset, SEEK_CUR); + fread(b_header, sizeof(bankHdr_t), 1, cps_file); + return 0; +} + +uint32_t cps_readBankData(uint16_t bank_pos, uint16_t ch_pos) { + cps_header_t header = { 0 }; + if (!_readHeader(&header)) + return -1; + if (bank_pos >= header.b_count) + return -1; + fseek(cps_file, + header.ct_count * sizeof(contact_t) + + header.ch_count * sizeof(channel_t) + + bank_pos * sizeof(uint32_t), + SEEK_CUR); + uint32_t offset = 0; + fread(&offset, sizeof(uint32_t), 1, cps_file); + fseek(cps_file, header.b_count - bank_pos * sizeof(uint32_t) + offset, SEEK_CUR); + bankHdr_t b_header = { 0 }; + fread(&b_header, sizeof(bankHdr_t), 1, cps_file); + if (ch_pos >= b_header.ch_count) + return -1; + fseek(cps_file, ch_pos * sizeof(uint32_t), SEEK_CUR); + uint32_t ch_index = 0; + fread(&ch_index, sizeof(uint32_t), 1, cps_file); + return ch_index; +} + +int cps_writeContactData(contact_t contact, uint16_t pos) { + +} diff --git a/openrtx/src/core/state.c b/openrtx/src/core/state.c index 4df987ae..8e664b9b 100644 --- a/openrtx/src/core/state.c +++ b/openrtx/src/core/state.c @@ -20,12 +20,13 @@ #include #include +#include #include #include #include #include #include -#include +#include state_t state; @@ -50,6 +51,26 @@ void state_init() state.channel = cps_getDefaultChannel(); } + /* + * Try loading default codeplug from nonvolatile memory, if fails create + * an empty one. + */ + if(cps_open(NULL) < 0) + { + cps_create(NULL); + + // If cannot open the newly created codeplug -> unrecoverable error + if(cps_open(NULL) < 0) + { + #ifdef PLATFORM_LINUX + exit(-1); + #else + // TODO: implement error handling for non-linux targets + while(1) ; + #endif + } + } + /* * Initialise remaining fields */ diff --git a/openrtx/src/core/utils.c b/openrtx/src/core/utils.c index 3fb79fb3..9ba574ee 100644 --- a/openrtx/src/core/utils.c +++ b/openrtx/src/core/utils.c @@ -58,3 +58,15 @@ float dBmToWatt(const uint8_t n) return power; } + +uint32_t bcd2bin(uint32_t bcd) +{ + return ((bcd >> 28) & 0x0F) * 10000000 + + ((bcd >> 24) & 0x0F) * 1000000 + + ((bcd >> 20) & 0x0F) * 100000 + + ((bcd >> 16) & 0x0F) * 10000 + + ((bcd >> 12) & 0x0F) * 1000 + + ((bcd >> 8) & 0x0F) * 100 + + ((bcd >> 4) & 0x0F) * 10 + + (bcd & 0x0F); +} diff --git a/openrtx/src/ui/ui.c b/openrtx/src/ui/ui.c index 49377752..e746bb26 100644 --- a/openrtx/src/ui/ui.c +++ b/openrtx/src/ui/ui.c @@ -538,26 +538,25 @@ bool _ui_drawDarkOverlay() { return true; } -int _ui_fsm_loadChannel(uint16_t bank_index, bool *sync_rtx) { - uint16_t channel_index = bank_index; +int _ui_fsm_loadChannel(uint16_t channel_index, bool *sync_rtx) { channel_t channel; // If a bank is active, get index from current bank if(state.bank_enabled) { - // Calculate bank size - const uint8_t bank_size = sizeof(state.bank.member)/sizeof(state.bank.member[0]); - if((bank_index <= 0) || (bank_index > bank_size)) + bankHdr_t bank = { 0 }; + cps_readBankHeader(&bank, state.bank); + if((channel_index <= 0) || (channel_index > bank.ch_count)) return -1; else // Channel index is 1-based while bank array access is 0-based - channel_index = state.bank.member[bank_index - 1]; + channel_index = cps_readBankData(state.bank, channel_index); } int result = cps_readChannelData(&channel, channel_index); // Read successful and channel is valid if(result != -1 && _ui_channel_valid(&channel)) { // Set new channel index - state.channel_index = bank_index; + state.channel_index = channel_index; // Copy channel read to state state.channel = channel; *sync_rtx = true; @@ -1309,24 +1308,23 @@ void ui_updateFSM(event_t event, bool *sync_rtx) { if(state.ui_screen == MENU_BANK) { - bank_t bank; - // menu_selected is 0-based while channels are 1-based - // menu_selected == 0 corresponds to "All Channels" bank - if(cps_readBankData(&bank, ui_state.menu_selected + 1) != -1) + bankHdr_t bank; + // manu_selected is 0-based + // bank 0 means "All Channel" mode + // banks (1, n) are mapped to banks (0, n-1) + if(cps_readBankHeader(&bank, ui_state.menu_selected + 1) != -1) ui_state.menu_selected += 1; } else if(state.ui_screen == MENU_CHANNEL) { channel_t channel; - // menu_selected is 0-based while channels are 1-based - if(cps_readChannelData(&channel, ui_state.menu_selected + 2) != -1) + if(cps_readChannelData(&channel, ui_state.menu_selected + 1) != -1) ui_state.menu_selected += 1; } else if(state.ui_screen == MENU_CONTACTS) { contact_t contact; - // menu_selected is 0-based while channels are 1-based - if(cps_readContactData(&contact, ui_state.menu_selected + 2) != -1) + if(cps_readContactData(&contact, ui_state.menu_selected + 1) != -1) ui_state.menu_selected += 1; } } @@ -1334,7 +1332,7 @@ void ui_updateFSM(event_t event, bool *sync_rtx) { if(state.ui_screen == MENU_BANK) { - bank_t newbank; + bankHdr_t newbank; int result = 0; // If "All channels" is selected, load default bank if(ui_state.menu_selected == 0) @@ -1342,11 +1340,11 @@ void ui_updateFSM(event_t event, bool *sync_rtx) else { state.bank_enabled = true; - result = cps_readBankData(&newbank, ui_state.menu_selected); + result = cps_readBankHeader(&newbank, ui_state.menu_selected - 1); } if(result != -1) { - state.bank = newbank; + state.bank = ui_state.menu_selected - 1; // If we were in VFO mode, save VFO channel if(ui_state.last_main_state == MAIN_VFO) state.vfo_channel = state.channel; diff --git a/openrtx/src/ui/ui_main.c b/openrtx/src/ui/ui_main.c index 99d4fda0..5e31c141 100644 --- a/openrtx/src/ui/ui_main.c +++ b/openrtx/src/ui/ui_main.c @@ -77,13 +77,16 @@ void _ui_drawBankChannel() if(!last_state.bank_enabled) gfx_print(layout.line1_pos, layout.line1_font, TEXT_ALIGN_LEFT, color_white, "bank: All channels"); - else + else { + bankHdr_t bank = { 0 }; + cps_readBankHeader(&bank, last_state.bank); gfx_print(layout.line1_pos, layout.line1_font, TEXT_ALIGN_LEFT, - color_white, "bank: %.13s", last_state.bank.name); + color_white, "bank: %.13s", bank.name); + } // Print Channel name gfx_print(layout.line2_pos, layout.line2_font, TEXT_ALIGN_LEFT, color_white, " %03d: %.12s", - last_state.channel_index, last_state.channel.name); + last_state.channel_index + 1, last_state.channel.name); } void _ui_drawFrequency() diff --git a/openrtx/src/ui/ui_menu.c b/openrtx/src/ui/ui_menu.c index 62c0dd51..8df19c4b 100644 --- a/openrtx/src/ui/ui_menu.c +++ b/openrtx/src/ui/ui_menu.c @@ -268,8 +268,8 @@ int _ui_getBankName(char *buf, uint8_t max_len, uint8_t index) } else { - bank_t bank; - result = cps_readBankData(&bank, index); + bankHdr_t bank; + result = cps_readBankHeader(&bank, index); if(result != -1) snprintf(buf, max_len, "%s", bank.name); } diff --git a/platform/drivers/NVM/nvmData_GDx.h b/platform/drivers/CPS/cps_data_GDx.h similarity index 100% rename from platform/drivers/NVM/nvmData_GDx.h rename to platform/drivers/CPS/cps_data_GDx.h diff --git a/platform/drivers/NVM/nvmData_MD3x0.h b/platform/drivers/CPS/cps_data_MD3x0.h similarity index 100% rename from platform/drivers/NVM/nvmData_MD3x0.h rename to platform/drivers/CPS/cps_data_MD3x0.h diff --git a/platform/drivers/NVM/nvmData_MDUV3x0.h b/platform/drivers/CPS/cps_data_MDUV3x0.h similarity index 100% rename from platform/drivers/NVM/nvmData_MDUV3x0.h rename to platform/drivers/CPS/cps_data_MDUV3x0.h diff --git a/platform/drivers/CPS/cps_io_native_GDx.c b/platform/drivers/CPS/cps_io_native_GDx.c new file mode 100644 index 00000000..3e28c974 --- /dev/null +++ b/platform/drivers/CPS/cps_io_native_GDx.c @@ -0,0 +1,276 @@ +/*************************************************************************** + * Copyright (C) 2020 - 2022 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * 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; 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 +#include +#include +#include +#include "AT24Cx.h" +#include "W25Qx.h" +#include "cps_data_GDx.h" + +//static const uint32_t zoneBaseAddr = 0x149e0; /**< Base address of zones */ +static const uint32_t channelBaseAddrEEPROM = 0x03780; /**< Base address of channel data */ +static const uint32_t channelBaseAddrFlash = 0x7b1c0; /**< Base address of channel data */ +static const uint32_t vfoChannelBaseAddr = 0x7590; /**< Base address of VFO channel */ +static const uint32_t zoneBaseAddr = 0x8010; /**< Base address of zones */ +static const uint32_t contactBaseAddr = 0x87620; /**< Base address of contacts */ +static const uint32_t maxNumChannels = 1024; /**< Maximum number of channels in memory */ +static const uint32_t maxNumZones = 68; /**< Maximum number of zones in memory */ +static const uint32_t maxNumContacts = 1024; /**< Maximum number of contacts in memory */ + +// Strings in GD-77 codeplug are terminated with 0xFF, +// replace 0xFF terminator with 0x00 to be compatible with C strings +static void _addStringTerminator(char *buf, uint8_t max_len) +{ + for(int i=0; i= maxNumChannels) + return -1; + + memset(channel, 0x00, sizeof(channel_t)); + + // Channels are organized in 128-channel banks + uint8_t bank_num = pos / 128; + uint8_t bank_channel = pos % 128; + + // ### Read channel bank bitmap ### + uint8_t bitmap[16]; + // First channel bank (128 channels) is saved in EEPROM + if(pos < 128) + { + uint32_t readAddr = channelBaseAddrEEPROM + bank_num * sizeof(gdxChannelBank_t); + AT24Cx_readData(readAddr, ((uint8_t *) &bitmap), sizeof(bitmap)); + } + // Remaining 7 channel banks (896 channels) are saved in SPI Flash + else + { + W25Qx_wakeup(); + delayUs(5); + uint32_t readAddr = channelBaseAddrFlash + (bank_num - 1) * sizeof(gdxChannelBank_t); + W25Qx_readData(readAddr, ((uint8_t *) &bitmap), sizeof(bitmap)); + W25Qx_sleep(); + } + uint8_t bitmap_byte = bank_channel / 8; + uint8_t bitmap_bit = bank_channel % 8; + gdxChannel_t chData; + // The channel is marked not valid in the bitmap + if(!(bitmap[bitmap_byte] & (1 << bitmap_bit))) + return -1; + // The channel is marked valid in the bitmap + // ### Read desired channel from the correct bank ### + else + { + uint32_t channelOffset = sizeof(bitmap) + pos * sizeof(gdxChannel_t); + // First channel bank (128 channels) is saved in EEPROM + if(pos < 128) + { + uint32_t bankAddr = channelBaseAddrEEPROM + bank_num * sizeof(gdxChannelBank_t); + AT24Cx_readData(bankAddr + channelOffset, ((uint8_t *) &chData), sizeof(gdxChannel_t)); + } + // Remaining 7 channel banks (896 channels) are saved in SPI Flash + else + { + W25Qx_wakeup(); + delayUs(5); + uint32_t bankAddr = channelBaseAddrFlash + bank_num * sizeof(gdxChannelBank_t); + W25Qx_readData(bankAddr + channelOffset, ((uint8_t *) &chData), sizeof(gdxChannel_t)); + W25Qx_sleep(); + } + } + // Copy data to OpenRTX channel_t + channel->mode = chData.channel_mode + 1; + channel->bandwidth = chData.bandwidth; + channel->rx_only = chData.rx_only; + channel->power = ((chData.power == 1) ? 135 : 100); + channel->rx_frequency = bcd2bin(chData.rx_frequency) * 10; + channel->tx_frequency = bcd2bin(chData.tx_frequency) * 10; + channel->scanList_index = chData.scan_list_index; + channel->groupList_index = chData.group_list_index; + memcpy(channel->name, chData.name, sizeof(chData.name)); + // Terminate string with 0x00 instead of 0xFF + _addStringTerminator(channel->name, sizeof(chData.name)); + + /* Load mode-specific parameters */ + if(channel->mode == OPMODE_FM) + { + channel->fm.txToneEn = 0; + channel->fm.rxToneEn = 0; + uint16_t rx_css = chData.ctcss_dcs_receive; + uint16_t tx_css = chData.ctcss_dcs_transmit; + + // TODO: Implement binary search to speed up this lookup + if((rx_css != 0) && (rx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(rx_css))) + { + channel->fm.rxTone = i; + channel->fm.rxToneEn = 1; + break; + } + } + } + + if((tx_css != 0) && (tx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(tx_css))) + { + channel->fm.txTone = i; + channel->fm.txToneEn = 1; + break; + } + } + } + + // TODO: Implement warning screen if tone was not found + } + else if(channel->mode == OPMODE_DMR) + { + channel->dmr.contactName_index = chData.contact_name_index; + channel->dmr.dmr_timeslot = chData.repeater_slot; + channel->dmr.rxColorCode = chData.colorcode_rx; + channel->dmr.txColorCode = chData.colorcode_tx; + } + return 0; +} + +int cps_readBankHeader(bankHdr_t *b_header, uint16_t pos) +{ + if(pos >= maxNumZones) return -1; + + // ### Read bank bank bitmap ### + uint8_t bitmap[32]; + AT24Cx_readData(zoneBaseAddr, ((uint8_t *) &bitmap), sizeof(bitmap)); + + uint8_t bitmap_byte = pos / 8; + uint8_t bitmap_bit = pos % 8; + // The bank is marked not valid in the bitmap + if(!(bitmap[bitmap_byte] & (1 << bitmap_bit))) return -1; + + gdxZone_t zoneData; + uint32_t zoneAddr = zoneBaseAddr + sizeof(bitmap) + pos * sizeof(gdxZone_t); + AT24Cx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(gdxZone_t)); + + // Check if bank is empty + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + + memcpy(b_header->name, zoneData.name, sizeof(zoneData.name)); + // Terminate string with 0x00 instead of 0xFF + _addStringTerminator(b_header->name, sizeof(zoneData.name)); + return 0; +} + +int32_t cps_readBankData(uint16_t bank_pos, uint16_t ch_pos) +{ + if(bank_pos >= maxNumZones) return -1; + + // ### Read bank bank bitmap ### + uint8_t bitmap[32]; + AT24Cx_readData(zoneBaseAddr, ((uint8_t *) &bitmap), sizeof(bitmap)); + + uint8_t bitmap_byte = bank_pos / 8; + uint8_t bitmap_bit = bank_pos % 8; + // The bank is marked not valid in the bitmap + if(!(bitmap[bitmap_byte] & (1 << bitmap_bit))) return -1; + + gdxZone_t zoneData; + uint32_t zoneAddr = zoneBaseAddr + sizeof(bitmap) + bank_pos * sizeof(gdxZone_t); + AT24Cx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(gdxZone_t)); + + // Check if bank is empty + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + return zoneData.member[ch_pos]; +} + +int cps_readContactData(contact_t *contact, uint16_t pos) +{ + if(pos >= maxNumContacts) return -1; + + W25Qx_wakeup(); + delayUs(5); + + gdxContact_t contactData; + uint32_t contactAddr = contactBaseAddr + pos * sizeof(gdxContact_t); + W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(gdxContact_t)); + W25Qx_sleep(); + + // Check if contact is empty + if(wcslen((wchar_t *) contactData.name) == 0) return -1; + + // Copy contact name + memcpy(contact->name, contactData.name, sizeof(contactData.name)); + // Terminate string with 0x00 instead of 0xFF + _addStringTerminator(contact->name, sizeof(contactData.name)); + + contact->mode = OPMODE_DMR; + + // Copy contact DMR ID + contact->info.dmr.id = contactData.id[0] + | (contactData.id[1] << 8) + | (contactData.id[2] << 16); + + // Copy contact details + contact->info.dmr.contactType = contactData.type; + contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; + + return 0; +} diff --git a/platform/drivers/CPS/cps_io_native_MD3x0.c b/platform/drivers/CPS/cps_io_native_MD3x0.c new file mode 100644 index 00000000..da1636fe --- /dev/null +++ b/platform/drivers/CPS/cps_io_native_MD3x0.c @@ -0,0 +1,229 @@ +/*************************************************************************** + * Copyright (C) 2020 - 2022 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * 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; 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 +#include +#include +#include +#include "cps_data_MD3x0.h" +#include "W25Qx.h" + +static const uint32_t zoneBaseAddr = 0x149e0; /**< Base address of zones */ +static const uint32_t chDataBaseAddr = 0x1ee00; /**< Base address of channel data */ +static const uint32_t contactBaseAddr = 0x05f80; /**< Base address of contacts */ +static const uint32_t maxNumChannels = 1000; /**< Maximum number of channels in memory */ +static const uint32_t maxNumZones = 250; /**< Maximum number of zones in memory */ +static const uint32_t maxNumContacts = 10000; /**< Maximum number of contacts in memory */ + + +/** + * This function does not apply to address-based codeplugs + */ +int cps_open(char *cps_name) +{ + + (void) cps_name; + return 0; +} + +/** + * This function does not apply to address-based codeplugs + */ +void cps_close() +{ +} + +/** + * This function does not apply to address-based codeplugs + */ +int cps_create(char *cps_name) +{ + (void) cps_name; + return 0; +} + +int cps_readChannelData(channel_t *channel, uint16_t pos) +{ + if(pos >= maxNumChannels) return -1; + + memset(channel, 0x00, sizeof(channel_t)); + + W25Qx_wakeup(); + delayUs(5); + + md3x0Channel_t chData; + uint32_t readAddr = chDataBaseAddr + pos * sizeof(md3x0Channel_t); + W25Qx_readData(readAddr, ((uint8_t *) &chData), sizeof(md3x0Channel_t)); + W25Qx_sleep(); + + channel->mode = chData.channel_mode; + channel->bandwidth = chData.bandwidth; + channel->rx_only = chData.rx_only; + channel->power = ((chData.power == 1) ? 135 : 100); + channel->rx_frequency = bcd2bin(chData.rx_frequency) * 10; + channel->tx_frequency = bcd2bin(chData.tx_frequency) * 10; + channel->scanList_index = chData.scan_list_index; + channel->groupList_index = chData.group_list_index; + + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + channel->name[i] = ((char) (chData.name[i] & 0x00FF)); + } + + /* Load mode-specific parameters */ + if(channel->mode == OPMODE_FM) + { + channel->fm.txToneEn = 0; + channel->fm.rxToneEn = 0; + uint16_t rx_css = chData.ctcss_dcs_receive; + uint16_t tx_css = chData.ctcss_dcs_transmit; + + // TODO: Implement binary search to speed up this lookup + if((rx_css != 0) && (rx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(rx_css))) + { + channel->fm.rxTone = i; + channel->fm.rxToneEn = 1; + break; + } + } + } + + if((tx_css != 0) && (tx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(tx_css))) + { + channel->fm.txTone = i; + channel->fm.txToneEn = 1; + break; + } + } + } + + // TODO: Implement warning screen if tone was not found + } + else if(channel->mode == OPMODE_DMR) + { + channel->dmr.contactName_index = chData.contact_name_index; + channel->dmr.dmr_timeslot = chData.repeater_slot; + channel->dmr.rxColorCode = chData.colorcode; + channel->dmr.txColorCode = chData.colorcode; + } + + return 0; +} + + +int cps_readBankHeader(bankHdr_t *b_header, uint16_t pos) +{ + if(pos >= maxNumZones) return -1; + + W25Qx_wakeup(); + delayUs(5); + + md3x0Zone_t zoneData; + uint32_t zoneAddr = zoneBaseAddr + pos * sizeof(md3x0Zone_t); + W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(md3x0Zone_t)); + W25Qx_sleep(); + + // Check if zone is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + b_header->name[i] = ((char) (zoneData.name[i] & 0x00FF)); + } + b_header->ch_count = 16; + return 0; +} + +int32_t cps_readBankData(uint16_t bank_pos, uint16_t ch_pos) +{ + if(bank_pos >= maxNumZones) return -1; + + W25Qx_wakeup(); + delayUs(5); + + md3x0Zone_t zoneData; + uint32_t zoneAddr = zoneBaseAddr + bank_pos * sizeof(md3x0Zone_t); + W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(md3x0Zone_t)); + W25Qx_sleep(); + + // Check if zone is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + if(ch_pos > 15) return -1; + // Channel index in zones are 1 based because an empty zone contains 0s + // OpenRTX CPS interface is 0-based so we decrease by one + return zoneData.member[ch_pos] - 1; +} + +int cps_readContactData(contact_t *contact, uint16_t pos) +{ + if(pos >= maxNumContacts) return -1; + + W25Qx_wakeup(); + delayUs(5); + + md3x0Contact_t contactData; + // Note: pos is 1-based to be consistent with channels + uint32_t contactAddr = contactBaseAddr + pos * sizeof(md3x0Contact_t); + W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(md3x0Contact_t)); + W25Qx_sleep(); + + // Check if contact is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) contactData.name) == 0) return -1; + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + contact->name[i] = ((char) (contactData.name[i] & 0x00FF)); + } + + contact->mode = OPMODE_DMR; + + // Copy contact DMR ID + contact->info.dmr.id = contactData.id[0] + | (contactData.id[1] << 8) + | (contactData.id[2] << 16); + + // Copy contact details + contact->info.dmr.contactType = contactData.type; + contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; + + return 0; +} diff --git a/platform/drivers/CPS/cps_io_native_MD9600.c b/platform/drivers/CPS/cps_io_native_MD9600.c new file mode 100644 index 00000000..903d9114 --- /dev/null +++ b/platform/drivers/CPS/cps_io_native_MD9600.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (C) 2020 - 2022 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * 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; 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 +#include +#include +#include +#include +#include "cps_data_MDUV3x0.h" +#include "W25Qx.h" + +static const uint32_t zoneBaseAddr = 0x149E0; /**< Base address of zones */ +static const uint32_t zoneExtBaseAddr = 0x31000; /**< Base address of zone extensions */ +static const uint32_t vfoChannelBaseAddr = 0x2EF00; /**< Base address of VFO channel */ +static const uint32_t chDataBaseAddr = 0x110000; /**< Base address of channel data */ +static const uint32_t contactBaseAddr = 0x140000; /**< Base address of contacts */ +static const uint32_t maxNumChannels = 3000; /**< Maximum number of channels in memory */ +static const uint32_t maxNumZones = 250; /**< Maximum number of zones and zone extensions in memory */ +static const uint32_t maxNumContacts = 10000; /**< Maximum number of contacts in memory */ + + +/** + * Used to read channel data from SPI flash into a channel_t struct + */ +static int _readChannelAtAddress(channel_t *channel, uint32_t addr) +{ + W25Qx_wakeup(); + delayUs(5); + mduv3x0Channel_t chData; + W25Qx_readData(addr, ((uint8_t *) &chData), sizeof(mduv3x0Channel_t)); + W25Qx_sleep(); + + // Check if the channel is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) chData.name) == 0) return -1; + + channel->mode = chData.channel_mode; + channel->bandwidth = chData.bandwidth; + channel->rx_only = chData.rx_only; + channel->rx_frequency = bcd2bin(chData.rx_frequency) * 10; + channel->tx_frequency = bcd2bin(chData.tx_frequency) * 10; + channel->scanList_index = chData.scan_list_index; + channel->groupList_index = chData.group_list_index; + + if(chData.power == 3) + { + channel->power = 135; /* High power -> 5W = 37dBm */ + } + else if(chData.power == 2) + { + channel->power = 120; /* Mid power -> 2.5W = 34dBm */ + } + else + { + channel->power = 100; /* Low power -> 1W = 30dBm */ + } + + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + channel->name[i] = ((char) (chData.name[i] & 0x00FF)); + } + + /* Load mode-specific parameters */ + if(channel->mode == OPMODE_FM) + { + channel->fm.txToneEn = 0; + channel->fm.rxToneEn = 0; + uint16_t rx_css = chData.ctcss_dcs_receive; + uint16_t tx_css = chData.ctcss_dcs_transmit; + + // TODO: Implement binary search to speed up this lookup + if((rx_css != 0) && (rx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(rx_css))) + { + channel->fm.rxTone = i; + channel->fm.rxToneEn = 1; + break; + } + } + } + + if((tx_css != 0) && (tx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(tx_css))) + { + channel->fm.txTone = i; + channel->fm.txToneEn = 1; + break; + } + } + } + + // TODO: Implement warning screen if tone was not found + } + else if(channel->mode == OPMODE_DMR) + { + channel->dmr.contactName_index = chData.contact_name_index; + channel->dmr.dmr_timeslot = chData.repeater_slot; + channel->dmr.rxColorCode = chData.colorcode; + channel->dmr.txColorCode = chData.colorcode; + } + + return 0; +} + + + +/** + * This function does not apply to address-based codeplugs + */ +int cps_open(char *cps_name) +{ + (void) cps_name; + return 0; +} + +/** + * This function does not apply to address-based codeplugs + */ +void cps_close() +{ +} + +/** + * This function does not apply to address-based codeplugs + */ +int cps_create(char *cps_name) +{ + (void) cps_name; + return 0; +} + +int cps_readChannelData(channel_t *channel, uint16_t pos) +{ + if(pos >= maxNumChannels) return -1; + + memset(channel, 0x00, sizeof(channel_t)); + + uint32_t readAddr = chDataBaseAddr + pos * sizeof(mduv3x0Channel_t); + return _readChannelAtAddress(channel, readAddr); +} + +int cps_readBankHeader(bankHdr_t *b_header, uint16_t pos) +{ + if(pos >= maxNumZones) return -1; + + W25Qx_wakeup(); + delayUs(5); + + mduv3x0Zone_t zoneData; + mduv3x0ZoneExt_t zoneExtData; + uint32_t zoneAddr = zoneBaseAddr + pos * sizeof(mduv3x0Zone_t); + uint32_t zoneExtAddr = zoneExtBaseAddr + pos * sizeof(mduv3x0ZoneExt_t); + W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(mduv3x0Zone_t)); + W25Qx_readData(zoneExtAddr, ((uint8_t *) &zoneExtData), sizeof(mduv3x0ZoneExt_t)); + W25Qx_sleep(); + + // Check if zone is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + b_header->name[i] = ((char) (zoneData.name[i] & 0x00FF)); + } + return 0; +} + +int32_t cps_readBankData(uint16_t bank_pos, uint16_t ch_pos) +{ + if(bank_pos >= maxNumZones) return -1; + + W25Qx_wakeup(); + delayUs(5); + + mduv3x0Zone_t zoneData; + mduv3x0ZoneExt_t zoneExtData; + uint32_t zoneAddr = zoneBaseAddr + bank_pos * sizeof(mduv3x0Zone_t); + uint32_t zoneExtAddr = zoneExtBaseAddr + bank_pos * sizeof(mduv3x0ZoneExt_t); + W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(mduv3x0Zone_t)); + W25Qx_readData(zoneExtAddr, ((uint8_t *) &zoneExtData), sizeof(mduv3x0ZoneExt_t)); + W25Qx_sleep(); + + // Check if zone is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + // Channel index in zones are 1 based because an empty zone contains 0s + // OpenRTX CPS interface is 0-based so we decrease by one + if (ch_pos < 16) + return zoneData.member_a[ch_pos] - 1; + else + return zoneExtData.ext_a[ch_pos - 16] - 1; +} + +int cps_readContactData(contact_t *contact, uint16_t pos) +{ + if(pos >= maxNumContacts) return -1; + + W25Qx_wakeup(); + delayUs(5); + + mduv3x0Contact_t contactData; + uint32_t contactAddr = contactBaseAddr + pos * sizeof(mduv3x0Contact_t); + W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(mduv3x0Contact_t)); + W25Qx_sleep(); + + // Check if contact is empty + if(wcslen((wchar_t *) contactData.name) == 0) return -1; + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + contact->name[i] = ((char) (contactData.name[i] & 0x00FF)); + } + + contact->mode = OPMODE_DMR; + + // Copy contact DMR ID + contact->info.dmr.id = contactData.id[0] + | (contactData.id[1] << 8) + | (contactData.id[2] << 16); + + // Copy contact details + contact->info.dmr.contactType = contactData.type; + contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; + + return 0; +} diff --git a/platform/drivers/CPS/cps_io_native_MDUV3x0.c b/platform/drivers/CPS/cps_io_native_MDUV3x0.c new file mode 100644 index 00000000..f5514117 --- /dev/null +++ b/platform/drivers/CPS/cps_io_native_MDUV3x0.c @@ -0,0 +1,262 @@ +/*************************************************************************** + * Copyright (C) 2020 - 2022 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * 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; 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 +#include +#include +#include +#include "cps_data_MDUV3x0.h" +#include "W25Qx.h" + +static const uint32_t zoneBaseAddr = 0x149E0; /**< Base address of zones */ +static const uint32_t zoneExtBaseAddr = 0x31000; /**< Base address of zone extensions */ +static const uint32_t vfoChannelBaseAddr = 0x2EF00; /**< Base address of VFO channel */ +static const uint32_t chDataBaseAddr = 0x110000; /**< Base address of channel data */ +static const uint32_t contactBaseAddr = 0x140000; /**< Base address of contacts */ +static const uint32_t maxNumChannels = 3000; /**< Maximum number of channels in memory */ +static const uint32_t maxNumZones = 250; /**< Maximum number of zones and zone extensions in memory */ +static const uint32_t maxNumContacts = 10000; /**< Maximum number of contacts in memory */ + + +/** + * Used to read channel data from SPI flash into a channel_t struct + */ +static int _readChannelAtAddress(channel_t *channel, uint32_t addr) +{ + W25Qx_wakeup(); + delayUs(5); + mduv3x0Channel_t chData; + W25Qx_readData(addr, ((uint8_t *) &chData), sizeof(mduv3x0Channel_t)); + W25Qx_sleep(); + + // Check if the channel is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) chData.name) == 0) return -1; + + channel->mode = chData.channel_mode; + channel->bandwidth = chData.bandwidth; + channel->rx_only = chData.rx_only; + channel->rx_frequency = bcd2bin(chData.rx_frequency) * 10; + channel->tx_frequency = bcd2bin(chData.tx_frequency) * 10; + channel->scanList_index = chData.scan_list_index; + channel->groupList_index = chData.group_list_index; + + if(chData.power == 3) + { + channel->power = 135; /* High power -> 5W = 37dBm */ + } + else if(chData.power == 2) + { + channel->power = 120; /* Mid power -> 2.5W = 34dBm */ + } + else + { + channel->power = 100; /* Low power -> 1W = 30dBm */ + } + + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + channel->name[i] = ((char) (chData.name[i] & 0x00FF)); + } + + /* Load mode-specific parameters */ + if(channel->mode == OPMODE_FM) + { + channel->fm.txToneEn = 0; + channel->fm.rxToneEn = 0; + uint16_t rx_css = chData.ctcss_dcs_receive; + uint16_t tx_css = chData.ctcss_dcs_transmit; + + // TODO: Implement binary search to speed up this lookup + if((rx_css != 0) && (rx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(rx_css))) + { + channel->fm.rxTone = i; + channel->fm.rxToneEn = 1; + break; + } + } + } + + if((tx_css != 0) && (tx_css != 0xFFFF)) + { + for(int i = 0; i < MAX_TONE_INDEX; i++) + { + if(ctcss_tone[i] == ((uint16_t) bcd2bin(tx_css))) + { + channel->fm.txTone = i; + channel->fm.txToneEn = 1; + break; + } + } + } + + // TODO: Implement warning screen if tone was not found + } + else if(channel->mode == OPMODE_DMR) + { + channel->dmr.contactName_index = chData.contact_name_index; + channel->dmr.dmr_timeslot = chData.repeater_slot; + channel->dmr.rxColorCode = chData.colorcode; + channel->dmr.txColorCode = chData.colorcode; + } + + return 0; +} + + +/** + * This function does not apply to address-based codeplugs + */ +int cps_open(char *cps_name) +{ + (void) cps_name; + return 0; +} + +/** + * This function does not apply to address-based codeplugs + */ +void cps_close() +{ +} + +/** + * This function does not apply to address-based codeplugs + */ +int cps_create(char *cps_name) +{ + (void) cps_name; + return 0; +} + +int cps_readChannelData(channel_t *channel, uint16_t pos) +{ + if(pos >= maxNumChannels) return -1; + + memset(channel, 0x00, sizeof(channel_t)); + + // Note: pos is 1-based because an empty slot in a zone contains index 0 + uint32_t readAddr = chDataBaseAddr + pos * sizeof(mduv3x0Channel_t); + return _readChannelAtAddress(channel, readAddr); +} + +int cps_readBankHeader(bankHdr_t *b_header, uint16_t pos) +{ + if(pos >= maxNumZones) return -1; + + W25Qx_wakeup(); + delayUs(5); + + mduv3x0Zone_t zoneData; + mduv3x0ZoneExt_t zoneExtData; + // Note: pos is 1-based to be consistent with channels + uint32_t zoneAddr = zoneBaseAddr + pos * sizeof(mduv3x0Zone_t); + uint32_t zoneExtAddr = zoneExtBaseAddr + pos * sizeof(mduv3x0ZoneExt_t); + W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(mduv3x0Zone_t)); + W25Qx_readData(zoneExtAddr, ((uint8_t *) &zoneExtData), sizeof(mduv3x0ZoneExt_t)); + W25Qx_sleep(); + + // Check if zone is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + b_header->name[i] = ((char) (zoneData.name[i] & 0x00FF)); + } + b_header->ch_count = 64; + return 0; +} + +int32_t cps_readBankData(uint16_t bank_pos, uint16_t ch_pos) +{ + if(bank_pos >= maxNumZones) return -1; + + W25Qx_wakeup(); + delayUs(5); + + mduv3x0Zone_t zoneData; + mduv3x0ZoneExt_t zoneExtData; + uint32_t zoneAddr = zoneBaseAddr + bank_pos * sizeof(mduv3x0Zone_t); + uint32_t zoneExtAddr = zoneExtBaseAddr + bank_pos * sizeof(mduv3x0ZoneExt_t); + W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(mduv3x0Zone_t)); + W25Qx_readData(zoneExtAddr, ((uint8_t *) &zoneExtData), sizeof(mduv3x0ZoneExt_t)); + W25Qx_sleep(); + + // Check if zone is empty + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + if(wcslen((wchar_t *) zoneData.name) == 0) return -1; + // Channel index in zones are 1 based because an empty zone contains 0s + // OpenRTX CPS interface is 0-based so we decrease by one + if (ch_pos < 16) + return zoneData.member_a[ch_pos] - 1; + else + return zoneExtData.ext_a[ch_pos - 16] - 1; +} + +int cps_readContactData(contact_t *contact, uint16_t pos) +{ + if(pos >= maxNumContacts) return -1; + + W25Qx_wakeup(); + delayUs(5); + + mduv3x0Contact_t contactData; + // Note: pos is 1-based to be consistent with channels + uint32_t contactAddr = contactBaseAddr + pos * sizeof(mduv3x0Contact_t); + W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(mduv3x0Contact_t)); + W25Qx_sleep(); + + // Check if contact is empty + if(wcslen((wchar_t *) contactData.name) == 0) return -1; + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + contact->name[i] = ((char) (contactData.name[i] & 0x00FF)); + } + + contact->mode = OPMODE_DMR; + + // Copy contact DMR ID + contact->info.dmr.id = contactData.id[0] + | (contactData.id[1] << 8) + | (contactData.id[2] << 16); + + // Copy contact details + contact->info.dmr.contactType = contactData.type; + contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; + + return 0; +} diff --git a/platform/drivers/CPS/cps_io_native_Mod17.c b/platform/drivers/CPS/cps_io_native_Mod17.c new file mode 100644 index 00000000..0c6bbb9b --- /dev/null +++ b/platform/drivers/CPS/cps_io_native_Mod17.c @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2022 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * 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; 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 + + +/** + * This function does not apply to address-based codeplugs + */ +int cps_open(char *cps_name) +{ + (void) cps_name; + return 0; +} + +/** + * This function does not apply to address-based codeplugs + */ +void cps_close() +{ +} + +/** + * This function does not apply to address-based codeplugs + */ +int cps_create(char *cps_name) +{ + (void) cps_name; + return 0; +} + +int cps_readChannelData(channel_t *channel, uint16_t pos) +{ + (void) channel; + (void) pos; + return -1; +} + +int cps_readBankHeader(bankHdr_t *b_header, uint16_t pos) +{ + (void) b_header; + (void) pos; + return -1; +} + +int32_t cps_readBankData(uint16_t bank_pos, uint16_t ch_pos) +{ + (void) bank_pos; + (void) ch_pos; + return -1; +} + +int cps_readContactData(contact_t *contact, uint16_t pos) +{ + (void) contact; + (void) pos; + return -1; +} diff --git a/platform/drivers/NVM/nvmem_GDx.c b/platform/drivers/NVM/nvmem_GDx.c index ada04904..ca873066 100644 --- a/platform/drivers/NVM/nvmem_GDx.c +++ b/platform/drivers/NVM/nvmem_GDx.c @@ -22,11 +22,10 @@ #include #include #include -#include #include +#include #include "AT24Cx.h" #include "W25Qx.h" -#include "nvmData_GDx.h" #if defined(PLATFORM_GD77) static const uint32_t UHF_CAL_BASE = 0x8F000; @@ -38,32 +37,6 @@ static const uint32_t VHF_CAL_BASE = 0x6F070; #warning GDx calibration: platform not supported #endif -//const uint32_t zoneBaseAddr = 0x149e0; /**< Base address of zones */ -const uint32_t channelBaseAddrEEPROM = 0x03780; /**< Base address of channel data */ -const uint32_t channelBaseAddrFlash = 0x7b1c0; /**< Base address of channel data */ -const uint32_t vfoChannelBaseAddr = 0x7590; /**< Base address of VFO channel */ -const uint32_t zoneBaseAddr = 0x8010; /**< Base address of zones */ -const uint32_t contactBaseAddr = 0x87620; /**< Base address of contacts */ -const uint32_t maxNumChannels = 1024; /**< Maximum number of channels in memory */ -const uint32_t maxNumZones = 68; /**< Maximum number of zones in memory */ -const uint32_t maxNumContacts = 1024; /**< Maximum number of contacts in memory */ - -/** - * \internal Utility function to convert 4 byte BCD values into a 32-bit - * unsigned integer ones. - */ -uint32_t _bcd2bin(uint32_t bcd) -{ - return ((bcd >> 28) & 0x0F) * 10000000 + - ((bcd >> 24) & 0x0F) * 1000000 + - ((bcd >> 20) & 0x0F) * 100000 + - ((bcd >> 16) & 0x0F) * 10000 + - ((bcd >> 12) & 0x0F) * 1000 + - ((bcd >> 8) & 0x0F) * 100 + - ((bcd >> 4) & 0x0F) * 10 + - (bcd & 0x0F); -} - /** * \internal Utility function for loading band-specific calibration data into * the corresponding data structure. @@ -114,21 +87,6 @@ void _loadBandCalData(uint32_t baseAddr, bandCalData_t *cal) } } -// Strings in GD-77 codeplug are terminated with 0xFF, -// replace 0xFF terminator with 0x00 to be compatible with C strings - -void _addStringTerminator(char *buf, uint8_t max_len) -{ - for(int i=0; imode = chData.channel_mode + 1; - channel->bandwidth = chData.bandwidth; - channel->rx_only = chData.rx_only; - channel->power = ((chData.power == 1) ? 135 : 100); - channel->rx_frequency = _bcd2bin(chData.rx_frequency) * 10; - channel->tx_frequency = _bcd2bin(chData.tx_frequency) * 10; - channel->groupList_index = chData.group_list_index; - memcpy(channel->name, chData.name, sizeof(chData.name)); - // Terminate string with 0x00 instead of 0xFF - _addStringTerminator(channel->name, sizeof(chData.name)); - - /* Load mode-specific parameters */ - if(channel->mode == OPMODE_FM) - { - channel->fm.txToneEn = 0; - channel->fm.rxToneEn = 0; - uint16_t rx_css = chData.ctcss_dcs_receive; - uint16_t tx_css = chData.ctcss_dcs_transmit; - - // TODO: Implement binary search to speed up this lookup - if((rx_css != 0) && (rx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(rx_css))) - { - channel->fm.rxTone = i; - channel->fm.rxToneEn = 1; - break; - } - } - } - - if((tx_css != 0) && (tx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(tx_css))) - { - channel->fm.txTone = i; - channel->fm.txToneEn = 1; - break; - } - } - } - - // TODO: Implement warning screen if tone was not found - } - else if(channel->mode == OPMODE_DMR) - { - channel->dmr.contactName_index = chData.contact_name_index; - channel->dmr.dmr_timeslot = chData.repeater_slot; - channel->dmr.rxColorCode = chData.colorcode_rx; - channel->dmr.txColorCode = chData.colorcode_tx; - } - return 0; -} - -int cps_readChannelData(channel_t *channel, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumChannels)) - return -1; - - memset(channel, 0x00, sizeof(channel_t)); - - // Channels are organized in 128-channel banks - uint8_t bank_num = (pos - 1) / 128; - // Note: pos is 1-based because an empty slot in a bank contains index 0 - uint8_t bank_channel = (pos - 1) % 128; - - // ### Read channel bank bitmap ### - uint8_t bitmap[16]; - // First channel bank (128 channels) is saved in EEPROM - if(pos <= 128) - { - uint32_t readAddr = channelBaseAddrEEPROM + bank_num * sizeof(gdxChannelBank_t); - AT24Cx_readData(readAddr, ((uint8_t *) &bitmap), sizeof(bitmap)); - } - // Remaining 7 channel banks (896 channels) are saved in SPI Flash - else - { - W25Qx_wakeup(); - delayUs(5); - uint32_t readAddr = channelBaseAddrFlash + (bank_num - 1) * sizeof(gdxChannelBank_t); - W25Qx_readData(readAddr, ((uint8_t *) &bitmap), sizeof(bitmap)); - W25Qx_sleep(); - } - uint8_t bitmap_byte = bank_channel / 8; - uint8_t bitmap_bit = bank_channel % 8; - gdxChannel_t chData; - // The channel is marked not valid in the bitmap - if(!(bitmap[bitmap_byte] & (1 << bitmap_bit))) - return -1; - // The channel is marked valid in the bitmap - // ### Read desired channel from the correct bank ### - else - { - uint32_t channelOffset = sizeof(bitmap) + (pos - 1) * sizeof(gdxChannel_t); - // First channel bank (128 channels) is saved in EEPROM - if(pos <= 128) - { - uint32_t bankAddr = channelBaseAddrEEPROM + bank_num * sizeof(gdxChannelBank_t); - AT24Cx_readData(bankAddr + channelOffset, ((uint8_t *) &chData), sizeof(gdxChannel_t)); - } - // Remaining 7 channel banks (896 channels) are saved in SPI Flash - else - { - W25Qx_wakeup(); - delayUs(5); - uint32_t bankAddr = channelBaseAddrFlash + bank_num * sizeof(gdxChannelBank_t); - W25Qx_readData(bankAddr + channelOffset, ((uint8_t *) &chData), sizeof(gdxChannel_t)); - W25Qx_sleep(); - } - } - // Copy data to OpenRTX channel_t - channel->mode = chData.channel_mode + 1; - channel->bandwidth = chData.bandwidth; - channel->rx_only = chData.rx_only; - channel->power = ((chData.power == 1) ? 135 : 100); - channel->rx_frequency = _bcd2bin(chData.rx_frequency) * 10; - channel->tx_frequency = _bcd2bin(chData.tx_frequency) * 10; - channel->scanList_index = chData.scan_list_index; - channel->groupList_index = chData.group_list_index; - memcpy(channel->name, chData.name, sizeof(chData.name)); - // Terminate string with 0x00 instead of 0xFF - _addStringTerminator(channel->name, sizeof(chData.name)); - - /* Load mode-specific parameters */ - if(channel->mode == OPMODE_FM) - { - channel->fm.txToneEn = 0; - channel->fm.rxToneEn = 0; - uint16_t rx_css = chData.ctcss_dcs_receive; - uint16_t tx_css = chData.ctcss_dcs_transmit; - - // TODO: Implement binary search to speed up this lookup - if((rx_css != 0) && (rx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(rx_css))) - { - channel->fm.rxTone = i; - channel->fm.rxToneEn = 1; - break; - } - } - } - - if((tx_css != 0) && (tx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(tx_css))) - { - channel->fm.txTone = i; - channel->fm.txToneEn = 1; - break; - } - } - } - - // TODO: Implement warning screen if tone was not found - } - else if(channel->mode == OPMODE_DMR) - { - channel->dmr.contactName_index = chData.contact_name_index; - channel->dmr.dmr_timeslot = chData.repeater_slot; - channel->dmr.rxColorCode = chData.colorcode_rx; - channel->dmr.txColorCode = chData.colorcode_tx; - } - return 0; -} - -int cps_readBankData(bank_t* bank, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumZones)) return -1; - - // bank number is 1-based to be consistent with channels - // Convert to 0-based index to fetch data from flash - uint16_t index = pos - 1; - // ### Read bank bank bitmap ### - uint8_t bitmap[32]; - AT24Cx_readData(zoneBaseAddr, ((uint8_t *) &bitmap), sizeof(bitmap)); - - uint8_t bitmap_byte = index / 8; - uint8_t bitmap_bit = index % 8; - // The bank is marked not valid in the bitmap - if(!(bitmap[bitmap_byte] & (1 << bitmap_bit))) return -1; - - gdxZone_t zoneData; - uint32_t zoneAddr = zoneBaseAddr + sizeof(bitmap) + index * sizeof(gdxZone_t); - AT24Cx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(gdxZone_t)); - - // Check if bank is empty - if(wcslen((wchar_t *) zoneData.name) == 0) return -1; - - memcpy(bank->name, zoneData.name, sizeof(zoneData.name)); - // Terminate string with 0x00 instead of 0xFF - _addStringTerminator(bank->name, sizeof(zoneData.name)); - // Copy bank channel indexes - for(uint16_t i = 0; i < 16; i++) - { - bank->member[i] = zoneData.member[i]; - } - return 0; -} - -int cps_readContactData(contact_t *contact, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumContacts)) return -1; - - W25Qx_wakeup(); - delayUs(5); - - gdxContact_t contactData; - // Note: pos is 1-based to be consistent with channels - uint32_t contactAddr = contactBaseAddr + (pos - 1) * sizeof(gdxContact_t); - W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(gdxContact_t)); - W25Qx_sleep(); - - // Check if contact is empty - if(wcslen((wchar_t *) contactData.name) == 0) return -1; - - // Copy contact name - memcpy(contact->name, contactData.name, sizeof(contactData.name)); - // Terminate string with 0x00 instead of 0xFF - _addStringTerminator(contact->name, sizeof(contactData.name)); - - contact->mode = DMR; - - // Copy contact DMR ID - contact->info.dmr.id = contactData.id[0] - | (contactData.id[1] << 8) - | (contactData.id[2] << 16); - - // Copy contact details - contact->info.dmr.contactType = contactData.type; - contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; - - return 0; + (void) channel; + return -1; } int nvm_readSettings(settings_t *settings) diff --git a/platform/drivers/NVM/nvmem_MD3x0.c b/platform/drivers/NVM/nvmem_MD3x0.c index 36e9a729..604b40fd 100644 --- a/platform/drivers/NVM/nvmem_MD3x0.c +++ b/platform/drivers/NVM/nvmem_MD3x0.c @@ -23,32 +23,9 @@ #include #include #include -#include "nvmData_MD3x0.h" +#include #include "W25Qx.h" -const uint32_t zoneBaseAddr = 0x149e0; /**< Base address of zones */ -const uint32_t chDataBaseAddr = 0x1ee00; /**< Base address of channel data */ -const uint32_t contactBaseAddr = 0x05f80; /**< Base address of contacts */ -const uint32_t maxNumChannels = 1000; /**< Maximum number of channels in memory */ -const uint32_t maxNumZones = 250; /**< Maximum number of zones in memory */ -const uint32_t maxNumContacts = 10000; /**< Maximum number of contacts in memory */ - -/** - * \internal Utility function to convert 4 byte BCD values into a 32-bit - * unsigned integer ones. - */ -uint32_t _bcd2bin(uint32_t bcd) -{ - return ((bcd >> 28) & 0x0F) * 10000000 + - ((bcd >> 24) & 0x0F) * 1000000 + - ((bcd >> 20) & 0x0F) * 100000 + - ((bcd >> 16) & 0x0F) * 10000 + - ((bcd >> 12) & 0x0F) * 1000 + - ((bcd >> 8) & 0x0F) * 100 + - ((bcd >> 4) & 0x0F) * 10 + - (bcd & 0x0F); -} - void nvm_init() { W25Qx_init(); @@ -104,8 +81,8 @@ void nvm_readCalibData(void *buf) */ for(uint8_t i = 0; i < 9; i++) { - calib->rxFreq[i] = ((freq_t) _bcd2bin(freqs[2*i])) * 10; - calib->txFreq[i] = ((freq_t) _bcd2bin(freqs[2*i+1])) * 10; + calib->rxFreq[i] = ((freq_t) bcd2bin(freqs[2*i])) * 10; + calib->txFreq[i] = ((freq_t) bcd2bin(freqs[2*i+1])) * 10; } } @@ -135,8 +112,8 @@ void nvm_loadHwInfo(hwInfo_t *info) } /* These devices are single-band only, either VHF or UHF. */ - freqMin = ((uint16_t) _bcd2bin(freqMin))/10; - freqMax = ((uint16_t) _bcd2bin(freqMax))/10; + freqMin = ((uint16_t) bcd2bin(freqMin))/10; + freqMax = ((uint16_t) bcd2bin(freqMax))/10; if(freqMin < 200) { @@ -168,159 +145,6 @@ int nvm_readVFOChannelData(channel_t *channel) } */ -int cps_readChannelData(channel_t *channel, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumChannels)) return -1; - - memset(channel, 0x00, sizeof(channel_t)); - - W25Qx_wakeup(); - delayUs(5); - - md3x0Channel_t chData; - // Note: pos is 1-based because an empty slot in a zone contains index 0 - uint32_t readAddr = chDataBaseAddr + (pos - 1) * sizeof(md3x0Channel_t); - W25Qx_readData(readAddr, ((uint8_t *) &chData), sizeof(md3x0Channel_t)); - W25Qx_sleep(); - - channel->mode = chData.channel_mode; - channel->bandwidth = chData.bandwidth; - channel->rx_only = chData.rx_only; - channel->power = ((chData.power == 1) ? 135 : 100); - channel->rx_frequency = _bcd2bin(chData.rx_frequency) * 10; - channel->tx_frequency = _bcd2bin(chData.tx_frequency) * 10; - channel->scanList_index = chData.scan_list_index; - channel->groupList_index = chData.group_list_index; - - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - channel->name[i] = ((char) (chData.name[i] & 0x00FF)); - } - - /* Load mode-specific parameters */ - if(channel->mode == OPMODE_FM) - { - channel->fm.txToneEn = 0; - channel->fm.rxToneEn = 0; - uint16_t rx_css = chData.ctcss_dcs_receive; - uint16_t tx_css = chData.ctcss_dcs_transmit; - - // TODO: Implement binary search to speed up this lookup - if((rx_css != 0) && (rx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(rx_css))) - { - channel->fm.rxTone = i; - channel->fm.rxToneEn = 1; - break; - } - } - } - - if((tx_css != 0) && (tx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(tx_css))) - { - channel->fm.txTone = i; - channel->fm.txToneEn = 1; - break; - } - } - } - - // TODO: Implement warning screen if tone was not found - } - else if(channel->mode == OPMODE_DMR) - { - channel->dmr.contactName_index = chData.contact_name_index; - channel->dmr.dmr_timeslot = chData.repeater_slot; - channel->dmr.rxColorCode = chData.colorcode; - channel->dmr.txColorCode = chData.colorcode; - } - - return 0; -} - -int cps_readBankData(bank_t* bank, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumZones)) return -1; - - W25Qx_wakeup(); - delayUs(5); - - md3x0Zone_t zoneData; - // Note: pos is 1-based to be consistent with channels - uint32_t zoneAddr = zoneBaseAddr + (pos - 1) * sizeof(md3x0Zone_t); - W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(md3x0Zone_t)); - W25Qx_sleep(); - - // Check if zone is empty - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - if(wcslen((wchar_t *) zoneData.name) == 0) return -1; - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - bank->name[i] = ((char) (zoneData.name[i] & 0x00FF)); - } - // Copy zone channel indexes - for(uint16_t i = 0; i < 16; i++) - { - bank->member[i] = zoneData.member[i]; - } - - return 0; -} - -int cps_readContactData(contact_t *contact, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumContacts)) return -1; - - W25Qx_wakeup(); - delayUs(5); - - md3x0Contact_t contactData; - // Note: pos is 1-based to be consistent with channels - uint32_t contactAddr = contactBaseAddr + (pos - 1) * sizeof(md3x0Contact_t); - W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(md3x0Contact_t)); - W25Qx_sleep(); - - // Check if contact is empty - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - if(wcslen((wchar_t *) contactData.name) == 0) return -1; - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - contact->name[i] = ((char) (contactData.name[i] & 0x00FF)); - } - - contact->mode = DMR; - - // Copy contact DMR ID - contact->info.dmr.id = contactData.id[0] - | (contactData.id[1] << 8) - | (contactData.id[2] << 16); - - // Copy contact details - contact->info.dmr.contactType = contactData.type; - contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; - - return 0; -} - /* TODO: temporarily implemented in "nvmem_settings_MDx.c" diff --git a/platform/drivers/NVM/nvmem_MD9600.c b/platform/drivers/NVM/nvmem_MD9600.c index 6b35af0e..41682f10 100644 --- a/platform/drivers/NVM/nvmem_MD9600.c +++ b/platform/drivers/NVM/nvmem_MD9600.c @@ -23,130 +23,9 @@ #include #include #include -#include "nvmData_MDUV3x0.h" +#include #include "W25Qx.h" -const uint32_t zoneBaseAddr = 0x149E0; /**< Base address of zones */ -const uint32_t zoneExtBaseAddr = 0x31000; /**< Base address of zone extensions */ -const uint32_t vfoChannelBaseAddr = 0x2EF00; /**< Base address of VFO channel */ -const uint32_t chDataBaseAddr = 0x110000; /**< Base address of channel data */ -const uint32_t contactBaseAddr = 0x140000; /**< Base address of contacts */ -const uint32_t maxNumChannels = 3000; /**< Maximum number of channels in memory */ -const uint32_t maxNumZones = 250; /**< Maximum number of zones and zone extensions in memory */ -const uint32_t maxNumContacts = 10000; /**< Maximum number of contacts in memory */ -/* This address has been chosen by OpenRTX to store the settings - * because it is empty (0xFF) and has enough free space */ -const uint32_t settingsAddr = 0x6000; - -/** - * \internal Utility function to convert 4 byte BCD values into a 32-bit - * unsigned integer ones. - */ -uint32_t _bcd2bin(uint32_t bcd) -{ - return ((bcd >> 28) & 0x0F) * 10000000 + - ((bcd >> 24) & 0x0F) * 1000000 + - ((bcd >> 20) & 0x0F) * 100000 + - ((bcd >> 16) & 0x0F) * 10000 + - ((bcd >> 12) & 0x0F) * 1000 + - ((bcd >> 8) & 0x0F) * 100 + - ((bcd >> 4) & 0x0F) * 10 + - (bcd & 0x0F); -} - -/** - * Used to read channel data from SPI flash into a channel_t struct - */ -int _cps_readChannelAtAddress(channel_t *channel, uint32_t addr) -{ - W25Qx_wakeup(); - delayUs(5); - mduv3x0Channel_t chData; - W25Qx_readData(addr, ((uint8_t *) &chData), sizeof(mduv3x0Channel_t)); - W25Qx_sleep(); - - // Check if the channel is empty - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - if(wcslen((wchar_t *) chData.name) == 0) return -1; - - channel->mode = chData.channel_mode; - channel->bandwidth = chData.bandwidth; - channel->rx_only = chData.rx_only; - channel->rx_frequency = _bcd2bin(chData.rx_frequency) * 10; - channel->tx_frequency = _bcd2bin(chData.tx_frequency) * 10; - channel->scanList_index = chData.scan_list_index; - channel->groupList_index = chData.group_list_index; - - if(chData.power == 3) - { - channel->power = 135; /* High power -> 5W = 37dBm */ - } - else if(chData.power == 2) - { - channel->power = 120; /* Mid power -> 2.5W = 34dBm */ - } - else - { - channel->power = 100; /* Low power -> 1W = 30dBm */ - } - - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - channel->name[i] = ((char) (chData.name[i] & 0x00FF)); - } - - /* Load mode-specific parameters */ - if(channel->mode == OPMODE_FM) - { - channel->fm.txToneEn = 0; - channel->fm.rxToneEn = 0; - uint16_t rx_css = chData.ctcss_dcs_receive; - uint16_t tx_css = chData.ctcss_dcs_transmit; - - // TODO: Implement binary search to speed up this lookup - if((rx_css != 0) && (rx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(rx_css))) - { - channel->fm.rxTone = i; - channel->fm.rxToneEn = 1; - break; - } - } - } - - if((tx_css != 0) && (tx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(tx_css))) - { - channel->fm.txTone = i; - channel->fm.txToneEn = 1; - break; - } - } - } - - // TODO: Implement warning screen if tone was not found - } - else if(channel->mode == OPMODE_DMR) - { - channel->dmr.contactName_index = chData.contact_name_index; - channel->dmr.dmr_timeslot = chData.repeater_slot; - channel->dmr.rxColorCode = chData.colorcode; - channel->dmr.txColorCode = chData.colorcode; - } - - return 0; -} - void nvm_init() { W25Qx_init(); @@ -171,96 +50,6 @@ int nvm_readVFOChannelData(channel_t *channel) } */ -int cps_readChannelData(channel_t *channel, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumChannels)) return -1; - - memset(channel, 0x00, sizeof(channel_t)); - - // Note: pos is 1-based because an empty slot in a zone contains index 0 - uint32_t readAddr = chDataBaseAddr + (pos - 1) * sizeof(mduv3x0Channel_t); - return _cps_readChannelAtAddress(channel, readAddr); -} - -int cps_readBankData(bank_t* bank, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumZones)) return -1; - - W25Qx_wakeup(); - delayUs(5); - - mduv3x0Zone_t zoneData; - mduv3x0ZoneExt_t zoneExtData; - // Note: pos is 1-based to be consistent with channels - uint32_t zoneAddr = zoneBaseAddr + (pos - 1) * sizeof(mduv3x0Zone_t); - uint32_t zoneExtAddr = zoneExtBaseAddr + (pos - 1) * sizeof(mduv3x0ZoneExt_t); - W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(mduv3x0Zone_t)); - W25Qx_readData(zoneExtAddr, ((uint8_t *) &zoneExtData), sizeof(mduv3x0ZoneExt_t)); - W25Qx_sleep(); - - // Check if zone is empty - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - if(wcslen((wchar_t *) zoneData.name) == 0) return -1; - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - bank->name[i] = ((char) (zoneData.name[i] & 0x00FF)); - } - // Copy zone channel indexes - for(uint16_t i = 0; i < 16; i++) - { - bank->member[i] = zoneData.member_a[i]; - } - // Copy zone extension channel indexes - for(uint16_t i = 0; i < 48; i++) - { - bank->member[16 + i] = zoneExtData.ext_a[i]; - } - - return 0; -} - -int cps_readContactData(contact_t *contact, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumContacts)) return -1; - - W25Qx_wakeup(); - delayUs(5); - - mduv3x0Contact_t contactData; - // Note: pos is 1-based to be consistent with channels - uint32_t contactAddr = contactBaseAddr + (pos - 1) * sizeof(mduv3x0Contact_t); - W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(mduv3x0Contact_t)); - W25Qx_sleep(); - - // Check if contact is empty - if(wcslen((wchar_t *) contactData.name) == 0) return -1; - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - contact->name[i] = ((char) (contactData.name[i] & 0x00FF)); - } - - contact->mode = DMR; - - // Copy contact DMR ID - contact->info.dmr.id = contactData.id[0] - | (contactData.id[1] << 8) - | (contactData.id[2] << 16); - - // Copy contact details - contact->info.dmr.contactType = contactData.type; - contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; - - return 0; -} - /* TODO: temporarily implemented in "nvmem_settings_MDx.c" @@ -283,10 +72,4 @@ int nvm_writeSettings(const settings_t *settings) { // Disable settings write until DFU is implemented for flash backups return -1; - - W25Qx_wakeup(); - delayUs(5); - bool success = W25Qx_writeData(settingsAddr, ((uint8_t *) &settings), sizeof(settings_t)); - W25Qx_sleep(); - return success? 0 : -1; } diff --git a/platform/drivers/NVM/nvmem_MDUV3x0.c b/platform/drivers/NVM/nvmem_MDUV3x0.c index 029257bc..d80548ff 100644 --- a/platform/drivers/NVM/nvmem_MDUV3x0.c +++ b/platform/drivers/NVM/nvmem_MDUV3x0.c @@ -23,129 +23,9 @@ #include #include #include -#include "nvmData_MDUV3x0.h" +#include #include "W25Qx.h" -const uint32_t zoneBaseAddr = 0x149E0; /**< Base address of zones */ -const uint32_t zoneExtBaseAddr = 0x31000; /**< Base address of zone extensions */ -const uint32_t vfoChannelBaseAddr = 0x2EF00; /**< Base address of VFO channel */ -const uint32_t chDataBaseAddr = 0x110000; /**< Base address of channel data */ -const uint32_t contactBaseAddr = 0x140000; /**< Base address of contacts */ -const uint32_t maxNumChannels = 3000; /**< Maximum number of channels in memory */ -const uint32_t maxNumZones = 250; /**< Maximum number of zones and zone extensions in memory */ -const uint32_t maxNumContacts = 10000; /**< Maximum number of contacts in memory */ -/* This address has been chosen by OpenRTX to store the settings - * because it is empty (0xFF) and has enough free space */ -const uint32_t settingsAddr = 0x6000; - -/** - * \internal Utility function to convert 4 byte BCD values into a 32-bit - * unsigned integer ones. - */ -static uint32_t _bcd2bin(uint32_t bcd) -{ - return ((bcd >> 28) & 0x0F) * 10000000 + - ((bcd >> 24) & 0x0F) * 1000000 + - ((bcd >> 20) & 0x0F) * 100000 + - ((bcd >> 16) & 0x0F) * 10000 + - ((bcd >> 12) & 0x0F) * 1000 + - ((bcd >> 8) & 0x0F) * 100 + - ((bcd >> 4) & 0x0F) * 10 + - (bcd & 0x0F); -} - -/** - * Used to read channel data from SPI flash into a channel_t struct - */ -static int _cps_readChannelAtAddress(channel_t *channel, uint32_t addr) -{ - W25Qx_wakeup(); - delayUs(5); - mduv3x0Channel_t chData; - W25Qx_readData(addr, ((uint8_t *) &chData), sizeof(mduv3x0Channel_t)); - W25Qx_sleep(); - - // Check if the channel is empty - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - if(wcslen((wchar_t *) chData.name) == 0) return -1; - - channel->mode = chData.channel_mode; - channel->bandwidth = chData.bandwidth; - channel->rx_only = chData.rx_only; - channel->rx_frequency = _bcd2bin(chData.rx_frequency) * 10; - channel->tx_frequency = _bcd2bin(chData.tx_frequency) * 10; - channel->scanList_index = chData.scan_list_index; - channel->groupList_index = chData.group_list_index; - - if(chData.power == 3) - { - channel->power = 135; /* High power -> 5W = 37dBm */ - } - else if(chData.power == 2) - { - channel->power = 120; /* Mid power -> 2.5W = 34dBm */ - } - else - { - channel->power = 100; /* Low power -> 1W = 30dBm */ - } - - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - channel->name[i] = ((char) (chData.name[i] & 0x00FF)); - } - - /* Load mode-specific parameters */ - if(channel->mode == OPMODE_FM) - { - channel->fm.txToneEn = 0; - channel->fm.rxToneEn = 0; - uint16_t rx_css = chData.ctcss_dcs_receive; - uint16_t tx_css = chData.ctcss_dcs_transmit; - - // TODO: Implement binary search to speed up this lookup - if((rx_css != 0) && (rx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(rx_css))) - { - channel->fm.rxTone = i; - channel->fm.rxToneEn = 1; - break; - } - } - } - - if((tx_css != 0) && (tx_css != 0xFFFF)) - { - for(int i = 0; i < MAX_TONE_INDEX; i++) - { - if(ctcss_tone[i] == ((uint16_t) _bcd2bin(tx_css))) - { - channel->fm.txTone = i; - channel->fm.txToneEn = 1; - break; - } - } - } - - // TODO: Implement warning screen if tone was not found - } - else if(channel->mode == OPMODE_DMR) - { - channel->dmr.contactName_index = chData.contact_name_index; - channel->dmr.dmr_timeslot = chData.repeater_slot; - channel->dmr.rxColorCode = chData.colorcode; - channel->dmr.txColorCode = chData.colorcode; - } - - return 0; -} void nvm_init() { @@ -191,8 +71,8 @@ void nvm_readCalibData(void *buf) for(uint8_t i = 0; i < 9; i++) { - calib->uhfCal.rxFreq[i] = ((freq_t) _bcd2bin(freqs[2*i])); - calib->uhfCal.txFreq[i] = ((freq_t) _bcd2bin(freqs[2*i+1])); + calib->uhfCal.rxFreq[i] = ((freq_t) bcd2bin(freqs[2*i])); + calib->uhfCal.txFreq[i] = ((freq_t) bcd2bin(freqs[2*i+1])); } /* VHF-band calibration data */ @@ -219,8 +99,8 @@ void nvm_readCalibData(void *buf) for(uint8_t i = 0; i < 5; i++) { - calib->vhfCal.rxFreq[i] = ((freq_t) _bcd2bin(freqs[2*i])); - calib->vhfCal.txFreq[i] = ((freq_t) _bcd2bin(freqs[2*i+1])); + calib->vhfCal.rxFreq[i] = ((freq_t) bcd2bin(freqs[2*i])); + calib->vhfCal.txFreq[i] = ((freq_t) bcd2bin(freqs[2*i+1])); } } @@ -253,10 +133,10 @@ void nvm_loadHwInfo(hwInfo_t *info) if(info->name[i] == 0xFF) info->name[i] = '\0'; } - info->vhf_minFreq = ((uint16_t) _bcd2bin(vhf_freqMin))/10; - info->vhf_maxFreq = ((uint16_t) _bcd2bin(vhf_freqMax))/10; - info->uhf_minFreq = ((uint16_t) _bcd2bin(uhf_freqMin))/10; - info->uhf_maxFreq = ((uint16_t) _bcd2bin(uhf_freqMax))/10; + info->vhf_minFreq = ((uint16_t) bcd2bin(vhf_freqMin))/10; + info->vhf_maxFreq = ((uint16_t) bcd2bin(vhf_freqMax))/10; + info->uhf_minFreq = ((uint16_t) bcd2bin(uhf_freqMin))/10; + info->uhf_maxFreq = ((uint16_t) bcd2bin(uhf_freqMax))/10; info->vhf_band = 1; info->uhf_band = 1; info->lcd_type = lcdInfo & 0x03; @@ -271,96 +151,6 @@ int nvm_readVFOChannelData(channel_t *channel) } */ -int cps_readChannelData(channel_t *channel, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumChannels)) return -1; - - memset(channel, 0x00, sizeof(channel_t)); - - // Note: pos is 1-based because an empty slot in a zone contains index 0 - uint32_t readAddr = chDataBaseAddr + (pos - 1) * sizeof(mduv3x0Channel_t); - return _cps_readChannelAtAddress(channel, readAddr); -} - -int cps_readBankData(bank_t* bank, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumZones)) return -1; - - W25Qx_wakeup(); - delayUs(5); - - mduv3x0Zone_t zoneData; - mduv3x0ZoneExt_t zoneExtData; - // Note: pos is 1-based to be consistent with channels - uint32_t zoneAddr = zoneBaseAddr + (pos - 1) * sizeof(mduv3x0Zone_t); - uint32_t zoneExtAddr = zoneExtBaseAddr + (pos - 1) * sizeof(mduv3x0ZoneExt_t); - W25Qx_readData(zoneAddr, ((uint8_t *) &zoneData), sizeof(mduv3x0Zone_t)); - W25Qx_readData(zoneExtAddr, ((uint8_t *) &zoneExtData), sizeof(mduv3x0ZoneExt_t)); - W25Qx_sleep(); - - // Check if zone is empty - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - if(wcslen((wchar_t *) zoneData.name) == 0) return -1; - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - bank->name[i] = ((char) (zoneData.name[i] & 0x00FF)); - } - // Copy zone channel indexes - for(uint16_t i = 0; i < 16; i++) - { - bank->member[i] = zoneData.member_a[i]; - } - // Copy zone extension channel indexes - for(uint16_t i = 0; i < 48; i++) - { - bank->member[16 + i] = zoneExtData.ext_a[i]; - } - - return 0; -} - -int cps_readContactData(contact_t *contact, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumContacts)) return -1; - - W25Qx_wakeup(); - delayUs(5); - - mduv3x0Contact_t contactData; - // Note: pos is 1-based to be consistent with channels - uint32_t contactAddr = contactBaseAddr + (pos - 1) * sizeof(mduv3x0Contact_t); - W25Qx_readData(contactAddr, ((uint8_t *) &contactData), sizeof(mduv3x0Contact_t)); - W25Qx_sleep(); - - // Check if contact is empty - if(wcslen((wchar_t *) contactData.name) == 0) return -1; - /* - * Brutally convert channel name from unicode to char by truncating the most - * significant byte - */ - for(uint16_t i = 0; i < 16; i++) - { - contact->name[i] = ((char) (contactData.name[i] & 0x00FF)); - } - - contact->mode = DMR; - - // Copy contact DMR ID - contact->info.dmr.id = contactData.id[0] - | (contactData.id[1] << 8) - | (contactData.id[2] << 16); - - // Copy contact details - contact->info.dmr.contactType = contactData.type; - contact->info.dmr.rx_tone = contactData.receive_tone ? true : false; - - return 0; -} - /* TODO: temporarily implemented in "nvmem_settings_MDx.c" @@ -382,10 +172,4 @@ int nvm_writeSettings(const settings_t *settings) { // Disable settings write until DFU is implemented for flash backups return -1; - - W25Qx_wakeup(); - delayUs(5); - bool success = W25Qx_writeData(settingsAddr, ((uint8_t *) &settings), sizeof(settings_t)); - W25Qx_sleep(); - return success? 0 : -1; } diff --git a/platform/drivers/NVM/nvmem_Mod17.c b/platform/drivers/NVM/nvmem_Mod17.c index cac12aa3..65495558 100644 --- a/platform/drivers/NVM/nvmem_Mod17.c +++ b/platform/drivers/NVM/nvmem_Mod17.c @@ -56,27 +56,6 @@ int nvm_readVFOChannelData(channel_t *channel) return 0; } -int cps_readChannelData(channel_t *channel, uint16_t pos) -{ - (void) channel; - (void) pos; - return -1; -} - -int cps_readBankData(bank_t* bank, uint16_t pos) -{ - (void) bank; - (void) pos; - return -1; -} - -int cps_readContactData(contact_t *contact, uint16_t pos) -{ - (void) contact; - (void) pos; - return -1; -} - int nvm_readSettings(settings_t *settings) { (void) settings; diff --git a/platform/drivers/NVM/nvmem_linux.c b/platform/drivers/NVM/nvmem_linux.c index 49341914..c9bad75c 100644 --- a/platform/drivers/NVM/nvmem_linux.c +++ b/platform/drivers/NVM/nvmem_linux.c @@ -234,43 +234,6 @@ int nvm_readVFOChannelData(channel_t *channel) return _cps_read(memory_paths[P_VFO], channel, sizeof(channel_t)); } -int cps_readChannelData(channel_t *channel, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumChannels)) return -1; - - /* Generate dummy channel name */ - snprintf(channel->name, 16, "Channel %d", pos); - /* Generate dummy frequency values */ - channel->rx_frequency = dummy_base_freq + pos * 100000; - channel->tx_frequency = dummy_base_freq + pos * 100000; - - return 0; -} - -int cps_readBankData(bank_t* bank, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumZones)) return -1; - - /* Generate dummy bank name */ - snprintf(bank->name, 16, "Zone %d", pos); - memset(bank->member, 0, sizeof(bank->member)); - // Add fake bank member indexes - bank->member[0] = pos; - bank->member[1] = pos+1; - bank->member[2] = pos+2; - return 0; -} - -int cps_readContactData(contact_t *contact, uint16_t pos) -{ - if((pos <= 0) || (pos > maxNumContacts)) return -1; - - /* Generate dummy contact name */ - snprintf(contact->name, 16, "Contact %d", pos); - - return 0; -} - int nvm_readSettings(settings_t *settings) { return _cps_read(memory_paths[P_SETTINGS], settings, sizeof(settings_t)); diff --git a/tests/unit/cps.c b/tests/unit/cps.c new file mode 100644 index 00000000..4aa8af80 --- /dev/null +++ b/tests/unit/cps.c @@ -0,0 +1,19 @@ +#include + +int main() { + // Initialize a new cps + int err = cps_create("test.rtxc"); + if (err) { + printf("Error in codeplug initialization!\n"); + return -1; + } + // Write data + // Close it + // Re-open it + err = cps_open("test.rtxc"); + if (err) { + printf("Error in codeplug reading!\n"); + return -1; + } + // Read data and compare +}