|
| 1 | +Add touch 2400 to request the bootloader's mass storage (UF2) mode |
| 2 | + |
| 3 | +**Target:** `adafruit/Adafruit_TinyUSB_Arduino`, base `master` |
| 4 | +**Branch:** `feature/touch2400-enter-uf2-dfu` — 4 files, +21/−0, no deletions |
| 5 | +**Status:** prepared, not submitted. Not yet verified on hardware (see the last section). |
| 6 | + |
| 7 | +## The problem |
| 8 | + |
| 9 | +A host that cannot reach the board physically has no way to ask for the mass storage side of the |
| 10 | +bootloader. Touch 1200 is the only remote trigger this library exposes, and on nRF52 it lands in |
| 11 | +serial-only DFU (CDC) — never the UF2 drive (CDC + MSC). |
| 12 | + |
| 13 | +That gap has a concrete consequence: **UF2 is the only route that can carry a bootloader update.** A |
| 14 | +DFU zip cannot, because that path requires an application manifest. So a remotely deployed nRF52 |
| 15 | +node — in our case reached over a USB/IP bridge, with nobody near the board — can have its |
| 16 | +application updated but never its bootloader, purely because the mass storage mode is unreachable |
| 17 | +without a physical double tap. |
| 18 | + |
| 19 | +## What this changes |
| 20 | + |
| 21 | +Three small additions and one nRF52 implementation: |
| 22 | + |
| 23 | +- `TinyUSB_Port_EnterMassStorageDFU()`, a new **optional** port hook, invoked when the host |
| 24 | + disconnects CDC at **2400** baud. |
| 25 | +- A **weak default** forwarding it to `TinyUSB_Port_EnterDFU()`. This follows the pattern already |
| 26 | + used in the same header for `TinyUSB_Device_Init`, `TinyUSB_Device_Task` and |
| 27 | + `TinyUSB_Device_FlushCDC`, so it is not a new mechanism here. |
| 28 | +- The nRF52 port implements it with `enterUf2Dfu()`, which the core already provides right next to |
| 29 | + `enterSerialDfu()` in `wiring.h`. Nothing new is invented; an existing entry point is exposed. |
| 30 | + |
| 31 | +**Touch 1200 is not modified on any port.** Existing tools, the Arduino/PlatformIO upload path, and |
| 32 | +every board's current behaviour are untouched. Ports whose bootloader has no distinct mass storage |
| 33 | +mode need no change at all — the weak default makes touch 2400 degrade to exactly what touch 1200 |
| 34 | +already does there. Of the five ports in the tree, only `nrf` gains a distinct behaviour: |
| 35 | + |
| 36 | +| port | `EnterDFU` | overrides the new hook | |
| 37 | +|---|---|---| |
| 38 | +| nrf | yes | **yes** — `enterUf2Dfu()` | |
| 39 | +| rp2040, samd, ch32, esp32 | yes | no — falls back via the weak default | |
| 40 | + |
| 41 | +## Why not simply change what touch 1200 does |
| 42 | + |
| 43 | +That was the first thing we tried, as a local patch: make `TinyUSB_Port_EnterDFU()` call |
| 44 | +`enterUf2Dfu()`. It works, and we rejected it for two reasons. |
| 45 | + |
| 46 | +**It changes the normal upload path for everybody.** The 1200-baud touch *is* how the IDE uploads, so |
| 47 | +every upload on every sketch built with the core would start mounting a removable drive. On Windows |
| 48 | +that means a drive letter, an Explorer window, and an indexer or antivirus scanning a freshly |
| 49 | +appeared FAT volume — while a serial DFU transfer is in flight. Nuisance at best, and not something |
| 50 | +to inflict on users who never asked for it. |
| 51 | + |
| 52 | +**It removes the choice.** With a second baud rate the *host* decides, per touch, which mode it wants. |
| 53 | +Nothing has to be rebuilt to switch intent, and a tool that only knows 1200 keeps working. |
| 54 | + |
| 55 | +For completeness, we also measured whether the mass storage interface interferes with a serial DFU |
| 56 | +transfer, since that would have been the stronger objection to either approach: it does not. A full |
| 57 | +serial DFU flash completed normally with MSC mounted — 28.33 s, 928 progress marks, `Device |
| 58 | +programmed.` on an nRF52840 already in UF2 mode. So the argument against changing the default is |
| 59 | +about nuisance, not breakage. |
| 60 | + |
| 61 | +## The choice of 2400, and its one hazard |
| 62 | + |
| 63 | +2400 is proposed because it is archaic enough to be unused for real data, which is the same reason |
| 64 | +1200 was chosen. The hazard is the same one 1200 already has: **open a serial monitor at 2400 baud |
| 65 | +and close it, and the device reboots into UF2 mode.** Recoverable (a reset returns to the |
| 66 | +application) but surprising. |
| 67 | + |
| 68 | +If you would rather avoid that, the patch is trivially adaptable and we have no attachment to the |
| 69 | +value: |
| 70 | + |
| 71 | +- a different rate, or a deliberately odd one such as 1201, which no terminal offers by default; |
| 72 | +- or gate the whole thing behind a build flag, so only firmware that wants it responds at all. |
| 73 | + |
| 74 | +Say which you prefer and it will be changed — the mechanism is the point, not the number. |
| 75 | + |
| 76 | +## What is and is not verified |
| 77 | + |
| 78 | +Verified: the patch applies cleanly to `master`, the weak-default pattern matches existing usage in |
| 79 | +the same header, and `enterUf2Dfu()` is already declared in the nRF52 core's public `wiring.h` |
| 80 | +alongside `enterSerialDfu()`, so the nRF52 implementation compiles against what the core already |
| 81 | +exposes. |
| 82 | + |
| 83 | +The 2400 path has **not** yet been exercised on hardware. What has been measured, on an nRF52840 |
| 84 | +ProMicro and a XIAO nRF52840 Sense, is the behaviour on both sides of it: a 1200-baud touch reaching |
| 85 | +serial-only DFU (CDC, no mass storage), and a device in UF2 mode (CDC + MSC) accepting a full serial |
| 86 | +DFU flash. Happy to run the 2400 path on both boards before you spend time on this — say so and the |
| 87 | +result will be posted here. |
0 commit comments