FreeTRX/MDUV380_firmware/application/source/usb/usb_com.c
2026-07-06 08:45:33 +02:00

1077 lines
32 KiB
C

/*
* Copyright (C) 2019 Kai Ludwig, DG4KLU
* Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
* Daniel Caujolle-Bert, F1RMB
*
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdarg.h>
#include "functions/hotspot.h"
#include "functions/settings.h"
#include "user_interface/uiUtilities.h"
#include "user_interface/menuSystem.h"
#include "usb/usb_com.h"
#include "functions/ticks.h"
#include "interfaces/wdog.h"
#include "hardware/HR-C6000.h"
#include "functions/sound.h"
#include "hardware/SPI_Flash.h"
#include "user_interface/uiLocalisation.h"
#include "functions/rxPowerSaving.h"
#include "main.h"
#include <interfaces/clockManager.h>
#include "interfaces/settingsStorage.h"
#include "interfaces/gps.h"
//#define DEBUG_POSITION 1
#define GITVERSIONREV GITVERSION
enum CPS_ACCESS_AREA
{
CPS_ACCESS_FLASH = 1,
CPS_ACCESS_EEPROM = 2,
CPS_ACCESS_MCU_ROM = 5,
CPS_ACCESS_DISPLAY_BUFFER = 6,
CPS_ACCESS_WAV_BUFFER = 7,
CPS_COMPRESS_AND_ACCESS_AMBE_BUFFER = 8,
CPS_ACCESS_RADIO_INFO = 9,
#if ! defined(CPU_MK22FN512VLL12)
CPS_ACCESS_FLASH_SECURITY_REGISTERS = 10,
#endif
};
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801) || defined(PLATFORM_DM1801A) || defined(PLATFORM_RD5R)
#define TASK_LOCK_WRITE() do { } while(0)
#define TASK_UNLOCK_WRITE() do { } while(0)
#elif defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
#define TASK_LOCK_WRITE() do { taskENTER_CRITICAL(); } while(0)
#define TASK_UNLOCK_WRITE() do { taskEXIT_CRITICAL(); } while(0)
#else
#error configure this platform about tasks locking
#endif
static void handleCPSRequest(void);
volatile int com_request = 0;
volatile uint8_t com_requestbuffer[COM_REQUESTBUFFER_SIZE];
volatile uint8_t usbComSendBuf[COM_BUFFER_SIZE];
static int sector = -1;
volatile int comRecvMMDVMIndexIn = 0;
volatile int comRecvMMDVMIndexOut = 0;
volatile int comRecvMMDVMFrameCount = 0;
static bool flashingDMRIDs = false;
static bool channelsRewritten = false;
static bool luczRewritten = false;
#if defined(HAS_GPS)
static gpsMode_t previousGPSState = GPS_NOT_DETECTED;
#endif
bool isCompressingAMBE = false;
volatile static bool hasToReply = false;
volatile static uint32_t replyLength = 0;
volatile bool usbIsResetting = false;
/*
static void hexDump2(uint8_t *ptr, int len,char *msg)
{
char buf[8];
msg[0] = 0;
for (int i = 0; i < len; i++)
{
sprintf(buf,"%02x",*ptr);
strcat(msg,buf);
ptr++;
}
}*/
static bool addressInSegment(uint32_t address, uint32_t length, uint32_t segmentStart, uint32_t segmentSize)
{
return ((address >= segmentStart) && ((address + length) <= (segmentStart + segmentSize)));
}
void tick_com_request(void)
{
switch (settingsUsbMode)
{
case USB_MODE_CPS:
if (com_request == 1)
{
TASK_LOCK_WRITE();
handleCPSRequest();
com_request = 0;
if (hasToReply)
{
CDC_Transmit_FS((uint8_t *) usbComSendBuf, replyLength);
hasToReply = false;
replyLength = 0;
}
TASK_UNLOCK_WRITE();
}
break;
case USB_MODE_HOTSPOT:
// That will happen once when MMDVMHost send the first frame
// out of the Hotspot mode.
if (com_request == 1)
{
com_request = 0;
if ((nonVolatileSettings.hotspotType != HOTSPOT_TYPE_OFF) &&
((comRecvMMDVMFrameCount >= 1) && (com_requestbuffer[1] == MMDVM_FRAME_START)) &&
(uiDataGlobal.dmrDisabled == false)) // DMR (digital) is disabled.
{
if (menuSystemGetCurrentMenuNumber() != UI_HOTSPOT_MODE)
{
menuSystemPushNewMenu(UI_HOTSPOT_MODE);
}
}
}
break;
}
}
static void cpsHandleReadCommand(void)
{
uint32_t address = (com_requestbuffer[2] << 24) + (com_requestbuffer[3] << 16) + (com_requestbuffer[4] << 8) + (com_requestbuffer[5] << 0);
uint32_t length = (com_requestbuffer[6] << 8) + (com_requestbuffer[7] << 0);
bool result = false;
if (length > (COM_REQUESTBUFFER_SIZE - 3))
{
length = (COM_REQUESTBUFFER_SIZE - 3);
}
switch(com_requestbuffer[1])
{
case CPS_ACCESS_FLASH:
{
// Calibration register, returns local copy
if (addressInSegment(address, length, 0x10000, 0x200))
{
uint8_t *p = calibrationGetLocalDataPointer();
memcpy((uint8_t *)&usbComSendBuf[3], (p + (address - 0x10000)), length);
result = true;
}
else
{
TASK_UNLOCK_WRITE();
result = SPI_Flash_read(address, (uint8_t *)&usbComSendBuf[3], length);
uint32_t end = address + length - 1;
const uint32_t VFOs_END = CODEPLUG_ADDR_VFO_A_CHANNEL + (sizeof(CodeplugChannel_t) * 2);
// if CPS is writing the second part of the EEPROM (emulated in Flash) then the VFO's are being updated.
if ((address <= CODEPLUG_ADDR_VFO_A_CHANNEL) && (end >= VFOs_END))
{
uint32_t offset = CODEPLUG_ADDR_VFO_A_CHANNEL - address;
CodeplugChannel_t * destPtr = (CodeplugChannel_t *)&usbComSendBuf[3 + offset];
memcpy((uint8_t *)destPtr, (uint8_t *)&settingsVFOChannel[0], CODEPLUG_CHANNEL_DATA_STRUCT_SIZE);
codeplugConvertChannelInternalToCodeplug(destPtr, destPtr);
destPtr = (CodeplugChannel_t *)&usbComSendBuf[3 + offset + CODEPLUG_CHANNEL_DATA_STRUCT_SIZE];
memcpy((uint8_t *)destPtr, (uint8_t *)&settingsVFOChannel[1], CODEPLUG_CHANNEL_DATA_STRUCT_SIZE);
codeplugConvertChannelInternalToCodeplug(destPtr, destPtr);
}
TASK_LOCK_WRITE();
}
}
break;
case CPS_ACCESS_EEPROM:
TASK_UNLOCK_WRITE();
result = EEPROM_Read(address, (uint8_t *)&usbComSendBuf[3], length);
TASK_LOCK_WRITE();
break;
case CPS_ACCESS_MCU_ROM:
// Base address of MCU ROM on the STM32 is 0x8000000 not 0x00000000
memcpy((uint8_t *)&usbComSendBuf[3], (uint8_t *)address + 0x8000000, length);
result = true;
break;
case CPS_ACCESS_DISPLAY_BUFFER:
rxPowerSavingSetState(ECOPHASE_POWERSAVE_INACTIVE); // Avoiding going into a state that USB doesn't work.
#if defined(PLATFORM_VARIANT_DM1701)
if (address < (DISPLAY_Y_OFFSET * DISPLAY_SIZE_X * 2))
{
memset((uint8_t *)&usbComSendBuf[3], 0xFF, length);// send white for blank area
}
else
{
memcpy((uint8_t *)&usbComSendBuf[3], (uint8_t *)displayGetPrimaryScreenBuffer() + address - (DISPLAY_Y_OFFSET * DISPLAY_SIZE_X * 2), length);
}
#else
memcpy((uint8_t *)&usbComSendBuf[3], (uint8_t *)displayGetPrimaryScreenBuffer() + address, length);
#endif
result = true;
break;
case CPS_ACCESS_WAV_BUFFER:
memcpy((uint8_t *)&usbComSendBuf[3], (uint8_t *)&audioAndHotspotDataBuffer.rawBuffer[address], length);
result = true;
break;
case CPS_COMPRESS_AND_ACCESS_AMBE_BUFFER:// read from ambe audio buffer
{
uint8_t ambeBuf[32];// ambe data is up to 27 bytes long, but the normal transfer length for the CPS is 32, so make the buffer big enough for that transfer size
memset(ambeBuf, 0, 32);// Clear the ambe output buffer
codecEncode((uint8_t *)ambeBuf, 3);
memcpy((uint8_t *)&usbComSendBuf[3], ambeBuf, length);// The ambe data is only 27 bytes long but the normal CPS request size is 32
memset((uint8_t *)&audioAndHotspotDataBuffer.rawBuffer[0], 0x00, 960);// clear the input wave buffer, in case the next transfer is not a complete AMBE frame. 960 bytes compresses to 27 bytes of AMBE
result = true;
}
break;
case CPS_ACCESS_RADIO_INFO:
{
struct __attribute__((__packed__))
{
uint32_t structVersion;
uint32_t radioType;
char gitRevision[16];
char buildDateTime[16];
uint32_t flashId;
uint16_t features;
} radioInfo;
radioInfo.structVersion = 0x03;
#if defined(PLATFORM_GD77)
radioInfo.radioType = 0;
#elif defined(PLATFORM_GD77S)
radioInfo.radioType = 1;
#elif defined(PLATFORM_DM1801)
radioInfo.radioType = 2;
#elif defined(PLATFORM_RD5R)
radioInfo.radioType = 3;
#elif defined(PLATFORM_DM1801A)
radioInfo.radioType = 4;
#elif defined(PLATFORM_MD9600)
radioInfo.radioType = 5;
#elif defined(PLATFORM_MDUV380)
radioInfo.radioType = 6;
#elif defined(PLATFORM_MD380)
radioInfo.radioType = 7;
#elif defined(PLATFORM_RT84_DM1701) // because of BGR565 / RGB656 colour formats
radioInfo.radioType = ((DISPLAYLCD_TYPE_IS_RGB(displayLCD_Type) != 0) ? 10 : 8);
#elif defined(PLATFORM_MD2017)
radioInfo.radioType = 9;
#endif
// 10 is reserved for DM1701 with RGB panel
snprintf(radioInfo.gitRevision, sizeof(radioInfo.gitRevision), "%s", XSTRINGIFY(GITVERSIONREV));
snprintf(radioInfo.buildDateTime, sizeof(radioInfo.buildDateTime), "%04d%02d%02d%02d%02d%02d", BUILD_YEAR, BUILD_MONTH, BUILD_DAY, BUILD_HOUR, BUILD_MIN, BUILD_SEC);
radioInfo.flashId = flashChipPartNumber;
// Features bitfield (16 bits)
radioInfo.features = (settingsIsOptionBitSet(BIT_INVERSE_VIDEO) ? 1 : 0);
radioInfo.features |= (((dmrIDDatabaseMemoryLocation2 == VOICE_PROMPTS_FLASH_HEADER_ADDRESS) ? 1 : 0) << 1);
radioInfo.features |= ((voicePromptDataIsLoaded ? 1 : 0) << 2);
length = sizeof(radioInfo);
memcpy((uint8_t *)&usbComSendBuf[3], &radioInfo, length);
result = true;
}
break;
#if ! defined(CPU_MK22FN512VLL12)
case CPS_ACCESS_FLASH_SECURITY_REGISTERS:
TASK_UNLOCK_WRITE();
result = SPI_Flash_readSecurityRegisters(address, (uint8_t *)&usbComSendBuf[3], length);
TASK_LOCK_WRITE();
break;
#endif
}
hasToReply = true;
if (result)
{
usbComSendBuf[0] = com_requestbuffer[0];
usbComSendBuf[1] = (length >> 8) & 0xFF;
usbComSendBuf[2] = (length >> 0) & 0xFF;
replyLength = length + 3;
}
else
{
usbComSendBuf[0] = '-';
replyLength = 1;
}
}
static void cpsHandleWriteCommand(void)
{
bool ok = false;
switch(com_requestbuffer[1])
{
case 1: // Flash Prepare Sector
if (sector == -1)
{
sector = (com_requestbuffer[2] << 16) + (com_requestbuffer[3] << 8) + (com_requestbuffer[4] << 0);
if ((sector * 4096) == 0x10000) // Local calibration
{
ok = true;
break;
}
else if ((sector * 4096) == 0x30000) // start address of DMRIDs DB
{
flashingDMRIDs = true;
}
TASK_UNLOCK_WRITE();
ok = SPI_Flash_read(sector * 4096, SPI_Flash_sectorbuffer, 4096);
TASK_LOCK_WRITE();
}
break;
case 2: // Flash Send Data
if (sector >= 0)
{
uint32_t address = (com_requestbuffer[2] << 24) + (com_requestbuffer[3] << 16) + (com_requestbuffer[4] << 8) + (com_requestbuffer[5] << 0);
uint32_t length = (com_requestbuffer[6] << 8) + (com_requestbuffer[7] << 0);
bool calibrationWriting = false;
#if defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
if ((calibrationWriting == false) && addressInSegment(address, length, 0x10000, 0x200)) // Local calibration
{
calibrationWriting = true;
}
// Channel is going to be rewritten, will need to reset current zone/etc...
else if ((channelsRewritten == false) && addressInSegment(address, length, CODEPLUG_ADDR_CHANNEL_HEADER_EEPROM, 0x1C10 /*128 first channels*/))
{
channelsRewritten = true;
}
else if ((luczRewritten == false) && addressInSegment(address, length, CODEPLUG_ADDR_LUCZ, (4 + CODEPLUG_ALL_ZONES_MAX + 1)))
{
luczRewritten = true;
}
else
#endif
#if !defined(PLATFORM_GD77S)
// Temporary hack to automatically set Prompt to Level 1
// A better solution will be added to the CPS and firmware at a later date.
if ((address == VOICE_PROMPTS_FLASH_HEADER_ADDRESS) || (address == VOICE_PROMPTS_FLASH_OLD_HEADER_ADDRESS))
{
if (voicePromptsCheckMagicAndVersion((uint32_t *)&com_requestbuffer[8]))
{
nonVolatileSettings.audioPromptMode = AUDIO_PROMPT_MODE_VOICE_LEVEL_1;
}
}
#endif
if (length > (COM_REQUESTBUFFER_SIZE - 8))
{
length = (COM_REQUESTBUFFER_SIZE - 8);
}
#if defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
// Temporary hack to prevent the QuickKeys getting overwritten by the codeplug
const int QUICKKEYS_BLOCK_END = (CODEPLUG_ADDR_QUICKKEYS + (CODEPLUG_QUICKKEYS_SIZE * sizeof(uint16_t)) - 1);
int end = (address + length) - 1;
if (((address >= CODEPLUG_ADDR_QUICKKEYS) && (address <= QUICKKEYS_BLOCK_END))
|| ((end >= CODEPLUG_ADDR_QUICKKEYS) && (end <= QUICKKEYS_BLOCK_END))
|| ((address < CODEPLUG_ADDR_QUICKKEYS) && (end > QUICKKEYS_BLOCK_END)))
{
if (address < CODEPLUG_ADDR_QUICKKEYS)
{
for (int i = 0; i < (CODEPLUG_ADDR_QUICKKEYS - address); i++)
{
if (sector == (address + i) / 4096)
{
SPI_Flash_sectorbuffer[(address + i) % 4096] = com_requestbuffer[i + 8];
}
}
if ((end > QUICKKEYS_BLOCK_END))
{
for (int i = 0; i < (end - QUICKKEYS_BLOCK_END); i++)
{
if (sector == ((QUICKKEYS_BLOCK_END + 1) + i) / 4096)
{
SPI_Flash_sectorbuffer[((QUICKKEYS_BLOCK_END + 1) + i) % 4096] = com_requestbuffer[i + 8 + ((QUICKKEYS_BLOCK_END + 1) - address)];
}
}
}
}
else
{
if ((address <= QUICKKEYS_BLOCK_END) && (end > QUICKKEYS_BLOCK_END))
{
for (int i = 0; i < (end - QUICKKEYS_BLOCK_END); i++)
{
if (sector == ((QUICKKEYS_BLOCK_END + 1) + i) / 4096)
{
SPI_Flash_sectorbuffer[((QUICKKEYS_BLOCK_END + 1) + i) % 4096] = com_requestbuffer[i + 8 + ((QUICKKEYS_BLOCK_END + 1) - address)];
}
}
}
}
}
else
#endif
{
#if defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
if (calibrationWriting)
{
uint8_t *p = calibrationGetLocalDataPointer();
memcpy((p + (address - 0x10000)), (uint8_t *)&com_requestbuffer[8], length);
sector = -2; // special case in Flash Write;
ok = true;
break;
}
#endif
for (int i = 0; i < length; i++)
{
if (sector == (address + i) / 4096)
{
SPI_Flash_sectorbuffer[(address + i) % 4096] = com_requestbuffer[i + 8];
}
}
}
ok = true;
}
break;
case 3: // Flash Write
if (sector >= 0)
{
TASK_UNLOCK_WRITE();
ok = SPI_Flash_eraseSector(sector * 4096);
TASK_LOCK_WRITE();
if (ok)
{
for (int i = 0; i < 16; i++)
{
TASK_UNLOCK_WRITE();
ok = SPI_Flash_writePage(sector * 4096 + i * 256, SPI_Flash_sectorbuffer + i * 256);
TASK_LOCK_WRITE();
if (!ok)
{
break;
}
}
}
sector = -1;
}
else if (sector == -2)
{
TASK_UNLOCK_WRITE();
calibrationSaveLocal();
TASK_LOCK_WRITE();
ok = true;
sector = -1;
}
break;
case 4: // EEPROM
{
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801) || defined(PLATFORM_DM1801A) || defined(PLATFORM_RD5R)
uint32_t address = (com_requestbuffer[2] << 24) + (com_requestbuffer[3] << 16) + (com_requestbuffer[4] << 8) + (com_requestbuffer[5] << 0);
uint32_t length = (com_requestbuffer[6] << 8) + (com_requestbuffer[7] << 0);
// Channel is going to be rewritten, will need to reset current zone/etc...
if ((channelsRewritten == false) && addressInSegment(address, length, CODEPLUG_ADDR_CHANNEL_HEADER_EEPROM, 0x1C10 /*128 first channels*/))
{
channelsRewritten = true;
}
else if ((luczRewritten == false) && addressInSegment(address, length, CODEPLUG_ADDR_LUCZ, (4 + CODEPLUG_ALL_ZONES_MAX + 1)))
{
luczRewritten = true;
}
if (length > (COM_REQUESTBUFFER_SIZE - 8))
{
length = (COM_REQUESTBUFFER_SIZE - 8);
}
// Temporary hack to prevent the QuickKeys getting overwritten by the codeplug
const int QUICKKEYS_BLOCK_END = (CODEPLUG_ADDR_QUICKKEYS + (CODEPLUG_QUICKKEYS_SIZE * sizeof(uint16_t)) - 1);
int end = (address + length) - 1;
if (((address >= CODEPLUG_ADDR_QUICKKEYS) && (address <= QUICKKEYS_BLOCK_END))
|| ((end >= CODEPLUG_ADDR_QUICKKEYS) && (end <= QUICKKEYS_BLOCK_END))
|| ((address < CODEPLUG_ADDR_QUICKKEYS) && (end > QUICKKEYS_BLOCK_END)))
{
if (address < CODEPLUG_ADDR_QUICKKEYS)
{
TASK_UNLOCK_WRITE();
ok = EEPROM_Write(address, (uint8_t*)com_requestbuffer + 8, (CODEPLUG_ADDR_QUICKKEYS - address));
TASK_LOCK_WRITE();
if (ok && (end > QUICKKEYS_BLOCK_END))
{
TASK_UNLOCK_WRITE();
ok = EEPROM_Write((QUICKKEYS_BLOCK_END + 1), (uint8_t *)com_requestbuffer + 8 + ((QUICKKEYS_BLOCK_END + 1) - address), (end - QUICKKEYS_BLOCK_END));
TASK_LOCK_WRITE();
}
}
else
{
if ((address <= QUICKKEYS_BLOCK_END) && (end > QUICKKEYS_BLOCK_END))
{
TASK_UNLOCK_WRITE();
ok = EEPROM_Write((QUICKKEYS_BLOCK_END + 1), (uint8_t *)com_requestbuffer + 8 + ((QUICKKEYS_BLOCK_END + 1) - address), (end - QUICKKEYS_BLOCK_END));
TASK_LOCK_WRITE();
}
else
{
ok = true;
}
}
// SEGGER_RTT_printf(0, "0x%06x\t0x%06x\n",address,end);
}
else
{
TASK_UNLOCK_WRITE();
ok = EEPROM_Write(address, (uint8_t *)com_requestbuffer + 8, length);
TASK_LOCK_WRITE();
}
#else
ok = true;
#endif
}
break;
case CPS_ACCESS_WAV_BUFFER:// write to raw audio buffer
{
uint32_t address = (com_requestbuffer[2] << 24) + (com_requestbuffer[3] << 16) + (com_requestbuffer[4] << 8) + (com_requestbuffer[5] << 0);
uint32_t length = (com_requestbuffer[6] << 8) + (com_requestbuffer[7] << 0);
wavbuffer_count = (address + length) / WAV_BUFFER_SIZE;
memcpy((uint8_t *)&audioAndHotspotDataBuffer.rawBuffer[address], (uint8_t *)&com_requestbuffer[8], length);
ok = true;
}
break;
case CPS_ACCESS_RADIO_INFO:
break;
}
hasToReply = true;
if (ok)
{
usbComSendBuf[0] = com_requestbuffer[0];
usbComSendBuf[1] = com_requestbuffer[1];
replyLength = 2;
}
else
{
sector = -1;
usbComSendBuf[0] = '-';
replyLength = 1;
}
}
#ifdef USB_DEBUG_COMMANDS
static void cpsHandleDebugCommand(void)
{
const pins[5] = { GPIO_PIN_11, GPIO_PIN_12, GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15};
int command = com_requestbuffer[1];
switch(command)
{
case 'W':
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11,com_requestbuffer[2]=='1'?GPIO_PIN_SET:GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12,com_requestbuffer[3]=='1'?GPIO_PIN_SET:GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_13,com_requestbuffer[4]=='1'?GPIO_PIN_SET:GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14,com_requestbuffer[5]=='1'?GPIO_PIN_SET:GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_15,com_requestbuffer[6]=='1'?GPIO_PIN_SET:GPIO_PIN_RESET);
memcpy(usbComSendBuf," OK\n\0",5);
hasToReply = true;
replyLength = strlen(usbComSendBuf);
break;
case 'R':
for(int i=0;i<5;i++)
{
usbComSendBuf[i]= (HAL_GPIO_ReadPin(GPIOE, pins[i])==GPIO_PIN_SET)?'1':'0';
}
usbComSendBuf[5] = '\n';
usbComSendBuf[6] = 0;
hasToReply = true;
replyLength = strlen(usbComSendBuf);
break;
}
}
#endif
#if defined(HAS_GPS)
static void cpsStopGPSNMEA(void)
{
if (SETTINGS_GPS_MODE_GET(nonVolatileSettings) >= GPS_MODE_ON_NMEA)
{
#if defined(LOG_GPS_DATA)
TASK_UNLOCK_WRITE();
gpsLoggingStop();
TASK_LOCK_WRITE();
#endif
previousGPSState = SETTINGS_GPS_MODE_GET(nonVolatileSettings);
nonVolatileSettings.gpsModeAndBaudsIndex = SETTINGS_GPS_MODE_SET(nonVolatileSettings, GPS_MODE_ON);
}
}
#endif
static void cpsHandleCommand(void)
{
int command = com_requestbuffer[1];
switch(command)
{
case 0:
#if defined(HAS_GPS)
cpsStopGPSNMEA();
#endif
if (menuSystemGetCurrentMenuNumber() == MENU_SATELLITE)
{
menuSystemPopAllAndDisplayRootMenu();
}
// Show CPS screen
menuSystemPushNewMenu(UI_CPS);
break;
case 1:
// Clear CPS screen
uiCPSUpdate(CPS2UI_COMMAND_CLEARBUF, 0, 0, FONT_SIZE_1, TEXT_ALIGN_LEFT, 0, NULL);
break;
case 2:
// Write a line of text to CPS screen
uiCPSUpdate(CPS2UI_COMMAND_PRINT, com_requestbuffer[2], com_requestbuffer[3], (ucFont_t)com_requestbuffer[4], (ucTextAlign_t)com_requestbuffer[5], com_requestbuffer[6], (char *)&com_requestbuffer[7]);
break;
case 3:
// Render CPS screen
uiCPSUpdate(CPS2UI_COMMAND_RENDER_DISPLAY, 0, 0, FONT_SIZE_1, TEXT_ALIGN_LEFT, 0, NULL);
break;
case 4:
// Turn on the display backlight
uiCPSUpdate(CPS2UI_COMMAND_BACKLIGHT, 0, 0, FONT_SIZE_1, TEXT_ALIGN_LEFT, 0, NULL);
break;
case 5:
// Close
if (flashingDMRIDs)
{
dmrIDCacheInit();
flashingDMRIDs = false;
}
isCompressingAMBE = false;
rxPowerSavingSetLevel(nonVolatileSettings.ecoLevel);
uiCPSUpdate(CPS2UI_COMMAND_END, 0, 0, FONT_SIZE_1, TEXT_ALIGN_LEFT, 0, NULL);
break;
case 6:
{
int subCommand = com_requestbuffer[2];
uint32_t m = ticksGetMillis();
// Do some other processing
switch(subCommand)
{
case 0:
// Channels has be rewritten, switch currentZone to All Channels
if (channelsRewritten)
{
int16_t firstContact = 1;
//
// Give it a bit of time before reading the zone count as DM-1801 EEPROM looks slower
// than GD-77 to write
m = ticksGetMillis();
while (true)
{
TASK_UNLOCK_WRITE();
vTaskDelay((10U / portTICK_PERIOD_MS));
TASK_LOCK_WRITE();
if ((ticksGetMillis() - m) > 50)
{
break;
}
}
TASK_UNLOCK_WRITE();
codeplugZonesInitCache(); // Re-read zone cache, as we're using codeplugZonesGetCount() below.
codeplugAllChannelsInitCache(); // Rebuild channels cache
TASK_LOCK_WRITE();
nonVolatileSettings.currentZone = (int16_t) (codeplugZonesGetCount() - 1); // Set to All Channels zone
TASK_UNLOCK_WRITE();
codeplugInitLastUsedChannelInZone(); // Re-read the LUCZ
TASK_LOCK_WRITE();
// Search for the first assigned contact
int16_t luczAllChannels = codeplugGetLastUsedChannelInZone(-1);
if (codeplugAllChannelsIndexIsInUse(luczAllChannels))
{
firstContact = luczAllChannels;
}
else
{
for (int16_t i = CODEPLUG_CHANNELS_MIN; i <= CODEPLUG_CHANNELS_MAX; i++)
{
if (codeplugAllChannelsIndexIsInUse(i))
{
firstContact = i;
break;
}
// Call tick_watchdog() ??
}
}
#if defined(PLATFORM_RD5R)
nonVolatileSettings.currentChannelIndexInAllZone = firstContact;
nonVolatileSettings.currentChannelIndexInZone = 0;
#endif
codeplugSetLastUsedChannelInZone(-1, firstContact);
luczRewritten = false;
}
#if defined(HAS_GPS)
// restore GPS state if needed
if (previousGPSState >= GPS_MODE_ON_NMEA)
{
nonVolatileSettings.gpsModeAndBaudsIndex = SETTINGS_GPS_MODE_SET(nonVolatileSettings, previousGPSState);
}
#endif
// save current settings and reboot
m = ticksGetMillis();
TASK_UNLOCK_WRITE();
settingsSaveSettings(false);// Need to save these channels prior to reboot, as reboot does not save
TASK_LOCK_WRITE();
if (! luczRewritten)
{
TASK_UNLOCK_WRITE();
codeplugSaveLastUsedChannelInZone();
TASK_LOCK_WRITE();
}
// Give it a bit of time before pulling the plug as DM-1801 EEPROM looks slower
// than GD-77 to write, then quickly power cycling triggers settings reset.
while (true)
{
TASK_UNLOCK_WRITE();
vTaskDelay((10U / portTICK_PERIOD_MS));
TASK_LOCK_WRITE();
if ((ticksGetMillis() - m) > 50)
{
break;
}
}
addTimerCallback(NVIC_SystemReset, 500, MENU_ANY, false);
break;
case 1:
#if defined(HAS_GPS)
if (previousGPSState >= GPS_MODE_ON_NMEA)
{
nonVolatileSettings.gpsModeAndBaudsIndex = SETTINGS_GPS_MODE_SET(nonVolatileSettings, previousGPSState);
}
#endif
addTimerCallback(NVIC_SystemReset, 500, MENU_ANY, false);
break;
case 2:
#if defined(HAS_GPS)
if (previousGPSState >= GPS_MODE_ON_NMEA)
{
nonVolatileSettings.gpsModeAndBaudsIndex = SETTINGS_GPS_MODE_SET(nonVolatileSettings, previousGPSState);
}
#endif
// Save settings VFO's to codeplug
TASK_UNLOCK_WRITE();
settingsSaveSettings(true);
TASK_LOCK_WRITE();
#if defined(HAS_GPS)
if (previousGPSState >= GPS_MODE_ON_NMEA)
{
nonVolatileSettings.gpsModeAndBaudsIndex = SETTINGS_GPS_MODE_SET(nonVolatileSettings, GPS_MODE_ON);
}
#endif
break;
case 3:
// flash green LED
uiCPSUpdate(CPS2UI_COMMAND_GREEN_LED, 0, 0, FONT_SIZE_1, TEXT_ALIGN_LEFT, 0, NULL);
break;
case 4:
// flash red LED
uiCPSUpdate(CPS2UI_COMMAND_RED_LED, 0, 0, FONT_SIZE_1, TEXT_ALIGN_LEFT, 0, NULL);
break;
case 5:
rxPowerSavingSetLevel(0);
isCompressingAMBE = true;
codecInitInternalBuffers();
break;
case 6:
soundInit();// Resets the sound buffers
memset((uint8_t *)&audioAndHotspotDataBuffer.rawBuffer[0], 0, (6 * WAV_BUFFER_SIZE));// clear 1 dmr frame size of wav buffer memory
break;
case 7:
memcpy(&uiDataGlobal.dateTimeSecs, (uint8_t *)&com_requestbuffer[3], sizeof(uint32_t));// update date with data from the CPS
#if ! defined(PLATFORM_GD77S)
daytimeThemeChangeUpdate(true);
#endif
#if defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
setRtc_custom(uiDataGlobal.dateTimeSecs);
#endif
menuSatelliteScreenClearPredictions(true);
break;
// 8:
// 9:
case 10: // wait 10ms
m = ticksGetMillis();
while (true)
{
TASK_UNLOCK_WRITE();
vTaskDelay((5U / portTICK_PERIOD_MS));
TASK_LOCK_WRITE();
if ((ticksGetMillis() - m) > 10)
{
break;
}
}
break;
default:
break;
}
}
break;
case 7:
#if defined(HAS_GPS)
if (previousGPSState >= GPS_MODE_ON_NMEA)
{
nonVolatileSettings.gpsModeAndBaudsIndex = SETTINGS_GPS_MODE_SET(nonVolatileSettings, previousGPSState);
previousGPSState = GPS_NOT_DETECTED;
#if defined(LOG_GPS_DATA)
TASK_UNLOCK_WRITE();
gpsLoggingStart();
TASK_LOCK_WRITE();
#endif
}
#endif
break;
#if defined(DEBUG_POSITION)
#warning DEBUG_POSITION is ENABLED
case 'P':
{
int lat,lon;
com_requestbuffer[COM_REQUESTBUFFER_SIZE-1] = 0;// terminate string ready for sscanf
// Command to set Lat/Lon to 0.0001 deg
// In the format CP NNNNNN NNNNNN
// e.g. CP -234567 12345678 = 23.4567S 123.4567E
sscanf((char *)&com_requestbuffer[2],"%d %d",&lat,&lon);
settingsSet(nonVolatileSettings.location.lat, latLonDoubleToFixed32(((double)lat) / 10000.0F));
settingsSet(nonVolatileSettings.location.Lon, latLonDoubleToFixed32(((double)lon) / 10000.0F));
}
break;
#endif
case 254: // PING
#if defined(HAS_GPS)
cpsStopGPSNMEA();
#endif
break;
default:
break;
}
// Send something generic back.
// Probably need to send a response code in the future
usbComSendBuf[0] = '-';
hasToReply = true;
replyLength = 1;
}
static void handleCPSRequest(void)
{
//Handle read
switch(com_requestbuffer[0])
{
case 'R':
cpsHandleReadCommand();
break;
case 'X'://W
cpsHandleWriteCommand();
break;
case 'W': // Fake write
{
usbComSendBuf[0] = com_requestbuffer[0];
usbComSendBuf[1] = com_requestbuffer[1];
hasToReply = true;
replyLength = 2;
}
break;
case 'C':
cpsHandleCommand();
break;
#ifdef USB_DEBUG_COMMANDS
case 'D':
cpsHandleDebugCommand();
break;
#endif
#if 0 // has to be rewritten
case '$':// NMEA
{
gpsPower(false);
nonVolatileSettings.gpsModeAndBaudsIndex = SETTINGS_GPS_MODE_SET(nonVolatileSettings, GPS_MODE_ON);
//HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
int bufPos = 0;
char rxchar;
do
{
rxchar = com_requestbuffer[bufPos];
if (rxchar != 13)
{
gpsLine[bufPos] = rxchar;
}
bufPos++;
} while((rxchar != 13) && (bufPos < COM_REQUESTBUFFER_SIZE)) ;
gpsLineLength = bufPos;
gpsLineReady = true;
}
break;
#endif
default:
usbComSendBuf[0] = '-';
hasToReply = true;
replyLength = 1;
break;
}
}
#if 0
__attribute__((section(".ccmram"))) volatile uint8_t com_buffer[COM_BUFFER_SIZE];
int com_buffer_write_idx = 0;
int com_buffer_read_idx = 0;
volatile int com_buffer_cnt = 0;
void send_packet(uint8_t val_0x82, uint8_t val_0x86, int ram)
{
taskENTER_CRITICAL();
if ((HR_C6000_datalogging) && ((com_buffer_cnt+8+(ram+1))<=COM_BUFFER_SIZE))
{
add_to_commbuffer((com_buffer_cnt >> 8) & 0xff);
add_to_commbuffer((com_buffer_cnt >> 0) & 0xff);
add_to_commbuffer(val_0x82);
add_to_commbuffer(val_0x86);
add_to_commbuffer(tmp_val_0x51);
add_to_commbuffer(tmp_val_0x52);
add_to_commbuffer(tmp_val_0x57);
add_to_commbuffer(tmp_val_0x5f);
for (int i=0;i<=ram;i++)
{
add_to_commbuffer(DMR_frame_buffer[i]);
}
}
taskEXIT_CRITICAL();
}
uint8_t tmp_ram1[256];
uint8_t tmp_ram2[256];
void send_packet_big(uint8_t val_0x82, uint8_t val_0x86, int ram1, int ram2)
{
taskENTER_CRITICAL();
if ((HR_C6000_datalogging) && ((com_buffer_cnt+8+(ram1+1)+(ram2+1))<=COM_BUFFER_SIZE))
{
add_to_commbuffer((com_buffer_cnt >> 8) & 0xff);
add_to_commbuffer((com_buffer_cnt >> 0) & 0xff);
add_to_commbuffer(val_0x82);
add_to_commbuffer(val_0x86);
add_to_commbuffer(tmp_val_0x51);
add_to_commbuffer(tmp_val_0x52);
add_to_commbuffer(tmp_val_0x57);
add_to_commbuffer(tmp_val_0x5f);
for (int i=0;i<=ram1;i++)
{
add_to_commbuffer(tmp_ram1[i]);
}
for (int i=0;i<=ram2;i++)
{
add_to_commbuffer(tmp_ram2[i]);
}
}
taskEXIT_CRITICAL();
}
void add_to_commbuffer(uint8_t value)
{
com_buffer[com_buffer_write_idx]=value;
com_buffer_cnt++;
com_buffer_write_idx++;
if (com_buffer_write_idx==COM_BUFFER_SIZE)
{
com_buffer_write_idx=0;
}
}
#endif
void USB_DEBUG_PRINT(const char *str)
{
strncpy((char *)usbComSendBuf, str, COM_BUFFER_SIZE);
usbComSendBuf[COM_BUFFER_SIZE - 1] = 0; // SAFETY: strncpy won't NULL terminate the buffer if length is exceeding.
CDC_Transmit_FS((uint8_t *)usbComSendBuf, strlen((char *)usbComSendBuf));
}
void USB_DEBUG_printf(const char *format, ...)
{
char buf[COM_BUFFER_SIZE];
va_list params;
va_start(params, format);
vsnprintf(buf, COM_BUFFER_SIZE, format, params);
va_end(params);
USB_DEBUG_PRINT(buf);
}
extern USBD_HandleTypeDef hUsbDeviceFS;
bool USB_DeviceIsResetting(void)
{
if ((hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED) && (clockManagerGetRunMode() != CLOCK_MANAGER_SPEED_RUN))
{
clockManagerSetRunMode(kAPP_PowerModeRun, CLOCK_MANAGER_SPEED_RUN);
}
return usbIsResetting;
}