From 3f9f88e0935d9017c6c9c3c5aa3043f2cba56ab1 Mon Sep 17 00:00:00 2001 From: dallanwagz Date: Sun, 26 Apr 2026 02:13:37 -0600 Subject: [PATCH 1/2] Fix modifier key macros ([ctrl]p, [alt]x, etc.) in keymap parser The keymap parser stored modifier tokens as standalone bytes, misaligning the 2-byte {modifier, keycode} pairs that send_keys() reads with a fixed stride. Macros using bracketed modifier syntax produced wrong keystrokes - [ctrl]p sent LEFT_CTRL + LEFT_SHIFT + RIGHT_CTRL instead of Ctrl+P, and multi-modifier strings like [ctrl][shift]p silently dropped both modifiers. Fix introduces a pending_modifier accumulator in serialconsole.c that collects modifier tokens (one or many) and applies them atomically to the next keystroke. Verified end-to-end on real DEF CON 29 badge hardware (SAMD21G16B) via the serial console: - Single-modifier macro [ctrl]p accepted and parsed - Multi-modifier macros [ctrl][shift]p and [gui][shift]s accepted - [gui][shift]s triggers Snipping Tool on the host (Win+Shift+S), confirming both modifiers reach the OS atomically Also rebuilds Firmware/Compiled/DC29.uf2 - the previously committed binary was built with the wrong linker script (ORIGIN=0x0) and was rejected by the badge's UF2 bootloader. See VALIDATION_NOTES.md for the build configuration and a bootloader-mode gotcha worth knowing before testing. --- Firmware/Compiled/DC29.uf2 | Bin 0 -> 113664 bytes Firmware/Source/DC29/src/serialconsole.c | 109 +++++++++++++++-------- VALIDATION_NOTES.md | 69 ++++++++++++++ 3 files changed, 139 insertions(+), 39 deletions(-) create mode 100644 Firmware/Compiled/DC29.uf2 create mode 100644 VALIDATION_NOTES.md diff --git a/Firmware/Compiled/DC29.uf2 b/Firmware/Compiled/DC29.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..ff2780f54fe37fb0f9a142d1266eea2f825055a5 GIT binary patch literal 113664 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 a7-=i=vmAE8ZWjcm5kL9A`^qUm`2QC 127){ - newKeymap[newKeymapCounter] = HID_MODIFIER_LEFT_SHIFT; + newKeymap[newKeymapCounter] = HID_MODIFIER_LEFT_SHIFT | pending_modifier; newKeymapCounter++; newKeymap[newKeymapCounter] = ascii_to_hid[newKeystroke[x]]-128; newKeymapCounter++; } else { - newKeymap[newKeymapCounter] = 0; + newKeymap[newKeymapCounter] = pending_modifier; newKeymapCounter++; newKeymap[newKeymapCounter] = ascii_to_hid[newKeystroke[x]]; newKeymapCounter++; } + pending_modifier = 0; } } if(newKeymapCounter + keymaplength < 231){ diff --git a/VALIDATION_NOTES.md b/VALIDATION_NOTES.md new file mode 100644 index 0000000..b60ac85 --- /dev/null +++ b/VALIDATION_NOTES.md @@ -0,0 +1,69 @@ +# Validation Notes — Issue #5 Modifier Key Fix + +End-to-end verification of the modifier key parser fix on real DEF CON 29 badge +hardware (SAMD21G16B). This document captures what was tested, the build +configuration that produced working firmware, and gotchas encountered during +the test cycle. + +## What Was Verified + +Built the fix on Windows with Microchip Studio 7.0, flashed via UF2, and +exercised the keymap parser through the badge's serial console: + +| Macro Set Via Serial | Parser Result | What It Sends | Hardware Behavior | +|---|---|---|---| +| `[ctrl]p` (single modifier) | Accepted, returned to main menu | Ctrl+P | (set successfully; per-button physical test pending) | +| `[ctrl][shift]p` (multi-modifier — the bug case) | Accepted, returned to main menu | Ctrl+Shift+P | (set successfully) | +| `[gui][shift]s` (multi-modifier with system-visible effect) | Accepted | Win+Shift+S | Snipping Tool overlay appears on host | + +The acceptance of multi-modifier macros without falling through to "Invalid +Input" is the parser-level confirmation. Pre-fix, multi-modifier strings +silently produced wrong modifier bytes (e.g., `[ctrl]p` → LEFT_CTRL + +LEFT_SHIFT + RIGHT_CTRL). + +## Build Configuration That Produced Working Firmware + +The previous `Firmware/Compiled/DC29.uf2` committed in this branch was built +with the wrong linker script (ORIGIN=0x0) and was rejected by the badge's UF2 +bootloader, which expects the application at 0x2000. The replacement `.uf2` +in this commit was built with: + +- **Device defines**: `__SAMD21G16B__` only (do **not** add `__SAMD21J18A__` — + with both defined, `samd21.h`'s `#elif` chain selects `samd21j18a.h`, which + doesn't define `NVMCTRL_RWW_EEPROM_SIZE` and breaks `nvm.c`). +- **Linker script**: `src/samd21g16b_flash.ld` (this is the in-tree custom + script with `ORIGIN = 0x00000000+0x2000`, not the ASF default at + `src/ASF/sam0/utils/linker_scripts/samd21/gcc/samd21j18a_flash.ld` which + has ORIGIN=0x0). +- **Configuration**: Release (Debug doesn't fit in 56KB). +- **Toolchain**: arm-none-eabi-gcc 6.3.1 from Microchip Studio. + +The Microchip Studio `.cproj` was edited so that **both** the Release +preprocessor symbols and Release linker flags reflect the above. Note that the +cproj contains two `` entries inside +the Release ``; both must point at `samd21g16b_flash.ld`. UF2 +conversion was done from `DC29.hex` using a custom PowerShell port of +`uf2conv.py` (Python isn't required on the build machine). + +## Validation Gotcha — Button-During-Reboot Trap + +When flashing, holding the bottom-right button across the bootloader's reboot +keeps the badge in DFU mode after the new firmware is written. From the host +this looks identical to a firmware crash: drive stays mounted, top-left LED +keeps pulsing, no COM port enumerates. We spent significant time chasing +imaginary firmware crashes (comparing vector tables, checking memory layout, +inspecting startup code) before unplug-and-replug **without holding any +button** booted the firmware cleanly. + +**Recommended flash workflow:** +1. Hold bottom-right button. +2. Plug in USB. +3. **Release the button as soon as the bootloader drive (D:, E:, etc.) appears.** +4. Drag/copy the `.uf2` onto the drive. +5. When the drive disappears, badge has rebooted into the new firmware. Verify + a CDC serial port appears (e.g., `COM3` on Windows, `/dev/ttyACM*` on + Linux/Mac). + +If the firmware appears not to boot after a flash, **before** debugging the +build, unplug and replug without touching any button. The firmware is almost +certainly fine. From 4abb96029449a1365521f28ba9faac2585d53166 Mon Sep 17 00:00:00 2001 From: dallanwagz Date: Mon, 27 Apr 2026 23:07:52 -0600 Subject: [PATCH 2/2] Fix [eject] parser typo and chained media-key macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related parser bugs in serialconsole.c, same family as the issue #5 modifier-key fix in the previous commit: 1. [eject] was completely non-functional. The check `newKeystroke[x+5] != ']'` had the operator inverted — for any valid `[eject]` token, x+5 IS `]`, so the branch never fired and the token fell through to ascii parsing. 2. Eight media tokens ([play], [next], [back], [stop], [eject], [mute], [vol+], [vol-]) carried a stale `newKeystroke[x+N] != '['` lookahead that was the same broken-by-design pattern the issue #5 fix removed from the modifier branches. Effect: chaining adjacent media tokens in one macro (e.g. `[play][next]`) silently dropped the first token, because the char after its `]` is `[` (start of the next token), so the branch was skipped and gibberish ascii ended up in the keymap. Removing the lookahead makes media tokens behave like the modifier and F-key tokens already do: each token parses on its own, the loop advances past it, the next token parses on the next iteration. No new state, no new helpers — these are the minimal corrections that make the parser internally consistent. --- Firmware/Source/DC29/src/serialconsole.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Firmware/Source/DC29/src/serialconsole.c b/Firmware/Source/DC29/src/serialconsole.c index 6dd3864..7f5188a 100644 --- a/Firmware/Source/DC29/src/serialconsole.c +++ b/Firmware/Source/DC29/src/serialconsole.c @@ -481,56 +481,56 @@ void updateSerialConsole(void){ } else if(newKeystroke[x] == 'g' && newKeystroke[x+1] == 'u' && newKeystroke[x+2] == 'i' && newKeystroke[x+3] == ']'){ pending_modifier |= HID_MODIFIER_LEFT_UI; x += 3; - } else if(newKeystroke[x] == 'p' && newKeystroke[x+1] == 'l' && newKeystroke[x+2] == 'a' && newKeystroke[x+3] == 'y' && newKeystroke[x+4] == ']' && newKeystroke[x+5] != '['){ + } else if(newKeystroke[x] == 'p' && newKeystroke[x+1] == 'l' && newKeystroke[x+2] == 'a' && newKeystroke[x+3] == 'y' && newKeystroke[x+4] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_PLAY; newKeymapCounter ++; pending_modifier = 0; x += 4; - } else if(newKeystroke[x] == 'n' && newKeystroke[x+1] == 'e' && newKeystroke[x+2] == 'x' && newKeystroke[x+3] == 't' && newKeystroke[x+4] == ']' && newKeystroke[x+5] != '['){ + } else if(newKeystroke[x] == 'n' && newKeystroke[x+1] == 'e' && newKeystroke[x+2] == 'x' && newKeystroke[x+3] == 't' && newKeystroke[x+4] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_NEXT; newKeymapCounter ++; pending_modifier = 0; x += 4; - } else if(newKeystroke[x] == 'b' && newKeystroke[x+1] == 'a' && newKeystroke[x+2] == 'c' && newKeystroke[x+3] == 'k' && newKeystroke[x+4] == ']' && newKeystroke[x+5] != '['){ + } else if(newKeystroke[x] == 'b' && newKeystroke[x+1] == 'a' && newKeystroke[x+2] == 'c' && newKeystroke[x+3] == 'k' && newKeystroke[x+4] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_BACK; newKeymapCounter ++; pending_modifier = 0; x += 4; - } else if(newKeystroke[x] == 's' && newKeystroke[x+1] == 't' && newKeystroke[x+2] == 'o' && newKeystroke[x+3] == 'p' && newKeystroke[x+4] == ']' && newKeystroke[x+5] != '['){ + } else if(newKeystroke[x] == 's' && newKeystroke[x+1] == 't' && newKeystroke[x+2] == 'o' && newKeystroke[x+3] == 'p' && newKeystroke[x+4] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_STOP; newKeymapCounter ++; pending_modifier = 0; x += 4; - } else if(newKeystroke[x] == 'e' && newKeystroke[x+1] == 'j' && newKeystroke[x+2] == 'e' && newKeystroke[x+3] == 'c' && newKeystroke[x+4] == 't' && newKeystroke[x+5] != ']' && newKeystroke[x+6] != '['){ + } else if(newKeystroke[x] == 'e' && newKeystroke[x+1] == 'j' && newKeystroke[x+2] == 'e' && newKeystroke[x+3] == 'c' && newKeystroke[x+4] == 't' && newKeystroke[x+5] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_EJECT; newKeymapCounter ++; pending_modifier = 0; x += 5; - } else if(newKeystroke[x] == 'm' && newKeystroke[x+1] == 'u' && newKeystroke[x+2] == 't' && newKeystroke[x+3] == 'e' && newKeystroke[x+4] == ']' && newKeystroke[x+5] != '['){ + } else if(newKeystroke[x] == 'm' && newKeystroke[x+1] == 'u' && newKeystroke[x+2] == 't' && newKeystroke[x+3] == 'e' && newKeystroke[x+4] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_MUTE; newKeymapCounter ++; pending_modifier = 0; x += 4; - } else if(newKeystroke[x] == 'v' && newKeystroke[x+1] == 'o' && newKeystroke[x+2] == 'l' && newKeystroke[x+3] == '+' && newKeystroke[x+4] == ']' && newKeystroke[x+5] != '['){ + } else if(newKeystroke[x] == 'v' && newKeystroke[x+1] == 'o' && newKeystroke[x+2] == 'l' && newKeystroke[x+3] == '+' && newKeystroke[x+4] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_VOL_PLUS; newKeymapCounter ++; pending_modifier = 0; x += 4; - } else if(newKeystroke[x] == 'v' && newKeystroke[x+1] == 'o' && newKeystroke[x+2] == 'l' && newKeystroke[x+3] == '-' && newKeystroke[x+4] == ']' && newKeystroke[x+5] != '['){ + } else if(newKeystroke[x] == 'v' && newKeystroke[x+1] == 'o' && newKeystroke[x+2] == 'l' && newKeystroke[x+3] == '-' && newKeystroke[x+4] == ']'){ newKeymap[newKeymapCounter] = 240;//media key identifier newKeymapCounter ++; newKeymap[newKeymapCounter] = HID_MEDIA_VOL_MINUS;