From 5917edfb601abc878eb1ebc971c6ab1e9d47b645 Mon Sep 17 00:00:00 2001 From: James McKenzie Date: Sun, 7 Apr 2024 03:21:18 +0100 Subject: upadte for d2b5ed7 --- .gitignore | 1 + main/debug-compile.patch | 29 ++ main/dont_set_time_if_no_debugger.patch | 564 ++++++++++++++++++++++++++++++++ main/fix-serial.patch | 149 ++++----- main/metric.patch | 388 +--------------------- main/series | 3 + main/set-time.patch | 83 +++++ 7 files changed, 748 insertions(+), 469 deletions(-) create mode 100644 main/debug-compile.patch create mode 100644 main/dont_set_time_if_no_debugger.patch create mode 100644 main/set-time.patch diff --git a/.gitignore b/.gitignore index 8be5547..f5038c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ status +*~ diff --git a/main/debug-compile.patch b/main/debug-compile.patch new file mode 100644 index 0000000..838dce9 --- /dev/null +++ b/main/debug-compile.patch @@ -0,0 +1,29 @@ +diff --git a/make.mk b/make.mk +index bb2d153..ee40100 100644 +--- a/make.mk ++++ b/make.mk +@@ -58,11 +58,13 @@ CFLAGS += -fdata-sections -ffunction-sections + CFLAGS += -funsigned-char -funsigned-bitfields + CFLAGS += -mcpu=cortex-m0plus -mthumb + CFLAGS += -MD -MP -MT $(BUILD)/$(*F).o -MF $(BUILD)/$(@F).d ++CFLAGS += -g + + LDFLAGS += -mcpu=cortex-m0plus -mthumb + LDFLAGS += -Wl,--gc-sections + LDFLAGS += -Wl,--script=$(TOP)/watch-library/hardware/linker/saml22j18.ld + LDFLAGS += -Wl,--print-memory-usage ++LDFLAGS += -g + + LIBS += -lm + +diff --git a/movement/movement.h b/movement/movement.h +index 1dabfbc..ffceae6 100644 +--- a/movement/movement.h ++++ b/movement/movement.h +@@ -312,4 +312,6 @@ void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); + + uint8_t movement_claim_backup_register(void); + ++void set_time(void); ++ + #endif // MOVEMENT_H_ diff --git a/main/dont_set_time_if_no_debugger.patch b/main/dont_set_time_if_no_debugger.patch new file mode 100644 index 0000000..f38a49a --- /dev/null +++ b/main/dont_set_time_if_no_debugger.patch @@ -0,0 +1,564 @@ +diff --git a/make.mk b/make.mk +index 5610c1f..3cc2d6f 100644 +--- a/make.mk ++++ b/make.mk +@@ -124,6 +124,7 @@ SRCS += \ + $(TOP)/watch-library/hardware/watch/watch_deepsleep.c \ + $(TOP)/watch-library/hardware/watch/watch_private.c \ + $(TOP)/watch-library/hardware/watch/watch_private_cdc.c \ ++ $(TOP)/watch-library/hardware/watch/m0FaultDispatch.c \ + $(TOP)/watch-library/hardware/watch/watch.c \ + $(TOP)/watch-library/hardware/hal/src/hal_atomic.c \ + $(TOP)/watch-library/hardware/hal/src/hal_delay.c \ +diff --git a/movement/set_time.c b/movement/set_time.c +index 27cea25..0cea3d8 100644 +--- a/movement/set_time.c ++++ b/movement/set_time.c +@@ -7,11 +7,27 @@ + #include "watch.h" + #include "movement.h" + #include "watch_utility.h" ++#include "m0FaultDispatch.h" + + #define SYS_TIME 0x11 + + extern movement_state_t movement_state; + ++void ++faultHandlerWithExcFrame (struct CortexExcFrame *exc, uint32_t reason, ++ uint32_t addr, struct CortexPushedRegs *hiRegs) ++{ ++ ++ if (reason == EXC_m0_CAUSE_BKPT_HIT) ++ { ++ exc->pc += 4; ++ exc->r0_r3[0] = 0; ++ return; ++ } ++ ++ while (1); ++} ++ + static inline int32_t + get_tz_offset (movement_settings_t * settings) + { +@@ -32,6 +48,9 @@ void __attribute__((used)) set_time (void) + + t = sys_time (); + ++ if (!t) ++ return; ++ + watch_date_time wdt = watch_utility_date_time_from_unix_time (t, + get_tz_offset + (&movement_state. +diff --git a/watch-library/hardware/watch/m0FaultDispatch.c b/watch-library/hardware/watch/m0FaultDispatch.c +new file mode 100644 +index 0000000..da00b73 +--- /dev/null ++++ b/watch-library/hardware/watch/m0FaultDispatch.c +@@ -0,0 +1,460 @@ ++#include "m0FaultDispatch.h" ++#include ++ ++#define STR2(x) #x ++#define STR(x) STR2(x) ++ ++ ++ ++static uint32_t analyzeInstr16PrvUtilGetReg(const struct CortexExcFrame* frm, struct CortexPushedRegs *moreRegs, uint32_t regNo) ++{ ++ switch (regNo) { ++ case 0: return frm->r0; ++ case 1: return frm->r1; ++ case 2: return frm->r2; ++ case 3: return frm->r3; ++ case 4: //fallthrough ++ case 5: //fallthrough ++ case 6: //fallthrough ++ case 7: return moreRegs->regs4_7[regNo - 4]; ++ case 8: //fallthrough ++ case 9: //fallthrough ++ case 10: //fallthrough ++ case 11: return moreRegs->regs8_11[regNo - 8]; ++ case 12: return frm->r12; ++ case 13: return (uintptr_t)(frm + 1); ++ case 14: return frm->lr; ++ case 15: return frm->pc + 4; ++ default: return 0xffffffff; ++ } ++} ++ ++static bool load8(uint32_t addr, uint32_t *dstP) ++{ ++ uint32_t tmp; ++ bool ret; ++ ++ asm volatile( ++ " mov %0, #1 \n\t" //special formulation for our fault handler ++ " ldrb %1, [%2] \n\t" ++ " b 1f \n\t" ++ " mov %0, #0 \n\t" ++ "1: \n\t" ++ " str %1, [%3] \n\t" ++ :"=&l"(ret), "=&l"(tmp) ++ :"l"(addr), "l"(dstP) ++ :"cc", "memory"); ++ ++ return ret; ++} ++ ++static bool store8(uint32_t addr, uint32_t val) ++{ ++ bool ret; ++ ++ asm volatile( ++ " mov %0, #1 \n\t" //special formulation for our fault handler ++ " strb %2, [%1] \n\t" ++ " b 1f \n\t" ++ " mov %0, #0 \n\t" ++ "1: \n\t" ++ :"=&l"(ret) ++ :"l"(addr), "l"(val) ++ :"cc", "memory"); ++ ++ return ret; ++} ++ ++static bool load16(uint32_t addr, uint32_t *dstP) ++{ ++ uint32_t tmp; ++ bool ret; ++ ++ asm volatile( ++ " mov %0, #1 \n\t" //special formulation for our fault handler ++ " ldrh %1, [%2] \n\t" ++ " b 1f \n\t" ++ " mov %0, #0 \n\t" ++ "1: \n\t" ++ " str %1, [%3] \n\t" ++ :"=&l"(ret), "=&l"(tmp) ++ :"l"(addr), "l"(dstP) ++ :"cc", "memory"); ++ ++ return ret; ++} ++ ++static bool store16(uint32_t addr, uint32_t val) ++{ ++ bool ret; ++ ++ asm volatile( ++ " mov %0, #1 \n\t" //special formulation for our fault handler ++ " strh %2, [%1] \n\t" ++ " b 1f \n\t" ++ " mov %0, #0 \n\t" ++ "1: \n\t" ++ :"=&l"(ret) ++ :"l"(addr), "l"(val) ++ :"cc", "memory"); ++ ++ return ret; ++} ++ ++static bool load32(uint32_t addr, uint32_t *dstP) ++{ ++ uint32_t tmp; ++ bool ret; ++ ++ asm volatile( ++ " mov %0, #1 \n\t" //special formulation for our fault handler ++ " ldr %1, [%2] \n\t" ++ " b 1f \n\t" ++ " mov %0, #0 \n\t" ++ "1: \n\t" ++ " str %1, [%3] \n\t" ++ :"=&l"(ret), "=&l"(tmp) ++ :"l"(addr), "l"(dstP) ++ :"cc", "memory"); ++ ++ return ret; ++} ++ ++static bool store32(uint32_t addr, uint32_t val) ++{ ++ bool ret; ++ ++ asm volatile( ++ " mov %0, #1 \n\t" //special formulation for our fault handler ++ " str %2, [%1] \n\t" ++ " b 1f \n\t" ++ " mov %0, #0 \n\t" ++ "1: \n\t" ++ :"=&l"(ret) ++ :"l"(addr), "l"(val) ++ :"cc", "memory"); ++ ++ return ret; ++} ++ ++//we also get a pointer to the hiregs since we need to reset them before we call faultHandlerWithExcFrame() ++// we get them in pushedRegs ++void __attribute__((used)) analyzeInstr16(struct CortexExcFrame* frm, uint16_t instr, struct CortexPushedRegs *pushedRegs) ++{ ++ static const uint8_t popCntTab[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; //in a nibble ++ static bool (*const accessR[])(uint32_t addr, uint32_t *dstP) = {load8, load16, load32}; ++ static bool (*const accessW[])(uint32_t addr, uint32_t val) = {store8, store16, store32}; ++ uint_fast8_t excCause = EXC_m0_CAUSE_UNCLASSIFIABLE, accessSzLog = 0; ++ uint32_t addr, val, excExtraData = 0, ofst = 0, base = 0, i; ++ bool testStore = false; ++ ++ switch (instr >> 11) { ++ case 0b01000: ++ if ((instr & 0x05c0) == 0x0500) //cmp.hi with both lo regs ++ excCause = EXC_m0_CAUSE_UNDEFINSTR16; ++ if ((instr >> 8) == 0x47) { //BX or BLX ++ ++ if (instr & 7) //SBZ bits not Z ++ excCause = EXC_m0_CAUSE_UNDEFINSTR16; ++ else if ((instr & 0x78) == 0x78) //PC ++ excCause = EXC_m0_CAUSE_UNDEFINSTR16; ++ } ++ break; ++ ++ case 0b01001: //load from literal pool (alignment guaranteed by instr) ++ addr = ((frm->pc + 4) &~ 3) + ((instr & 0xff) << 2); ++ if (!load32(addr, &val)) { ++ ++ excCause = EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL; ++ excExtraData = addr; ++ } ++ break; ++ ++ case 0b01010: //load/store register ++ case 0b01011: ++ switch ((instr >> 9) & 0x07) { ++ case 0b000: //STR ++ testStore = true; ++ //fallthrough ++ case 0b100: //LDR ++ accessSzLog = 2; ++ break; ++ case 0b001: //STRH ++ testStore = true; ++ //fallthrough ++ case 0b101: //LDRH ++ case 0b111: //LDRSH ++ accessSzLog = 1; ++ break; ++ case 0b010: //STRB ++ testStore = true; ++ //fallthrough ++ case 0b110: //LDRB ++ case 0b011: //LDRSB ++ accessSzLog = 0; ++ break; ++ } ++ ofst = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, (instr >> 6) & 0x07); ++ goto load_store_check; ++ ++ case 0b01100: //str imm ++ testStore = true; ++ //fallthrough ++ case 0b01101: //ldr imm ++ accessSzLog = 2; ++ goto load_store_check_get_imm; ++ ++ case 0b01110: //strb imm ++ testStore = true; ++ //fallthrough ++ case 0b01111: //ldrb imm ++ accessSzLog = 0; ++ goto load_store_check_get_imm; ++ ++ case 0b10000: //strh imm ++ testStore = true; ++ //fallthrough ++ ++ case 0b10001: //ldrh imm ++ accessSzLog = 1; ++ goto load_store_check_get_imm; ++ ++load_store_check_get_imm: ++ ofst = ((instr >> 6) & 0x1f) << accessSzLog; ++ ++load_store_check: ++ base = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, (instr >> 3) & 0x07); ++ ++load_store_check_have_base: ++ addr = base + ofst; ++ ++ if (addr << (32 - accessSzLog)) { ++ ++ excCause = EXC_m0_CAUSE_DATA_UNALIGNED; ++ excExtraData = addr; ++ } ++ else if (!accessR[accessSzLog](addr, &val)) { ++ ++ excCause = EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL; ++ excExtraData = addr; ++ } ++ else if (testStore && !accessW[accessSzLog](addr, val)) { ++ ++ excCause = EXC_m0_CAUSE_MEM_WRITE_ACCESS_FAIL; ++ excExtraData = addr; ++ } ++ //if we have not faulted by now, we do not know why this load/store faulted ++ break; ++ ++ case 0b10010: //sp-based store ++ testStore = true; ++ //fallthrough ++ ++ case 0b10011: //sp-based load ++ ofst = (instr & 0xff) * 4; ++ accessSzLog = 3; ++ base = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, 13); ++ goto load_store_check_have_base; ++ ++ case 0b10111: ++ if ((instr & 0x0700) == 0x0600) ++ excCause = EXC_m0_CAUSE_BKPT_HIT; ++ //push/pop are here too, but if they fail, we'd fail to stash and not ever get here ++ break; ++ ++ case 0b11000: //STMIA ++ testStore = true; ++ //fallthrough ++ case 0b11001: //LDMIA ++ base = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, (instr >> 8) & 0x07); ++ if (base & 3) { //unaligned base ++ excExtraData = base; ++ excCause = EXC_m0_CAUSE_DATA_UNALIGNED; ++ } ++ else if (!(instr & 0xff)) //LDM/STM with empty reg set ++ excCause = EXC_m0_CAUSE_UNDEFINSTR16; ++ else { ++ i = popCntTab[instr & 0x0f] + popCntTab[(instr >> 4) & 0x0f]; ++ ++ do { ++ ++ addr = base; ++ base += 4; ++ ++ if (!load32(addr, &val)) { ++ ++ excCause = EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL; ++ excExtraData = addr; ++ } ++ else if (testStore && !store32(addr, val)) { ++ ++ excCause = EXC_m0_CAUSE_MEM_WRITE_ACCESS_FAIL; ++ excExtraData = addr; ++ } ++ else ++ continue; ++ ++ break; //one fault found is enough ++ ++ } while (--i); ++ //if we have not faulted by now, we do not know why this ldm/stm faulted ++ } ++ break; ++ ++ case 0b11011: ++ if ((instr & 0x0700) == 0x0600) //UDF ++ excCause = EXC_m0_CAUSE_UNDEFINSTR16; ++ break; ++ } ++ ++ faultHandlerWithExcFrame(frm, excCause, excExtraData, pushedRegs); ++} ++ ++void __attribute__((used,naked)) iHardFault_Handler(void) ++{ ++ asm volatile( ++ ++ //grab the appropriate SP ++ " mov r0, lr \n\t" ++ " lsr r0, #3 \n\t" ++ " bcs 1f \n\t" ++ " mrs r0, msp \n\t" ++ " b 2f \n\t" ++ "1: \n\t" ++ " mrs r0, psp \n\t" ++ "2: \n\t" ++ ++ //check for ARM mode ++ " ldr r1, [r0, #4 * 7] \n\t" //load pushed flags ++ " mov r3, #1 \n\t" ++ " lsl r3, #24 \n\t" //T flag ++ " tst r1, r3 \n\t" //check for T bit ++ " bne not_arm_mode \n\t" //if it is set, further testing to be done here ++ "is_arm_mode: \n\t" ++ " movs r1, #" STR(EXC_m0_CAUSE_BAD_CPU_MODE) " \n\t" ++ " b call_handler_no_pushed_regs \n\t" ++ ++ //check if re-entry ++ "not_arm_mode: \n\t" ++ " ldr r2, [r0, #4 * 7] \n\t" ++ " lsl r2, #32 - 6 \n\t" ++ " lsr r2, #32 - 6 \n\t" ++ " cmp r2, #4 \n\t" ++ " bne not_reentry \n\t" ++ ++ //is re-entry from our own code only (should only be memory access issues) ++ //our memory accesses are crafted specially and we detect that ++ //we also assume here that Pc in our exclusive mode is valid ++ " ldr r3, [r0, #4 * 6] \n\t" //exc.PC ++ " ldrh r1, [r3, #2] \n\t" //should be a "B . +4" = 0xe000 ++ " mov r2, #0xe0 \n\t" ++ " lsl r2, #8 \n\t" ++ " cmp r1, r2 \n\t" ++ " bne bug_in_classifier \n\t" ++ " ldrh r2, [r3, #4] \n\t" //should be a "MOV R?, #??" = 0x46f6 ++ " lsr r2, #11 \n\t" ++ " cmp r2, #0x04 \n\t" ++ " bne bug_in_classifier \n\t" ++ ++ //re-entry from our own code - skip the load/store and the next instruction ++ " add r3, #4 \n\t" ++ " str r3, [r0, #4 * 6] \n\t" //exc.PC ++ " bx lr \n\t" ++ ++ //re-entry from our code but not a specially-instrumented load/store instr ++ "bug_in_classifier: \n\t" ++ " mov r1, #" STR(EXC_m0_CAUSE_CLASSIFIER_ERROR) " \n\t" ++ " b call_handler_no_pushed_regs \n\t" ++ ++ "access_fail_plus_4: \n\t" ++ " add r2, #2 \n\t" ++ "access_fail_plus_2: \n\t" ++ " add r2, #2 \n\t" ++ "access_fail: \n\t" //expects address in r2 ++ " mov r1, #" STR(EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL) "\n\t" ++ "call_handler_no_pushed_regs: \n\t" ++ " push {r4-r7, lr} \n\t" ++ " mov r4, r8 \n\t" ++ " mov r5, r9 \n\t" ++ " mov r6, r10 \n\t" ++ " mov r7, r11 \n\t" ++ " push {r4-r7} \n\t" ++ " mov r3, sp \n\t" ++ " bl faultHandlerWithExcFrame \n\t" ++ " pop {r4-r7} \n\t" ++ " mov r8, r4 \n\t" ++ " mov r9, r5 \n\t" ++ " mov r10, r6 \n\t" ++ " mov r11, r7 \n\t" ++ " pop {r4-r7, pc} \n\t" ++ ++ //not re-entry, r3 still has T bit ++ "not_reentry: \n\t" ++ //further checks will take place in another context - go there now ++ " ldr r2, [r0, #4 * 6] \n\t" //exc.PC ++ " adr r1, check_more_in_custom_mode \n\t" //stashed PC ++ " add r3, r3, #4 \n\t" //desired SR ++ " push {r1, r3} \n\t" ++ " mov r1, r12 \n\t" //stashed r12 ++ " push {r1, lr} \n\t" //and stashed lr ++ " push {r0-r3} \n\t" //stashed r0..r3 ++ " mov r2, #0x0e \n\t" ++ " mvn r2, r2 \n\t" //get an lr to go to (handler mode, main stack) -> 0xfffffff1 ++ " bx r2 \n\t" ++ ++ //check more in a safer space (r0 = exc; r2 = exc->pc; lr & sp set for direct return to original exc cause) ++ ++ ".balign 4 \n\t" ++ "check_more_in_custom_mode: \n\t" ++ " cmp r0, r0 \n\t" //set Z ++ " ldrh r1, [r2] \n\t" //load instr, on failure, branch is skipped too ++ " b 1f \n\t" ++ " mov r1, #1 \n\t" //clear Z. only executed on access failure ++ "1: \n\t" //Z flag is clear on failure, due to the mov above, which is skipped on a succesful read ++ " bne access_fail \n\t" ++ " lsr r3, r1, #11 \n\t" ++ " cmp r3, #0x1C \n\t" ++ " bls instr_is_16bits_long \n\t" ++ ++ //let's read the second half of a 32-bit instr ++ "instr_is_32_bits_long: \n\t" ++ " mov r12, r2 \n\t" //save exc.pc ++ " cmp r0, r0 \n\t" //set Z ++ " ldrh r2, [r2, #2] \n\t" //load instr part 2, on failure, branch is skipped too ++ " b 1f \n\t" ++ " mov r1, #1 \n\t" //clear Z. only executed on access failure ++ "1: \n\t" //Z flag is clear on failure, due to the mov above, which is skipped on a succesful read ++ " bne access_fail_plus_2 \n\t" ++ ++ //32-bit instr. C-M0 has none that are valid AND can cause an exception, so this is definitely an UNDEF INSTR case ++ " mov r1, #" STR(EXC_m0_CAUSE_UNDEFINSTR32) " \n\t" ++ " b call_handler_no_pushed_regs \n\t" ++ ++ //it was a 16 bit instr. instr is in r1, pc is in r2 ++ "instr_is_16bits_long: \n\t" ++ //more triage will be done in C (r0 = excFrame, r1 = instr16, r2 = pc) ++ " push {r4-r7, lr} \n\t" ++ " mov r4, r8 \n\t" ++ " mov r5, r9 \n\t" ++ " mov r6, r10 \n\t" ++ " mov r7, r11 \n\t" ++ " push {r4-r7} \n\t" ++ " mov r2, sp \n\t" ++ " bl analyzeInstr16 \n\t" ++ " pop {r4-r7} \n\t" ++ " mov r8, r4 \n\t" ++ " mov r9, r5 \n\t" ++ " mov r10, r6 \n\t" ++ " mov r11, r7 \n\t" ++ " pop {r4-r7, pc} \n\t" ++ ".ltorg \n\t" ++ : ++ : ++ : "cc", "memory", "r0", "r1", "r2", "r3", "r12" //yes gcc needs this list... ++ ); ++} ++ ++ ++ ++ ++ +diff --git a/watch-library/shared/watch/m0FaultDispatch.h b/watch-library/shared/watch/m0FaultDispatch.h +new file mode 100644 +index 0000000..207f5d4 +--- /dev/null ++++ b/watch-library/shared/watch/m0FaultDispatch.h +@@ -0,0 +1,38 @@ ++#ifndef _M0_FAULT_DISPATCH_ ++#define _M0_FAULT_DISPATCH_ ++ ++#include ++ ++ ++#define EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL 1 //address provided ++#define EXC_m0_CAUSE_MEM_WRITE_ACCESS_FAIL 2 //address provided ++#define EXC_m0_CAUSE_BAD_CPU_MODE 3 //arm mode entered ++#define EXC_m0_CAUSE_DATA_UNALIGNED 4 //addres provided ++#define EXC_m0_CAUSE_UNDEFINSTR16 5 ++#define EXC_m0_CAUSE_UNDEFINSTR32 6 ++#define EXC_m0_CAUSE_BKPT_HIT 7 ++#define EXC_m0_CAUSE_CLASSIFIER_ERROR 8 //classifier itself crashed ++#define EXC_m0_CAUSE_UNCLASSIFIABLE 9 //classification failed ++ ++struct CortexExcFrame { ++ union { ++ struct { ++ uint32_t r0, r1, r2, r3; ++ }; ++ uint32_t r0_r3[3]; ++ }; ++ uint32_t r12, lr, pc, sr; ++}; ++ ++struct CortexPushedRegs { //when we push regs, we push them in this order. here for unification ++ uint32_t regs8_11[4]; ++ uint32_t regs4_7[4]; ++}; ++ ++//fault handling code, cause is EXC_m0_CAUSE_, extraData is usually an address. both unused on C-M3 ++void faultHandlerWithExcFrame(struct CortexExcFrame *exc, uint32_t cause, uint32_t extraData, struct CortexPushedRegs *pushedRegs); ++ ++ ++ ++ ++#endif diff --git a/main/fix-serial.patch b/main/fix-serial.patch index 80b11c5..4c06ffa 100644 --- a/main/fix-serial.patch +++ b/main/fix-serial.patch @@ -1,97 +1,64 @@ diff --git a/movement/filesystem.c b/movement/filesystem.c -index 97e3545..d3c2f83 100644 +index 9df0a8d..ac117af 100644 --- a/movement/filesystem.c +++ b/movement/filesystem.c -@@ -275,6 +275,10 @@ void filesystem_process_command(char *line) { - filesystem_append_file(filename, "\n", 1); - } - free(text); -+ } else if (strcmp(command, "format") == 0) { -+ lfs_unmount(&lfs); -+ lfs_format(&lfs, &cfg); -+ lfs_mount(&lfs, &cfg); - } else { - printf("%s: command not found\n", command); - } -diff --git a/movement/movement.c b/movement/movement.c -index 825f130..c7066f7 100644 ---- a/movement/movement.c -+++ b/movement/movement.c -@@ -547,7 +547,29 @@ bool app_loop(void) { - tx = ""; - }); - #else -- read(0, line, 256); -+#if 0 -+ read(0, &line[ll], sizeof(line)-ll); -+#else -+ // JMM yuck just yuck, we do our best to patch this mess up -+ { -+ static char buf[sizeof(line) - 1]; -+ static int bl; //again really signed? -+ int red; +@@ -280,3 +280,13 @@ int filesystem_cmd_echo(int argc, char *argv[]) { + return 0; + } + ++int filesystem_cmd_format(int argc, char *argv[]) { ++ (void) argc; ++ (void) argv; + -+ red = read(0, &buf[bl], sizeof(buf) - 1); ++ lfs_unmount(&lfs); ++ lfs_format(&lfs, &cfg); ++ lfs_mount(&lfs, &cfg); + -+ if (red > 0) { -+ write(0, &buf[bl], red); -+ bl += red; -+ if (buf[bl-1] == '\r') { -+ for (red = 0; red < bl; ++red) -+ line[red] = buf[red] == '\r' ? '\n' : buf[red]; -+ //memcpy(line,buf,bl-1); -+ bl = 0; -+ } -+ } -+ } -+#endif - #endif - if (strlen(line)) filesystem_process_command(line); - } -diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c -index cd607b8..5081fdb 100644 ---- a/watch-library/hardware/watch/watch_private.c -+++ b/watch-library/hardware/watch/watch_private.c -@@ -246,9 +246,18 @@ void _watch_enable_usb(void) { - // this function ends up getting called by printf to log stuff to the USB console. - int _write(int file, char *ptr, int len) { - (void)file; -+ int i; //it's 2023 boys and girls you can use size_t and ssize_t - if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) { -- tud_cdc_n_write(0, (void const*)ptr, len); -- tud_cdc_n_write_flush(0); -+ //tud_cdc_n_write(0, (void const*)ptr, len); -+ for (i = 0; i < len; ++i) -+ { -+ if (ptr[i]=='\n') { -+ tud_cdc_n_write(0, (void const*)"\r\n", 2); -+ } else { -+ tud_cdc_n_write(0, (void const*)&ptr[i], 1); -+ } -+ tud_cdc_n_write_flush(0); -+ } - return len; - } ++ return 0; ++} +diff --git a/movement/filesystem.h b/movement/filesystem.h +index fa3d9d1..ee13cba 100644 +--- a/movement/filesystem.h ++++ b/movement/filesystem.h +@@ -101,5 +101,6 @@ int filesystem_cmd_cat(int argc, char *argv[]); + int filesystem_cmd_df(int argc, char *argv[]); + int filesystem_cmd_rm(int argc, char *argv[]); + int filesystem_cmd_echo(int argc, char *argv[]); ++int filesystem_cmd_format(int argc, char *argv[]); -@@ -262,6 +271,8 @@ int _read(int file, char *ptr, int len) { - int actual_length = strlen(buf); - if (actual_length) { - memcpy(ptr, buf, min(len, actual_length)); -+ //JMM yuckity yuck yuck -+ buf[0] = 0; - return actual_length; - } - return 0; -diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h -index 790f9a1..b280ae6 100644 ---- a/watch-library/shared/watch/watch.h -+++ b/watch-library/shared/watch/watch.h -@@ -95,5 +95,6 @@ void watch_reset_to_bootloader(void); - * @return The number of bytes read, or zero if no bytes were read. - */ - int read(int file, char *ptr, int len); -+int write(int file, char *ptr, int len); - --#endif /* WATCH_H_ */ -\ No newline at end of file -+#endif /* WATCH_H_ */ + #endif // FILESYSTEM_H_ +diff --git a/movement/shell_cmd_list.c b/movement/shell_cmd_list.c +index 0ea08a5..a5d2d01 100644 +--- a/movement/shell_cmd_list.c ++++ b/movement/shell_cmd_list.c +@@ -92,6 +92,13 @@ shell_command_t g_shell_commands[] = { + .max_args = 3, + .cb = filesystem_cmd_echo, + }, ++ { ++ .name = "format", ++ .help = "usage: format", ++ .min_args = 0, ++ .max_args = 0, ++ .cb = filesystem_cmd_format, ++ }, + { + .name = "stress", + .help = "test CDC write; usage: stress [LEN] [DELAY_MS]", +diff --git a/watch-library/hardware/watch/watch_private_cdc.c b/watch-library/hardware/watch/watch_private_cdc.c +index a961b5e..31e354d 100644 +--- a/watch-library/hardware/watch/watch_private_cdc.c ++++ b/watch-library/hardware/watch/watch_private_cdc.c +@@ -145,7 +145,11 @@ static void prv_handle_writes(void) { + prv_handle_reads(); + } + if (tud_cdc_write_available()) { +- tud_cdc_write(&s_write_buf[idx], 1); ++ if (s_write_buf[idx]=='\n') { ++ tud_cdc_write("\r\n", 2); ++ } else { ++ tud_cdc_write(&s_write_buf[idx], 1); ++ } + } + s_write_buf[idx] = 0; + s_write_buf_len--; diff --git a/main/metric.patch b/main/metric.patch index cbf0287..0ff3dc7 100644 --- a/main/metric.patch +++ b/main/metric.patch @@ -11,394 +11,26 @@ index 878b4a4..5c2cee7 100644 moon_phase_face, sunrise_sunset_face, diff --git a/movement/make/Makefile b/movement/make/Makefile -index db08929..e131bbb 100644 +index 576822c..cf8850a 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile -@@ -120,6 +120,7 @@ SRCS += \ - ../watch_faces/complication/flashlight_face.c \ - ../watch_faces/clock/decimal_time_face.c \ - ../watch_faces/clock/wyoscan_face.c \ +@@ -131,6 +131,7 @@ SRCS += \ + ../watch_faces/clock/minute_repeater_decimal_face.c \ + ../watch_faces/complication/tuning_tones_face.c \ + ../watch_faces/complication/kitchen_conversions_face.c \ + ../watch_faces/complication/metric_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h -index ff34c06..94316ce 100644 +index 3557110..17fe809 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h -@@ -95,6 +95,7 @@ - #include "flashlight_face.h" - #include "decimal_time_face.h" - #include "wyoscan_face.h" +@@ -104,6 +104,7 @@ + #include "minute_repeater_decimal_face.h" + #include "tuning_tones_face.h" + #include "kitchen_conversions_face.h" +#include "metric_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ -diff --git a/movement/watch_faces/complication/equinox.c b/movement/watch_faces/complication/equinox.c -new file mode 100644 -index 0000000..e8d862e ---- /dev/null -+++ b/movement/watch_faces/complication/equinox.c -@@ -0,0 +1,101 @@ -+#define D2R(a) ((a)*(M_PI/180.)) -+ -+ -+ -+ -+// shamelessly stolen from Meeus Astronmical Algorithms Chapter 27 table 27.B -+// chapter 25 is probably more tractable, but this is easier to code up -+ -+# if 0 -+static double mean_vernal_equinox (unsigned year) -+{ -+ double y = year; -+ y -= 2000; -+ y *= 0.001; -+ return 2451623.80984 + 365242.37404 * y + 0.05169 * (y * y) - 0.00411 * (y * y * y) - 0.00057 * (y * y * y * y); -+} -+ -+static double mean_summer_solstice (unsigned year) -+{ -+ double y = year; -+ y -= 2000; -+ y *= 0.001; -+ return 2451716.56767 + 365241.62603 * y + 0.00325 * (y * y) + 0.00888 * (y * y * y) - 0.00030 * (y * y * y * y); -+} -+#endif -+ -+static double mean_autumnal_equinox (unsigned year) -+{ -+ double y = year; -+ y -= 2000; -+ y *= 0.001; -+ return 2451810.21715 + 365242.01767 * y - 0.11575 * (y * y) + 0.00337 * (y * y * y) + 0.00078 * (y * y * y * y); -+} -+ -+#if 0 -+static double mean_winter_solstice (unsigned year) -+{ -+ double y = year; -+ y -= 2000; -+ y *= 0.001; -+ return 2451900.05952 + 365242.74049 * y - 0.06223 * (y * y) - 0.00823 * (y * y * y) + 0.00032 * (y * y * y * y); -+} -+#endif -+ -+static double orbital_periodic_terms (double t) -+{ -+#define N 24 -+ const double A[N] = {485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45, -+ 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8 -+ }; -+ const double B[N] = {D2R (324.96), D2R (337.23), D2R (342.08), D2R (27.85), -+ D2R (73.14), D2R (171.52), D2R (222.54), D2R (296.72), -+ D2R (243.58), D2R (119.81), D2R (297.17), D2R (21.02), -+ D2R (247.54), D2R (325.15), D2R (60.93), D2R (155.12), -+ D2R (288.79), D2R (198.04), D2R (199.76), D2R (95.39), -+ D2R (287.11), D2R (320.81), D2R (227.73), D2R (15.45) -+ }; -+ const double C[N] = {D2R (1934.136), D2R (32964.467), D2R (20.186), -+ D2R (445267.112), D2R (45036.886), D2R (22518.443), -+ D2R (65928.934), D2R (3034.906), D2R (9037.513), -+ D2R (33718.147), D2R (150.678), D2R (2281.226), -+ D2R (29929.562), D2R (31555.956), D2R (4443.417), -+ D2R (67555.328), D2R (4562.452), D2R (62894.029), -+ D2R (31436.921), D2R (14577.848), D2R (31931.756), -+ D2R (34777.259), D2R (1222.114), D2R (16859.074) -+ }; -+ double s = 0; -+ unsigned i; -+ -+ for (i = 0; i < N; ++i) -+ s += A[i] * cos (B[i] + (C[i] * t)); -+ -+ return s; -+} -+ -+ -+static double mean_to_real (double j0) -+{ -+ -+ double t = (j0 - 2451545.) / 36525.; -+ double w = D2R ((35999.373 * t) - 2.47); -+ double dl = 1 + 0.0334 * cos (w) + 0.0007 * cos (2. * w); -+ double s = orbital_periodic_terms (t); -+ -+#if 0 -+ printf ("j0=%.6f\r\n", j0); -+ printf ("t=%.6f\r\n", t); -+ printf ("w=%.6f\r\n", w); -+ printf ("dl=%.6f\r\n", dl); -+ printf ("s=%.6f\r\n", s); -+#endif -+ -+ return j0 + ((0.00001 * s) / dl); -+} -+ -+static double autumnal_equinox (unsigned y) -+{ -+ return mean_to_real (mean_autumnal_equinox (y)); -+ // return mean_to_real (mean_summer_solstice (y)); -+} -+ -diff --git a/movement/watch_faces/complication/metric_face.c b/movement/watch_faces/complication/metric_face.c -new file mode 100644 -index 0000000..9eaafbf ---- /dev/null -+++ b/movement/watch_faces/complication/metric_face.c -@@ -0,0 +1,198 @@ -+#include -+#include -+#include -+#include "watch.h" -+#include "watch_utility.h" -+#include "vsop87a_micro.h" // smaller size, less accurate -+#include "vsop87a_milli.h" -+#include "astrolib.h" -+#include "metric_face.h" -+ -+#define RATIO 8 -+ -+#include "equinox.c" -+ -+static const char *days[10] = -+ { "Pr", "Du", "Tr", "Qa", "Qi", "SH", "Sp", "Oc", "No", "De" }; -+ -+ -+static uint32_t -+jd_to_timestamp (double tjd) -+{ -+ tjd -= 2440587.5; -+ tjd *= 86400.; -+ return (uint32_t) floor (tjd); -+} -+ -+static uint32_t -+timestamp_start_of_year (int y) -+{ -+ double julian_equinox = autumnal_equinox (y); -+ julian_equinox += (2.3372305555 / 360); /* Offset of paris meridian from greenwich meridian */ -+ return jd_to_timestamp (julian_equinox); -+} -+ -+static int -+days_since_equinox (watch_date_time date_time) -+{ -+ uint32_t now = watch_utility_date_time_to_unix_time (date_time, 0); -+ uint32_t start_of_year = -+ timestamp_start_of_year (date_time.unit.year + 2020); -+ -+ start_of_year/=86400; -+ start_of_year*=86400; -+ -+ if (start_of_year > now) -+ start_of_year = -+ timestamp_start_of_year (date_time.unit.year + 2020 - 1); -+ -+ now -= start_of_year; -+ -+ now /= 86400; -+ -+ return (int) now; -+} -+ -+static void -+calculate_metric_date (metric_state_t * state, watch_date_time date_time) -+{ -+ int dse = days_since_equinox (date_time); -+ -+ state->m_day = (dse % 30) + 1; -+ state->m_wday = (dse % 10); -+ state->c_day = date_time.unit.day; -+ -+} -+ -+ -+static uint32_t -+date_time_to_ds (watch_date_time dt) -+{ -+ uint32_t ret; -+ ret = dt.unit.hour; -+ ret *= 60; -+ ret += dt.unit.minute; -+ ret *= 60; -+ ret += dt.unit.second; -+ return ret; -+} -+ -+static void -+_metric_face_sync (movement_settings_t * settings, metric_state_t * state) -+{ -+ watch_date_time date_time = watch_rtc_get_date_time (); -+ -+ (void) settings; -+ -+ if (date_time.unit.day != state->c_day) -+ calculate_metric_date (state, date_time); -+ -+ state->dt = date_time_to_ds (date_time) * RATIO; -+ state->resync_at = state->dt * (3600 * RATIO); -+ if (state->resync_at > (86400 * RATIO)) -+ state->resync_at = (86400 * RATIO); -+ -+} -+ -+static void -+_metric_face_update (movement_event_t event, movement_settings_t * settings, -+ metric_state_t * state, int show_secs) -+{ -+ uint64_t v; -+ char buf[14]; -+ -+ (void) event; -+ -+ if (state->dt >= state->resync_at) -+ _metric_face_sync (settings, state); -+ -+ v = state->dt; -+ v *= (uint64_t) 100000; -+ v /= (uint64_t) (RATIO * 86400); -+ -+ -+#if 0 -+ { -+ watch_date_time date_time = watch_rtc_get_date_time (); -+ printf ("%d %d\n", state->dt, date_time_to_ds (date_time) * RATIO); -+ } -+#endif -+ -+ -+ sprintf (buf, "%s%2d %05d", days[state->m_wday], (int) state->m_day, -+ (int) v); -+ -+ if (!show_secs) -+ { -+ buf[8] = ' '; -+ buf[9] = ' '; -+ } -+ -+ watch_display_string (buf, 0); -+} -+ -+void -+metric_face_setup (movement_settings_t * settings, uint8_t watch_face_index, -+ void **context_ptr) -+{ -+ (void) settings; -+ (void) watch_face_index; -+ if (*context_ptr == NULL) -+ { -+ *context_ptr = malloc (sizeof (metric_state_t)); -+ memset (*context_ptr, 0, sizeof (metric_state_t)); -+ } -+} -+ -+void -+metric_face_activate (movement_settings_t * settings, void *context) -+{ -+ metric_state_t *state = (metric_state_t *) context; -+ (void) settings; -+ movement_request_tick_frequency (RATIO); -+ state->resync_at = 0; -+} -+ -+bool -+metric_face_loop (movement_event_t event, movement_settings_t * settings, -+ void *context) -+{ -+ (void) settings; -+ metric_state_t *state = (metric_state_t *) context; -+ -+ switch (event.event_type) -+ { -+ case EVENT_ACTIVATE: -+ watch_set_colon (); -+ /*fall through */ -+ case EVENT_TICK: -+ state->dt++; -+ _metric_face_update (event, settings, state, 1); -+ break; -+ case EVENT_LOW_ENERGY_UPDATE: -+ state->dt += RATIO * 60; -+ _metric_face_update (event, settings, state, 0); -+ break; -+ case EVENT_ALARM_BUTTON_UP: -+ break; -+ case EVENT_ALARM_LONG_PRESS: -+ break; -+ case EVENT_TIMEOUT: -+ state->resync_at = 0; -+ break; -+ default: -+ movement_default_loop_handler (event, settings); -+ break; -+ } -+ -+ return true; -+} -+ -+void -+metric_face_resign (movement_settings_t * settings, void *context) -+{ -+ (void) settings; -+ metric_state_t *state = (metric_state_t *) context; -+ state->resync_at = 0; -+ state->c_day = 0; -+} -diff --git a/movement/watch_faces/complication/metric_face.h b/movement/watch_faces/complication/metric_face.h -new file mode 100644 -index 0000000..1968e38 ---- /dev/null -+++ b/movement/watch_faces/complication/metric_face.h -@@ -0,0 +1,51 @@ -+/* -+ * MIT License -+ * -+ * Copyright (c) 2022 Joey Castillo -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in all -+ * copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -+ * SOFTWARE. -+ */ -+ -+#ifndef METRIC_FACE_H_ -+#define METRIC_FACE_H_ -+ -+#include "movement.h" -+ -+typedef struct { -+ uint32_t dt; -+ uint32_t resync_at; -+ uint32_t m_day; -+ uint32_t m_wday; -+ uint32_t c_day; -+} metric_state_t; -+ -+void metric_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); -+void metric_face_activate(movement_settings_t *settings, void *context); -+bool metric_face_loop(movement_event_t event, movement_settings_t *settings, void *context); -+void metric_face_resign(movement_settings_t *settings, void *context); -+ -+#define metric_face ((const watch_face_t){ \ -+ metric_face_setup, \ -+ metric_face_activate, \ -+ metric_face_loop, \ -+ metric_face_resign, \ -+ NULL, \ -+}) -+ -+#endif // METRIC_FACE_H_ diff --git a/main/series b/main/series index e0d931c..aa3c0cd 100644 --- a/main/series +++ b/main/series @@ -5,4 +5,7 @@ fix-serial.patch metric.patch siderial.patch moon.patch +debug-compile.patch +set-time.patch +dont_set_time_if_no_debugger.patch endstop diff --git a/main/set-time.patch b/main/set-time.patch new file mode 100644 index 0000000..c7d479c --- /dev/null +++ b/main/set-time.patch @@ -0,0 +1,83 @@ +diff --git a/make.mk b/make.mk +index ee40100..5610c1f 100644 +--- a/make.mk ++++ b/make.mk +@@ -64,7 +64,7 @@ LDFLAGS += -mcpu=cortex-m0plus -mthumb + LDFLAGS += -Wl,--gc-sections + LDFLAGS += -Wl,--script=$(TOP)/watch-library/hardware/linker/saml22j18.ld + LDFLAGS += -Wl,--print-memory-usage +-LDFLAGS += -g ++LDFLAGS += -g -u set_time + + LIBS += -lm + +diff --git a/movement/make/Makefile b/movement/make/Makefile +index a336b85..5fa1322 100644 +--- a/movement/make/Makefile ++++ b/movement/make/Makefile +@@ -51,6 +51,7 @@ SRCS += \ + ../../littlefs/lfs.c \ + ../../littlefs/lfs_util.c \ + ../movement.c \ ++ ../set_time.c \ + ../filesystem.c \ + ../shell.c \ + ../shell_cmd_list.c \ +diff --git a/movement/movement.c b/movement/movement.c +index 230dbe5..8abd6e7 100644 +--- a/movement/movement.c ++++ b/movement/movement.c +@@ -412,6 +412,7 @@ void app_wake_from_backup(void) { + + void app_setup(void) { + watch_store_backup_data(movement_state.settings.reg, 0); ++ set_time(); + + static bool is_first_launch = true; + +diff --git a/movement/set_time.c b/movement/set_time.c +new file mode 100644 +index 0000000..27cea25 +--- /dev/null ++++ b/movement/set_time.c +@@ -0,0 +1,40 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "watch.h" ++#include "movement.h" ++#include "watch_utility.h" ++ ++#define SYS_TIME 0x11 ++ ++extern movement_state_t movement_state; ++ ++static inline int32_t ++get_tz_offset (movement_settings_t * settings) ++{ ++ return movement_timezone_offsets[settings->bit.time_zone] * 60; ++} ++ ++static int32_t ++sys_time (void) ++{ ++ int32_t value = SYS_TIME; ++ asm ("mov r0,%0; bkpt $0xab; mov %0,r0": "+r" (value): :"r0"); ++ return value; ++} ++ ++void __attribute__((used)) set_time (void) ++{ ++ int32_t t; ++ ++ t = sys_time (); ++ ++ watch_date_time wdt = watch_utility_date_time_from_unix_time (t, ++ get_tz_offset ++ (&movement_state. ++ settings)); ++ watch_rtc_set_date_time (wdt); ++} -- cgit v1.2.3