forked from TMaxElectronics/MidiStick_Firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathConfigManager.c
447 lines (356 loc) · 17.1 KB
/
ConfigManager.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/*
Copyright (C) 2020,2021 TMax-Electronics.de
This file is part of the MidiStick Firmware.
the MidiStick Firmware is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
the MidiStick Firmware 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 the MidiStick Firmware. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ConfigManager.h"
#include "UART32.h"
#include "usb lib/usb_ch9.h"
#include "MidiController.h"
#include "HIDController.h"
#include <stdint.h>
#include <string.h>
#define _SUPPRESS_PLIB_WARNING
#include <peripheral/nvm.h>
#define FORCE_SETTINGS_OVERRIDE_MIN_FWVER "\x01V1.2"
MidiProgramm defaultProgramm = {"default programm ", 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 2}; //default programm has no effects enabled and uses the standard bend range of +-2
CoilConfig defaultCoil = {"default coil ", 0, 0, 10, 30, 0}; //default coil must not output anything, so the max on time is set to zero
/*
* This here is a bit of voodoo. In the linker I have ordered the compiler not to touch NVM above 0x9D020000 when generating code, so this will always be free.
* It is exactly at the half way point in NVM, so we have got 50% of our flash after this available for the updated code.
*
* At atartup the bootloader checks the value in the fwStatus variable, which tells it if it should start copying the data from the upper half of NVM to the lower->(the one we are executing from)
* 0x10 is the code for normal running code. It jumps to execution
* 0x20 is the code for a pending software update, while keeping current settings.
* 0x21 is the code for a pending software update, but with the settings copied over as well.
*
* The bootloader protects the settings by ignoring NVM between [resMemStart] and [resMemEnd] while copying and erasing. it will start to ignore the first page after teh value in resMemStart, and continue erasing/writing in the first page after resMemEnd
*/
//initialize the default configuration
const volatile CONF ConfigData __attribute__((aligned(BYTE_PAGE_SIZE),space(prog), address(0x9d01d000))) = {.cfg.name = "Midi Stick", .cfg.ledMode1 = LED_DUTY_LIMITER, .cfg.ledMode2 = LED_POWER, .cfg.ledMode3 = LED_DATA, .cfg.auxMode = 0, .cfg.fwVersion = "V2.2"
, .cfg.fwStatus = 0x11, .cfg.resMemStart = ((uint32_t) &ConfigData), .cfg.resMemEnd = ((uint32_t) &ConfigData.expCfg.compressorRelease), .cfg.compileDate = __DATE__, .cfg.compileTime = __TIME__, .devName = {sizeof(USBDevNameHeader),USB_DESCRIPTOR_STRING, {'M','i','d','i','S','t','i','c','k',' ',' ',' ',' ',' '}}, .cfg.USBPID = 0x6162,
.expCfg.stereoPosition = 64, .expCfg.stereoWidth = 16, .expCfg.stereoSlope = 255, .expCfg.flags = CONFIG_KEEP_CC, .expCfg.selectedCC = 0xff, .expCfg.compressorAttac = 1, .expCfg.compressorSustain = 155, .expCfg.compressorRelease = 1};
const volatile uint8_t FWUpdate[] __attribute__((address(0x9d020000), space(fwUpgradeReserved))) = {FORCE_SETTINGS_OVERRIDE_MIN_FWVER}; //dummy data
const volatile uint8_t NVM_mapMem[MAPMEM_SIZE] __attribute__((space(fwUpgradeReserved), address(0x9d020000 + BYTE_PAGE_SIZE))) = {0}; //dummy data
const volatile uint8_t NVM_blockMem[BLOCKMEM_SIZE] __attribute__((space(fwUpgradeReserved), address(0x9d020000 + BYTE_PAGE_SIZE + MAPMEM_SIZE))) = {0}; //dummy data
//erase all memory possibly containing previous firmware updates
void NVM_eraseFWUpdate(){
NVM_memclr4096(&FWUpdate, 0x1f000);
}
//write a data packet into the NVM, at the given offset
void NVM_writeFWUpdate(void* src, uint32_t pageOffset){
uint32_t offset = (pageOffset * PAGE_SIZE);
NVM_memclr4096((void*) ((uint32_t) &FWUpdate + offset), PAGE_SIZE);
NVM_memcpy128((void*) ((uint32_t) &FWUpdate + offset), src, PAGE_SIZE);
}
//write the fwStatus to 0x20 or 0x21, depending on what is required
void NVM_commitFWUpdate(unsigned settingsOverwrite){
void* pageStart = (void*) &ConfigData.cfg; //the page containing the config data starts here and contains the coil configs as well
//copy page data to ram
void* buffer = malloc(PAGE_SIZE);
memcpy(buffer, pageStart, PAGE_SIZE);
//change the data
((CFGData *)buffer)->fwStatus = 0x20 | settingsOverwrite;
//erase the page and write data from ram
NVM_erasePage(pageStart);
NVM_memcpy128(pageStart, buffer, PAGE_SIZE);
free(buffer);
}
void NVM_finishFWUpdate(){ //if an update was just performed, we need to reset the command and load the new firmware's info into the flash
if(ConfigData.cfg.fwStatus == 0x10) return;
UART_sendString("Info> Firmware was upgraded to ", 0);
CFGData * pageStart = (void*) &ConfigData.cfg;
CFGData * updatedCFG = (CFGData *) ((uint32_t) &ConfigData.cfg + 0x20000); //this loads the FW information from the region of the updated firmware
CFGData * buffer = malloc(PAGE_SIZE);
EXPCFGData * expBuffer = (EXPCFGData *) ((uint32_t) buffer + ((uint32_t) &ConfigData.expCfg - (uint32_t) &ConfigData.cfg));
memcpy(buffer, pageStart, PAGE_SIZE);
if(ConfigData.cfg.fwStatus != 0x11){
//make sure all parameters are within their valid range, if one is outside it, write the default value
//This is needed if a setting was added, as the value at that location is not predictable, and could potenitally cause problems
//If for example a device with V0.9 was updated to V0.91 (where the custom Pids were added) it would have a pid of 0x3e1, which would effectively soft brick the device
if(buffer->name[0] == 0){
strcpy(buffer->name, "Midi Stick");
}
if(buffer->ledMode1 > LED_TYPE_COUNT){
buffer->ledMode1 = LED_DATA;
}
if(buffer->ledMode2 > LED_TYPE_COUNT){
buffer->ledMode2 = LED_DUTY_LIMITER;
}
if(buffer->ledMode3 > LED_TYPE_COUNT){
buffer->ledMode3 = LED_OUT_ON;
}
if(buffer->auxMode > AUX_MODE_COUNT){
buffer->auxMode = AUX_AUDIO;
}
if(!(buffer->USBPID >= 0x6162 && buffer->USBPID < 0x6169)){
buffer->USBPID = 0x6162;
}
if(expBuffer->stereoPosition > 127){
expBuffer->stereoPosition = 0;
}
if(expBuffer->stereoWidth > 65){
expBuffer->stereoWidth = 64;
}
if(expBuffer->stereoSlope == 0){
expBuffer->stereoSlope = 0xff;
}
memcpy(buffer->fwVersion, updatedCFG->fwVersion, 22);
buffer->resMemEnd = updatedCFG->resMemEnd;
buffer->resMemStart = updatedCFG->resMemStart;
}
//overwrite the old data for fwStatus (otherwise the bootloader would just copy stuff again)
buffer->fwStatus = 0x10;
//erase the old and write the new data
NVM_erasePage(pageStart);
NVM_memcpy128(pageStart, buffer, PAGE_SIZE);
free(buffer);
#ifndef __DEBUG
Midi_setEnabled(0);
NVM_memclr4096(NVM_mapMem, MAPMEM_SIZE);
HID_erasePending = 1;
HID_currErasePage = NVM_blockMem;
UART_sendString(buffer->fwVersion, 1);
#endif
}
void NVM_clearAllCoils(){
void* pageStart = (void*) &ConfigData.coils[0];
NVM_memclr4096(pageStart, sizeof(CoilConfig) * 32); //clear the configs, this also aligns with the flash page, so again we don't need to worry about deleting configuration stuff or programms
//UART_sendString("deleted all coils", 1);
}
unsigned NVM_writeCFG(CFGData * src){
void* pageStart = (void*) &ConfigData.cfg; //the page containing the config data starts here and contains the coil configs as well
//copy page data to ram
void* settingsBuffer = malloc(PAGE_SIZE);
memcpy(settingsBuffer, pageStart, PAGE_SIZE);
//overwrite old data with the new
memcpy(settingsBuffer, src, 28);
//calculate the offset to the usb name string
uint32_t usbNameOffset = (uint32_t) ConfigData.devName.string - (uint32_t) pageStart;
uint16_t * currTarget = (uint16_t * ) (usbNameOffset + (uint32_t) settingsBuffer);
//convert the 8bit string to 16bit unicode and overwrite the old one
uint8_t c = 0;
for(c = 0; c < 21; c++){
*(currTarget++) = src->name[c];
}
//erase the page and write data from ram
NVM_erasePage(pageStart);
NVM_memcpy128(pageStart, settingsBuffer, PAGE_SIZE);
//UART_sendString("write: ", 0); UART_sendString(ConfigData.cfg.name, 1);
free(settingsBuffer);
return 1;
}
unsigned NVM_writeExpCFG(EXPCFGData * src){
void* pageStart = (void*) &ConfigData.cfg; //the page containing the config data starts here and contains the coil configs as well
//copy page data to ram
void* settingsBuffer = malloc(PAGE_SIZE);
memcpy(settingsBuffer, pageStart, PAGE_SIZE);
EXPCFGData * data = (EXPCFGData *) ((uint32_t) settingsBuffer + ((uint32_t) &ConfigData.expCfg - (uint32_t) &ConfigData.cfg));
//copy settings that cannot be changed by the config software
src->selectedCC = data->selectedCC;
//overwrite old data with the new
memcpy(data, src, sizeof(EXPCFGData));
//erase the page and write data from ram
NVM_erasePage(pageStart);
NVM_memcpy128(pageStart, settingsBuffer, PAGE_SIZE);
//UART_sendString("write: ", 0); UART_sendString(ConfigData.cfg.name, 1);
free(settingsBuffer);
return 1;
}
unsigned NVM_updateSelectedCC(uint8_t selected){
if(selected > 32 && selected != 0xff) return 0;
void* pageStart = (void*) &ConfigData.cfg; //the page containing the config data starts here and contains the coil configs as well
//copy page data to ram
void* settingsBuffer = malloc(PAGE_SIZE);
memcpy(settingsBuffer, pageStart, PAGE_SIZE);
EXPCFGData * data = (EXPCFGData *) ((uint32_t) settingsBuffer + ((uint32_t) &ConfigData.expCfg - (uint32_t) &ConfigData.cfg));
data->selectedCC = selected;
//erase the page and write data from ram
NVM_erasePage(pageStart);
NVM_memcpy128(pageStart, settingsBuffer, PAGE_SIZE);
free(settingsBuffer);
return 1;
}
unsigned NVM_updateDevicePID(uint16_t newPID){
if(!(newPID >= 0x6162 && newPID < 0x6169)) return 0;
void* pageStart = (void*) &ConfigData.cfg; //the page containing the config data starts here and contains the coil configs as well
//copy page data to ram
void* settingsBuffer = malloc(PAGE_SIZE);
memcpy(settingsBuffer, pageStart, PAGE_SIZE);
CFGData * currCFG = (CFGData *) settingsBuffer;
currCFG->USBPID = newPID;
//erase the page and write data from ram
NVM_erasePage(pageStart);
NVM_memcpy128(pageStart, settingsBuffer, PAGE_SIZE);
free(settingsBuffer);
return 1;
}
unsigned NVM_isCoilConfigValid(uint8_t index){
if(index > 31) return 0;
return ConfigData.coils[index].name[0] != 0 && (uint8_t) ConfigData.coils[index].name[0] != 0xff;
}
void NVM_copyProgrammData(MidiProgramm * dst, MidiProgramm * src){
dst->AttacTime = src->AttacTime;
dst->DecayTime = src->DecayTime;
dst->releaseTime = src->releaseTime;
dst->sustainPower = src->sustainPower;
dst->sustainEnabled = src->sustainEnabled;
dst->fmFreq = src->fmFreq;
dst->fmAmpl = src->fmAmpl;
dst->amFreq = src->amFreq;
dst->amFreq = src->amFreq;
dst->noteOffset = src->noteOffset;
dst->bendRange = src->bendRange;
memcpy(dst->name, src->name, 24);
}
unsigned NVM_readCoilConfig(CoilConfig * dest, uint8_t index){
if(!NVM_isCoilConfigValid(index)){
NVM_copyCoilData(dest, &defaultCoil);
return 0;
}
CoilConfig curr = ConfigData.coils[index];
NVM_copyCoilData(dest, &curr);
return 1;
}
unsigned NVM_writeCoilConfig(CoilConfig * src, uint8_t index){
if((uint8_t) ConfigData.coils[index].name[0] == 0xff){ //has the memory been cleared aleady?
if(src->maxDuty > 95 || src->maxOnTime > 10000) return;
NVM_memcpy4(&ConfigData.coils[index], src, sizeof(CoilConfig)); //if it is, we only need to write the data
}else{
if(src->maxDuty > 95 || src->maxOnTime > 10000) return;
void* pageStart = (void*) &ConfigData.coils[0];
void* settingsBuffer = malloc(PAGE_SIZE);
memcpy(settingsBuffer, pageStart, PAGE_SIZE);
uint32_t dataOffset = (void*) &ConfigData.coils[index] - pageStart; //subtract actual data position from offset to get relative offset in copied data
NVM_copyCoilData(settingsBuffer + dataOffset, src);
//at this point we have our new data in memory and can erase and then write it to NVM
NVM_erasePage(pageStart);
NVM_memcpy128(pageStart, settingsBuffer, PAGE_SIZE);
free(settingsBuffer);
}
}
void NVM_copyCoilData(CoilConfig * dst, CoilConfig * src){
dst->holdoffTime = src->holdoffTime;
dst->ontimeLimit = src->ontimeLimit;
dst->maxDuty = src->maxDuty;
dst->maxOnTime = src->maxOnTime;
dst->minOnTime = src->minOnTime;
memcpy(dst->name, src->name, 24);
}
//NVM read and write functions:
void NVM_memclr4096(void* start, uint32_t length){
uint32_t currOffset = 0;
for(;currOffset < length; currOffset += PAGE_SIZE) NVM_erasePage(start + currOffset);
}
void NVM_memcpy128(void * dst, void * src, uint32_t length){
uint32_t currOffset = 0;
for(;currOffset < length; currOffset += 128) NVM_writeRow(dst + currOffset, src + currOffset);
}
void NVM_memcpy4(void * dst, void * src, uint32_t length){
uint32_t currOffset = 0;
for(;currOffset < length; currOffset += 4){
uint32_t * data = src + currOffset;
NVM_writeWord(dst + currOffset, *data);
}
}
void NVM_writeRow(void* address, void * data){
NVMADDR = KVA_TO_PA((unsigned int) address);
NVMSRCADDR = KVA_TO_PA((unsigned int) data);
NVM_operation(0x4003); //NVM Row Program
}
void NVM_writeWord(void* address, unsigned int data){
NVMADDR = KVA_TO_PA((unsigned int)address);
NVMDATA = data;
NVM_operation(0x4001);
}
void NVM_erasePage(void* address){
NVMADDR = KVA_TO_PA((unsigned int)address);
NVM_operation(0x4004);
}
unsigned int __attribute__((nomips16)) NVM_operation(unsigned int nvmop){
int int_status;
int susp;
int_status = INTDisableInterrupts();
LATBCLR = _LATB_LATB15_MASK | _LATB_LATB5_MASK; //make sure to turn off the output just in case
NVMCON = NVMCON_WREN | nvmop;
{
unsigned long t0 = _CP0_GET_COUNT();
while (_CP0_GET_COUNT() - t0 < (80/2)*6);
}
NVMKEY = 0xAA996655;
NVMKEY = 0x556699AA;
NVMCONSET = NVMCON_WR;
while(NVMCON & NVMCON_WR);
NVMCONCLR = NVMCON_WREN;
INTRestoreInterrupts(int_status);
if(NVMCONbits.LVDERR) UART_sendString("smol folt", 1);
if(NVMCONbits.WRERR) UART_sendString("REEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 1);
return(NVMIsError());
}
//get the pointer to the cfg data
CFGData * NVM_getConfig(){
return &ConfigData.cfg;
}
//get the pointer to the cfg data
EXPCFGData * NVM_getExpConfig(){
return &ConfigData.expCfg;
}
//temporary storage of the crc result
int currCRC = 0xffffffff;
//add one byte to the crc calculation
void NVM_crc(uint8_t data) {
int i;
currCRC ^= data;
for (i = 0; i < 8; ++i) {
if (currCRC & 1)
currCRC = (currCRC >> 1) ^ 0xA001;
else
currCRC = (currCRC >> 1);
}
}
//calculate the crc of the update in flash
uint32_t NVM_getUpdateCRC(){
currCRC = 0xffffffff;
//UART_sendString("calc crc", 1);
uint32_t currByte = 0;
uint8_t * currPos = FWUpdate;
while((uint32_t) currPos < 0x9d040000){
if(currPos == FWUpdate || currPos > 0x9d03fff0) UART_sendHex(*(currPos), 1);
NVM_crc(*(currPos++));
}
return currCRC;
}
//get software version data:
char * NVM_getFWVersionString(){
return ConfigData.cfg.fwVersion;
}
uint32_t NVM_getBootloaderVersion() {
return *((uint32_t*) 0x9fc00bec); //pointer to the bl version
}
uint32_t NVM_getSerialNumber() {
return *((uint32_t*) 0x9fc00be8); //pointer to the bl version
}
char * NVM_getBootloaderVersionString() {
uint32_t version = NVM_getBootloaderVersion();
uint16_t majorNum = version >> 16;
uint16_t minorNum = version & 0xffff;
char * buff = malloc(128);
memset(buff, 0, 128);
sprintf(buff, "%d.%d", majorNum, minorNum);
return buff;
}
char * NVM_getFWCompileTime() {
return ConfigData.cfg.compileTime;
}
char * NVM_getFWCompileDate() {
return ConfigData.cfg.compileDate;
}