webserial-flasher 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/dist/autoDetect.d.ts +24 -0
  4. package/dist/autoDetect.d.ts.map +1 -0
  5. package/dist/autoDetect.js +66 -0
  6. package/dist/autoDetect.js.map +1 -0
  7. package/dist/boards/database.d.ts +17 -0
  8. package/dist/boards/database.d.ts.map +1 -0
  9. package/dist/boards/database.js +957 -0
  10. package/dist/boards/database.js.map +1 -0
  11. package/dist/core/constants.d.ts +44 -0
  12. package/dist/core/constants.d.ts.map +1 -0
  13. package/dist/core/constants.js +56 -0
  14. package/dist/core/constants.js.map +1 -0
  15. package/dist/core/errors.d.ts +45 -0
  16. package/dist/core/errors.d.ts.map +1 -0
  17. package/dist/core/errors.js +92 -0
  18. package/dist/core/errors.js.map +1 -0
  19. package/dist/core/types.d.ts +138 -0
  20. package/dist/core/types.d.ts.map +1 -0
  21. package/dist/core/types.js +3 -0
  22. package/dist/core/types.js.map +1 -0
  23. package/dist/index.d.ts +24 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +25 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/protocol/avr109/programmer.d.ts +78 -0
  28. package/dist/protocol/avr109/programmer.d.ts.map +1 -0
  29. package/dist/protocol/avr109/programmer.js +324 -0
  30. package/dist/protocol/avr109/programmer.js.map +1 -0
  31. package/dist/protocol/hexParser.d.ts +12 -0
  32. package/dist/protocol/hexParser.d.ts.map +1 -0
  33. package/dist/protocol/hexParser.js +133 -0
  34. package/dist/protocol/hexParser.js.map +1 -0
  35. package/dist/protocol/picoboot/constants.d.ts +65 -0
  36. package/dist/protocol/picoboot/constants.d.ts.map +1 -0
  37. package/dist/protocol/picoboot/constants.js +80 -0
  38. package/dist/protocol/picoboot/constants.js.map +1 -0
  39. package/dist/protocol/picoboot/programmer.d.ts +73 -0
  40. package/dist/protocol/picoboot/programmer.d.ts.map +1 -0
  41. package/dist/protocol/picoboot/programmer.js +278 -0
  42. package/dist/protocol/picoboot/programmer.js.map +1 -0
  43. package/dist/protocol/picoboot/uf2.d.ts +51 -0
  44. package/dist/protocol/picoboot/uf2.d.ts.map +1 -0
  45. package/dist/protocol/picoboot/uf2.js +119 -0
  46. package/dist/protocol/picoboot/uf2.js.map +1 -0
  47. package/dist/protocol/receiveData.d.ts +3 -0
  48. package/dist/protocol/receiveData.d.ts.map +1 -0
  49. package/dist/protocol/receiveData.js +72 -0
  50. package/dist/protocol/receiveData.js.map +1 -0
  51. package/dist/protocol/sendCommand.d.ts +14 -0
  52. package/dist/protocol/sendCommand.d.ts.map +1 -0
  53. package/dist/protocol/sendCommand.js +48 -0
  54. package/dist/protocol/sendCommand.js.map +1 -0
  55. package/dist/protocol/stk500v2/constants.d.ts +57 -0
  56. package/dist/protocol/stk500v2/constants.d.ts.map +1 -0
  57. package/dist/protocol/stk500v2/constants.js +62 -0
  58. package/dist/protocol/stk500v2/constants.js.map +1 -0
  59. package/dist/protocol/stk500v2/frame.d.ts +14 -0
  60. package/dist/protocol/stk500v2/frame.d.ts.map +1 -0
  61. package/dist/protocol/stk500v2/frame.js +116 -0
  62. package/dist/protocol/stk500v2/frame.js.map +1 -0
  63. package/dist/protocol/stk500v2/programmer.d.ts +92 -0
  64. package/dist/protocol/stk500v2/programmer.d.ts.map +1 -0
  65. package/dist/protocol/stk500v2/programmer.js +482 -0
  66. package/dist/protocol/stk500v2/programmer.js.map +1 -0
  67. package/dist/protocol/updi/constants.d.ts +107 -0
  68. package/dist/protocol/updi/constants.d.ts.map +1 -0
  69. package/dist/protocol/updi/constants.js +130 -0
  70. package/dist/protocol/updi/constants.js.map +1 -0
  71. package/dist/protocol/updi/link.d.ts +82 -0
  72. package/dist/protocol/updi/link.d.ts.map +1 -0
  73. package/dist/protocol/updi/link.js +241 -0
  74. package/dist/protocol/updi/link.js.map +1 -0
  75. package/dist/protocol/updi/programmer.d.ts +89 -0
  76. package/dist/protocol/updi/programmer.d.ts.map +1 -0
  77. package/dist/protocol/updi/programmer.js +359 -0
  78. package/dist/protocol/updi/programmer.js.map +1 -0
  79. package/dist/stk500.d.ts +101 -0
  80. package/dist/stk500.d.ts.map +1 -0
  81. package/dist/stk500.js +426 -0
  82. package/dist/stk500.js.map +1 -0
  83. package/dist/transport/IPicobootTransport.d.ts +25 -0
  84. package/dist/transport/IPicobootTransport.d.ts.map +1 -0
  85. package/dist/transport/IPicobootTransport.js +10 -0
  86. package/dist/transport/IPicobootTransport.js.map +1 -0
  87. package/dist/transport/ITransport.d.ts +33 -0
  88. package/dist/transport/ITransport.d.ts.map +1 -0
  89. package/dist/transport/ITransport.js +4 -0
  90. package/dist/transport/ITransport.js.map +1 -0
  91. package/dist/transport/NodeSerialTransport.d.ts +35 -0
  92. package/dist/transport/NodeSerialTransport.d.ts.map +1 -0
  93. package/dist/transport/NodeSerialTransport.js +102 -0
  94. package/dist/transport/NodeSerialTransport.js.map +1 -0
  95. package/dist/transport/NodeUSBTransport.d.ts +24 -0
  96. package/dist/transport/NodeUSBTransport.d.ts.map +1 -0
  97. package/dist/transport/NodeUSBTransport.js +146 -0
  98. package/dist/transport/NodeUSBTransport.js.map +1 -0
  99. package/dist/transport/WebSerialTransport.d.ts +63 -0
  100. package/dist/transport/WebSerialTransport.d.ts.map +1 -0
  101. package/dist/transport/WebSerialTransport.js +159 -0
  102. package/dist/transport/WebSerialTransport.js.map +1 -0
  103. package/package.json +79 -0
@@ -0,0 +1,80 @@
1
+ // PICOBOOT USB protocol constants for RP2040 / RP2350
2
+ // Reference: https://github.com/raspberrypi/pico-sdk/blob/master/src/common/boot_picoboot_headers/include/boot/picoboot.h
3
+ // https://github.com/raspberrypi/picotool/blob/master/picoboot_connection/picoboot_connection.h
4
+ // ── USB device identifiers ──────────────────────────────────────────────────
5
+ /** Raspberry Pi USB Vendor ID */
6
+ export const PICOBOOT_VID = 0x2E8A;
7
+ /** RP2040 USBBOOT Product ID (BOOTSEL mode) */
8
+ export const PICOBOOT_PID_RP2040 = 0x0003;
9
+ /** RP2350 USBBOOT Product ID (BOOTSEL mode) */
10
+ export const PICOBOOT_PID_RP2350 = 0x000F;
11
+ // ── USB interface / endpoint layout ────────────────────────────────────────
12
+ // Interface 0 = MSD (Mass Storage, virtual FAT drive for UF2 drag-and-drop)
13
+ // Interface 1 = PICOBOOT (vendor class 0xFF, binary protocol)
14
+ /** PICOBOOT interface index (interface 1) */
15
+ export const PICOBOOT_INTERFACE = 1;
16
+ // ── PICOBOOT command frame ──────────────────────────────────────────────────
17
+ /** Magic number at offset 0 of every PICOBOOT command packet (little-endian) */
18
+ export const PICOBOOT_MAGIC = 0x431FD10B;
19
+ /** Size of a PICOBOOT command packet in bytes */
20
+ export const PICOBOOT_CMD_SIZE = 32;
21
+ /** Size of the PICOBOOT status response in bytes */
22
+ export const PICOBOOT_STATUS_SIZE = 16;
23
+ // ── PICOBOOT command IDs ────────────────────────────────────────────────────
24
+ // Bit 7 = 1 means data flows IN (device → host). Bit 7 = 0 means OUT.
25
+ export const PicobootCmd = {
26
+ EXCLUSIVE_ACCESS: 0x01, // OUT, args: 1-byte exclusive flag
27
+ REBOOT: 0x02, // OUT, args: [PC u32][SP u32][delayMs u32]
28
+ FLASH_ERASE: 0x03, // OUT, args: [addr u32][size u32], no data phase
29
+ READ: 0x84, // IN (bit7 set), args: [addr u32][size u32]
30
+ WRITE: 0x05, // OUT, args: [addr u32][size u32]
31
+ EXIT_XIP: 0x06, // OUT, no args
32
+ ENTER_CMD_XIP: 0x07, // OUT, no args
33
+ EXEC: 0x08, // OUT, args: [addr u32]
34
+ VECTORIZE_FLASH: 0x09, // OUT, args: [addr u32]
35
+ };
36
+ // ── PICOBOOT status codes ───────────────────────────────────────────────────
37
+ export const PicobootStatus = {
38
+ OK: 0,
39
+ UNKNOWN_CMD: 1,
40
+ BAD_CHECKSUM: 2,
41
+ NOT_PERMITTED: 3,
42
+ INVALID_ADDRESS: 4,
43
+ BAD_ALIGNMENT: 5,
44
+ INTERLEAVED: 6,
45
+ REBOOTING: 7,
46
+ UNKNOWN: 8,
47
+ INVALID_STATE: 9,
48
+ };
49
+ // ── Flash geometry ──────────────────────────────────────────────────────────
50
+ /** Minimum erase granularity — address and size must be 4KB-aligned */
51
+ export const FLASH_SECTOR_SIZE = 4096;
52
+ /** Flash page write size — address and size must be 256-byte-aligned */
53
+ export const FLASH_PAGE_SIZE = 256;
54
+ /** XIP base address in CPU address space (flash starts here for the CPU) */
55
+ export const RP2040_FLASH_BASE = 0x10000000;
56
+ /**
57
+ * PICOBOOT uses offset-0 addressing for flash.
58
+ * To write to XIP address 0x10001000, pass addr = 0x00001000.
59
+ * Use this mask to convert: picobootAddr = xipAddr & ~RP2040_FLASH_BASE
60
+ */
61
+ export const RP2040_FLASH_ADDR_MASK = 0x0FFFFFFF;
62
+ // ── UF2 file format ─────────────────────────────────────────────────────────
63
+ // Reference: https://github.com/microsoft/uf2
64
+ export const UF2_MAGIC_START0 = 0x0A324655; // "UF2\n" bytes
65
+ export const UF2_MAGIC_START1 = 0x9E5D5157;
66
+ export const UF2_MAGIC_END = 0x0AB16F30;
67
+ /** UF2 flag: familyID field is valid (must be set for RP2040/RP2350) */
68
+ export const UF2_FLAG_FAMILY_ID = 0x00002000;
69
+ /** UF2 flag: block should not be flashed (skip when flashing) */
70
+ export const UF2_FLAG_NOT_MAIN = 0x00000001;
71
+ /** Total size of one UF2 block (bytes) */
72
+ export const UF2_BLOCK_SIZE = 512;
73
+ /** Usable payload bytes per UF2 block */
74
+ export const UF2_PAYLOAD_SIZE = 256;
75
+ // ── UF2 family IDs ──────────────────────────────────────────────────────────
76
+ export const UF2_FAMILY_RP2040 = 0xE48BFF56;
77
+ export const UF2_FAMILY_RP2350_ARM = 0xE48BFF59; // Secure ARM
78
+ export const UF2_FAMILY_RP2350_RISCV = 0xE48BFF5A;
79
+ export const UF2_FAMILY_RP2350_ARM_NS = 0xE48BFF5B; // Non-secure ARM
80
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/protocol/picoboot/constants.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,0HAA0H;AAC1H,2GAA2G;AAE3G,+EAA+E;AAE/E,iCAAiC;AACjC,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC;AACnC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAC1C,+CAA+C;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C,8EAA8E;AAC9E,4EAA4E;AAC5E,8DAA8D;AAE9D,6CAA6C;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAEpC,+EAA+E;AAE/E,gFAAgF;AAChF,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;AAEzC,iDAAiD;AACjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,oDAAoD;AACpD,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,+EAA+E;AAC/E,sEAAsE;AAEtE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,gBAAgB,EAAE,IAAI,EAAG,mCAAmC;IAC5D,MAAM,EAAY,IAAI,EAAG,2CAA2C;IACpE,WAAW,EAAO,IAAI,EAAG,iDAAiD;IAC1E,IAAI,EAAc,IAAI,EAAG,6CAA6C;IACtE,KAAK,EAAa,IAAI,EAAG,kCAAkC;IAC3D,QAAQ,EAAU,IAAI,EAAG,eAAe;IACxC,aAAa,EAAK,IAAI,EAAG,eAAe;IACxC,IAAI,EAAc,IAAI,EAAG,wBAAwB;IACjD,eAAe,EAAG,IAAI,EAAG,wBAAwB;CACzC,CAAC;AAEX,+EAA+E;AAE/E,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,EAAE,EAAe,CAAC;IAClB,WAAW,EAAM,CAAC;IAClB,YAAY,EAAK,CAAC;IAClB,aAAa,EAAI,CAAC;IAClB,eAAe,EAAE,CAAC;IAClB,aAAa,EAAI,CAAC;IAClB,WAAW,EAAM,CAAC;IAClB,SAAS,EAAQ,CAAC;IAClB,OAAO,EAAU,CAAC;IAClB,aAAa,EAAI,CAAC;CACV,CAAC;AAEX,+EAA+E;AAE/E,uEAAuE;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAEtC,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAEnC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAC;AAEjD,+EAA+E;AAC/E,8CAA8C;AAE9C,MAAM,CAAC,MAAM,gBAAgB,GAAK,UAAU,CAAC,CAAE,gBAAgB;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAK,UAAU,CAAC;AAC7C,MAAM,CAAC,MAAM,aAAa,GAAQ,UAAU,CAAC;AAE7C,wEAAwE;AACxE,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAC7C,iEAAiE;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAAI,UAAU,CAAC;AAE7C,0CAA0C;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAO,GAAG,CAAC;AACtC,yCAAyC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAK,GAAG,CAAC;AAEtC,+EAA+E;AAE/E,MAAM,CAAC,MAAM,iBAAiB,GAAS,UAAU,CAAC;AAClD,MAAM,CAAC,MAAM,qBAAqB,GAAK,UAAU,CAAC,CAAE,aAAa;AACjE,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAC;AAClD,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC,CAAC,iBAAiB"}
@@ -0,0 +1,73 @@
1
+ import type { IPicobootTransport } from '../../transport/IPicobootTransport.js';
2
+ import type { BootloadProgressCallback, STK500Options } from '../../core/types.js';
3
+ export declare class PicoBoot {
4
+ private readonly transport;
5
+ private readonly log;
6
+ private token;
7
+ constructor(transport: IPicobootTransport, opts?: STK500Options);
8
+ /**
9
+ * Flash firmware to the RP2040 / RP2350.
10
+ *
11
+ * Accepts either a UF2 file (detected by magic bytes) or a raw binary.
12
+ * Raw binaries are assumed to start at XIP base 0x10000000 unless
13
+ * `opts.baseAddr` is specified.
14
+ *
15
+ * @param data UF2 file or raw binary as Uint8Array
16
+ * @param progress Optional progress callback (status, 0–100)
17
+ * @param opts Optional: baseAddr (for raw binary), verify (default: true)
18
+ */
19
+ bootload(data: Uint8Array, progress?: BootloadProgressCallback, opts?: {
20
+ baseAddr?: number;
21
+ verify?: boolean;
22
+ }): Promise<void>;
23
+ /** EXCLUSIVE_ACCESS — take or release exclusive control (disables/enables MSD) */
24
+ exclusiveAccess(exclusive: boolean): Promise<void>;
25
+ /** EXIT_XIP — exit execute-in-place mode (required before erase or write) */
26
+ exitXip(): Promise<void>;
27
+ /** ENTER_CMD_XIP — re-enter XIP mode (optional, called after programming) */
28
+ enterCmdXip(): Promise<void>;
29
+ /**
30
+ * FLASH_ERASE — erase flash sectors.
31
+ * @param addr Flash offset (0-based, 4KB-aligned)
32
+ * @param size Number of bytes to erase (must be a multiple of 4096)
33
+ */
34
+ flashErase(addr: number, size: number): Promise<void>;
35
+ /**
36
+ * WRITE — write a flash page (256 bytes).
37
+ * @param addr Flash offset (0-based, 256-byte-aligned)
38
+ * @param data Exactly 256 bytes of payload
39
+ */
40
+ flashWrite(addr: number, data: Uint8Array): Promise<void>;
41
+ /**
42
+ * READ — read bytes from flash.
43
+ * @param addr Flash offset (0-based)
44
+ * @param size Number of bytes to read
45
+ */
46
+ flashRead(addr: number, size: number): Promise<Uint8Array>;
47
+ /**
48
+ * REBOOT — reboot the device.
49
+ * @param pc Program counter (0 = boot from flash)
50
+ * @param sp Stack pointer (0 = use default)
51
+ * @param delayMs Milliseconds before reboot (allows USB ACK to complete)
52
+ */
53
+ reboot(pc?: number, sp?: number, delayMs?: number): Promise<void>;
54
+ /**
55
+ * Convert a raw binary to UF2 format.
56
+ * Convenience wrapper around the standalone `binaryToUf2` utility.
57
+ */
58
+ binaryToUf2(binary: Uint8Array, baseAddr?: number, familyId?: number): Uint8Array;
59
+ /**
60
+ * Execute a PICOBOOT command and (optionally) transfer data.
61
+ *
62
+ * Flow for OUT commands (no data): CMD→ | STATUS←
63
+ * Flow for OUT commands (write): CMD→ | DATA→ | STATUS←
64
+ * Flow for IN commands (read): CMD→ | DATA← | STATUS←
65
+ *
66
+ * @returns Received data for IN commands, or throws on error status
67
+ */
68
+ private execCmd;
69
+ /** Build a 32-byte PICOBOOT command packet */
70
+ private buildCmd;
71
+ private statusName;
72
+ }
73
+ //# sourceMappingURL=programmer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"programmer.d.ts","sourceRoot":"","sources":["../../../src/protocol/picoboot/programmer.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAU,MAAM,qBAAqB,CAAC;AAY3F,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,KAAK,CAAK;gBAEN,SAAS,EAAE,kBAAkB,EAAE,IAAI,CAAC,EAAE,aAAa;IAa/D;;;;;;;;;;OAUG;IACG,QAAQ,CACZ,IAAI,EAAM,UAAU,EACpB,QAAQ,CAAC,EAAE,wBAAwB,EACnC,IAAI,CAAC,EAAK;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAChD,OAAO,CAAC,IAAI,CAAC;IA+FhB,kFAAkF;IAC5E,eAAe,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxD,6EAA6E;IACvE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,6EAA6E;IACvE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlC;;;;OAIG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3D;;;;OAIG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/D;;;;OAIG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAKhE;;;;;OAKG;IACG,MAAM,CAAC,EAAE,SAAI,EAAE,EAAE,SAAI,EAAE,OAAO,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1D;;;OAGG;IACH,WAAW,CACT,MAAM,EAAI,UAAU,EACpB,QAAQ,GAAE,MAA0B,EACpC,QAAQ,CAAC,EAAE,MAAM,GAChB,UAAU;IAMb;;;;;;;;OAQG;YACW,OAAO;IAyCrB,8CAA8C;IAC9C,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,UAAU;CAQnB"}
@@ -0,0 +1,278 @@
1
+ // PICOBOOT programmer for Raspberry Pi Pico (RP2040) and Pico 2 (RP2350).
2
+ //
3
+ // Protocol reference:
4
+ // https://github.com/raspberrypi/picotool/blob/master/picoboot_connection/picoboot_connection.c
5
+ // https://github.com/raspberrypi/pico-sdk/blob/master/src/common/boot_picoboot_headers/include/boot/picoboot.h
6
+ //
7
+ // Flash sequence:
8
+ // 1. EXCLUSIVE_ACCESS(1) — take exclusive control, disable MSD drive
9
+ // 2. EXIT_XIP — exit execute-in-place mode (required before erase/write)
10
+ // 3. FLASH_ERASE(addr, size) — erase sectors (4KB-aligned)
11
+ // 4. WRITE(addr, data) — write pages (256-byte-aligned)
12
+ // 5. REBOOT(0, 0, 500) — reboot from flash
13
+ //
14
+ // Addressing: PICOBOOT uses offset-0 flash addressing.
15
+ // XIP 0x10001000 → PICOBOOT addr 0x00001000
16
+ import { STK500ProtocolError, STK500VerifyError } from '../../core/errors.js';
17
+ import { PICOBOOT_MAGIC, PICOBOOT_CMD_SIZE, PICOBOOT_STATUS_SIZE, PicobootCmd, PicobootStatus, FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, RP2040_FLASH_BASE, RP2040_FLASH_ADDR_MASK, } from './constants.js';
18
+ import { parseUf2, binaryToUf2, isUf2 } from './uf2.js';
19
+ // ── PicoBoot class ──────────────────────────────────────────────────────────
20
+ export class PicoBoot {
21
+ constructor(transport, opts) {
22
+ this.token = 1;
23
+ this.transport = transport;
24
+ if (opts?.quiet) {
25
+ this.log = () => { };
26
+ }
27
+ else if (opts?.logger) {
28
+ this.log = opts.logger;
29
+ }
30
+ else {
31
+ this.log = (level, msg) => console.log(`[webserial-flasher:picoboot] [${level}] ${msg}`);
32
+ }
33
+ }
34
+ // ── Public API ─────────────────────────────────────────────────────────────
35
+ /**
36
+ * Flash firmware to the RP2040 / RP2350.
37
+ *
38
+ * Accepts either a UF2 file (detected by magic bytes) or a raw binary.
39
+ * Raw binaries are assumed to start at XIP base 0x10000000 unless
40
+ * `opts.baseAddr` is specified.
41
+ *
42
+ * @param data UF2 file or raw binary as Uint8Array
43
+ * @param progress Optional progress callback (status, 0–100)
44
+ * @param opts Optional: baseAddr (for raw binary), verify (default: true)
45
+ */
46
+ async bootload(data, progress, opts) {
47
+ const report = (status, pct) => {
48
+ this.log('info', `${status} (${pct}%)`);
49
+ progress?.(status, pct);
50
+ };
51
+ // ── 1. Parse input ────────────────────────────────────────────────────────
52
+ report('Parsing firmware', 0);
53
+ let binary;
54
+ let baseAddr;
55
+ if (isUf2(data)) {
56
+ this.log('debug', 'Input detected as UF2 file');
57
+ const parsed = parseUf2(data);
58
+ binary = parsed.binary;
59
+ baseAddr = parsed.baseAddr;
60
+ this.log('debug', `UF2: ${parsed.blocks.length} blocks, baseAddr=0x${baseAddr.toString(16)}, familyId=0x${parsed.familyId.toString(16)}`);
61
+ }
62
+ else {
63
+ this.log('debug', 'Input detected as raw binary');
64
+ binary = data;
65
+ baseAddr = opts?.baseAddr ?? RP2040_FLASH_BASE;
66
+ }
67
+ // Convert XIP address to PICOBOOT offset address
68
+ const flashOffset = baseAddr & RP2040_FLASH_ADDR_MASK;
69
+ const doVerify = opts?.verify !== false;
70
+ // ── 2. Reset interface ────────────────────────────────────────────────────
71
+ report('Connecting to device', 2);
72
+ try {
73
+ await this.transport.resetInterface();
74
+ }
75
+ catch {
76
+ this.log('debug', 'resetInterface failed (non-fatal, continuing)');
77
+ }
78
+ // ── 3. Exclusive access ───────────────────────────────────────────────────
79
+ report('Taking exclusive access', 5);
80
+ await this.exclusiveAccess(true);
81
+ // ── 4. Exit XIP mode ──────────────────────────────────────────────────────
82
+ report('Exiting XIP mode', 8);
83
+ await this.exitXip();
84
+ // ── 5. Erase flash ────────────────────────────────────────────────────────
85
+ report('Erasing flash', 10);
86
+ const eraseSize = alignUp(binary.length, FLASH_SECTOR_SIZE);
87
+ await this.flashErase(flashOffset, eraseSize);
88
+ this.log('debug', `Erased ${eraseSize} bytes at offset 0x${flashOffset.toString(16)}`);
89
+ // ── 6. Write pages ────────────────────────────────────────────────────────
90
+ const padded = padToAlignment(binary, FLASH_PAGE_SIZE);
91
+ const numPages = padded.length / FLASH_PAGE_SIZE;
92
+ report('Uploading', 15);
93
+ for (let i = 0; i < numPages; i++) {
94
+ const pageOff = flashOffset + i * FLASH_PAGE_SIZE;
95
+ const page = padded.slice(i * FLASH_PAGE_SIZE, (i + 1) * FLASH_PAGE_SIZE);
96
+ await this.flashWrite(pageOff, page);
97
+ const pct = 15 + Math.round((i + 1) / numPages * 55);
98
+ progress?.('Uploading', pct);
99
+ }
100
+ report('Uploading', 70);
101
+ this.log('debug', `Wrote ${padded.length} bytes (${numPages} pages)`);
102
+ // ── 7. Verify ─────────────────────────────────────────────────────────────
103
+ if (doVerify) {
104
+ report('Verifying', 72);
105
+ const readBack = await this.flashRead(flashOffset, padded.length);
106
+ for (let i = 0; i < binary.length; i++) {
107
+ if (readBack[i] !== binary[i]) {
108
+ throw new STK500VerifyError(baseAddr + i, binary[i], readBack[i]);
109
+ }
110
+ if (i % (FLASH_PAGE_SIZE * 8) === 0) {
111
+ const pct = 72 + Math.round(i / binary.length * 20);
112
+ progress?.('Verifying', pct);
113
+ }
114
+ }
115
+ report('Verifying', 92);
116
+ }
117
+ // ── 8. Reboot ─────────────────────────────────────────────────────────────
118
+ report('Rebooting device', 95);
119
+ await this.reboot(0, 0, 500);
120
+ report('Complete', 100);
121
+ }
122
+ // ── PICOBOOT commands ──────────────────────────────────────────────────────
123
+ /** EXCLUSIVE_ACCESS — take or release exclusive control (disables/enables MSD) */
124
+ async exclusiveAccess(exclusive) {
125
+ const args = new Uint8Array([exclusive ? 1 : 0]);
126
+ await this.execCmd(PicobootCmd.EXCLUSIVE_ACCESS, args);
127
+ }
128
+ /** EXIT_XIP — exit execute-in-place mode (required before erase or write) */
129
+ async exitXip() {
130
+ await this.execCmd(PicobootCmd.EXIT_XIP, new Uint8Array(0));
131
+ }
132
+ /** ENTER_CMD_XIP — re-enter XIP mode (optional, called after programming) */
133
+ async enterCmdXip() {
134
+ await this.execCmd(PicobootCmd.ENTER_CMD_XIP, new Uint8Array(0));
135
+ }
136
+ /**
137
+ * FLASH_ERASE — erase flash sectors.
138
+ * @param addr Flash offset (0-based, 4KB-aligned)
139
+ * @param size Number of bytes to erase (must be a multiple of 4096)
140
+ */
141
+ async flashErase(addr, size) {
142
+ if (addr % FLASH_SECTOR_SIZE !== 0) {
143
+ throw new STK500ProtocolError(`FLASH_ERASE: addr 0x${addr.toString(16)} is not 4KB-aligned`);
144
+ }
145
+ if (size % FLASH_SECTOR_SIZE !== 0) {
146
+ throw new STK500ProtocolError(`FLASH_ERASE: size ${size} is not a multiple of 4096`);
147
+ }
148
+ const args = buildRangeArgs(addr, size);
149
+ await this.execCmd(PicobootCmd.FLASH_ERASE, args);
150
+ }
151
+ /**
152
+ * WRITE — write a flash page (256 bytes).
153
+ * @param addr Flash offset (0-based, 256-byte-aligned)
154
+ * @param data Exactly 256 bytes of payload
155
+ */
156
+ async flashWrite(addr, data) {
157
+ if (addr % FLASH_PAGE_SIZE !== 0) {
158
+ throw new STK500ProtocolError(`WRITE: addr 0x${addr.toString(16)} is not 256-byte-aligned`);
159
+ }
160
+ if (data.length !== FLASH_PAGE_SIZE) {
161
+ throw new STK500ProtocolError(`WRITE: data must be exactly ${FLASH_PAGE_SIZE} bytes (got ${data.length})`);
162
+ }
163
+ const args = buildRangeArgs(addr, data.length);
164
+ await this.execCmd(PicobootCmd.WRITE, args, data);
165
+ }
166
+ /**
167
+ * READ — read bytes from flash.
168
+ * @param addr Flash offset (0-based)
169
+ * @param size Number of bytes to read
170
+ */
171
+ async flashRead(addr, size) {
172
+ const args = buildRangeArgs(addr, size);
173
+ return this.execCmd(PicobootCmd.READ, args, undefined, size);
174
+ }
175
+ /**
176
+ * REBOOT — reboot the device.
177
+ * @param pc Program counter (0 = boot from flash)
178
+ * @param sp Stack pointer (0 = use default)
179
+ * @param delayMs Milliseconds before reboot (allows USB ACK to complete)
180
+ */
181
+ async reboot(pc = 0, sp = 0, delayMs = 500) {
182
+ const args = new Uint8Array(12);
183
+ const view = new DataView(args.buffer);
184
+ view.setUint32(0, pc, true);
185
+ view.setUint32(4, sp, true);
186
+ view.setUint32(8, delayMs, true);
187
+ await this.execCmd(PicobootCmd.REBOOT, args);
188
+ }
189
+ // ── UF2 helpers ────────────────────────────────────────────────────────────
190
+ /**
191
+ * Convert a raw binary to UF2 format.
192
+ * Convenience wrapper around the standalone `binaryToUf2` utility.
193
+ */
194
+ binaryToUf2(binary, baseAddr = RP2040_FLASH_BASE, familyId) {
195
+ return binaryToUf2(binary, baseAddr, familyId);
196
+ }
197
+ // ── Private protocol helpers ───────────────────────────────────────────────
198
+ /**
199
+ * Execute a PICOBOOT command and (optionally) transfer data.
200
+ *
201
+ * Flow for OUT commands (no data): CMD→ | STATUS←
202
+ * Flow for OUT commands (write): CMD→ | DATA→ | STATUS←
203
+ * Flow for IN commands (read): CMD→ | DATA← | STATUS←
204
+ *
205
+ * @returns Received data for IN commands, or throws on error status
206
+ */
207
+ async execCmd(cmdId, args, outData, inLength = 0) {
208
+ const isInCmd = (cmdId & 0x80) !== 0;
209
+ const transferLength = isInCmd ? inLength : (outData?.length ?? 0);
210
+ const cmd = this.buildCmd(cmdId, args, transferLength);
211
+ await this.transport.sendCommand(cmd);
212
+ // Data phase: OUT (write) or IN (read)
213
+ let readData = null;
214
+ if (isInCmd && inLength > 0) {
215
+ readData = await this.transport.receiveBytes(inLength);
216
+ }
217
+ else if (outData && outData.length > 0) {
218
+ await this.transport.sendBytes(outData);
219
+ }
220
+ // Status phase — read up to PICOBOOT_STATUS_SIZE bytes from IN endpoint.
221
+ // The device sends a 16-byte picoboot_cmd_status struct:
222
+ // [dToken u32][dStatusCode u32][bCmdId u8][bInProgress u8][6-byte pad]
223
+ const status = await this.transport.receiveBytes(PICOBOOT_STATUS_SIZE);
224
+ if (status.length >= 8) {
225
+ const statusCode = new DataView(status.buffer, status.byteOffset, status.byteLength).getUint32(4, true);
226
+ if (statusCode !== PicobootStatus.OK) {
227
+ const name = this.statusName(statusCode);
228
+ throw new STK500ProtocolError(`PICOBOOT cmd 0x${cmdId.toString(16)} failed: ${name} (code ${statusCode})`);
229
+ }
230
+ }
231
+ // If status.length < 8, assume ZLP (zero-length packet) = OK
232
+ return readData;
233
+ }
234
+ /** Build a 32-byte PICOBOOT command packet */
235
+ buildCmd(cmdId, args, transferLength) {
236
+ const buf = new Uint8Array(PICOBOOT_CMD_SIZE);
237
+ const view = new DataView(buf.buffer);
238
+ view.setUint32(0, PICOBOOT_MAGIC, true); // dMagic
239
+ view.setUint32(4, this.token++, true); // dToken
240
+ buf[8] = cmdId; // bCmdId
241
+ buf[9] = args.length & 0xFF; // bCmdSize
242
+ // bytes 10-11: _unused = 0
243
+ view.setUint32(12, transferLength, true); // dTransferLength
244
+ buf.set(args.slice(0, Math.min(args.length, 16)), 16); // args (max 16 bytes)
245
+ return buf;
246
+ }
247
+ statusName(code) {
248
+ const names = {
249
+ 0: 'OK', 1: 'UNKNOWN_CMD', 2: 'BAD_CHECKSUM', 3: 'NOT_PERMITTED',
250
+ 4: 'INVALID_ADDRESS', 5: 'BAD_ALIGNMENT', 6: 'INTERLEAVED',
251
+ 7: 'REBOOTING', 8: 'UNKNOWN', 9: 'INVALID_STATE',
252
+ };
253
+ return names[code] ?? `status_${code}`;
254
+ }
255
+ }
256
+ // ── Private utilities ────────────────────────────────────────────────────────
257
+ /** Build the 8-byte range args struct: [addr u32][size u32] (little-endian) */
258
+ function buildRangeArgs(addr, size) {
259
+ const args = new Uint8Array(8);
260
+ const view = new DataView(args.buffer);
261
+ view.setUint32(0, addr, true);
262
+ view.setUint32(4, size, true);
263
+ return args;
264
+ }
265
+ /** Round `n` up to the nearest multiple of `alignment` */
266
+ function alignUp(n, alignment) {
267
+ return Math.ceil(n / alignment) * alignment;
268
+ }
269
+ /** Pad `data` to a multiple of `alignment` with 0xFF */
270
+ function padToAlignment(data, alignment) {
271
+ const padded = alignUp(data.length, alignment);
272
+ if (padded === data.length)
273
+ return data;
274
+ const out = new Uint8Array(padded).fill(0xFF);
275
+ out.set(data);
276
+ return out;
277
+ }
278
+ //# sourceMappingURL=programmer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"programmer.js","sourceRoot":"","sources":["../../../src/protocol/picoboot/programmer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ export interface UF2Block {
2
+ /** UF2 flags field */
3
+ flags: number;
4
+ /** Target XIP address for this block */
5
+ targetAddr: number;
6
+ /** Number of payload bytes in this block (always 256 for RP2040) */
7
+ payloadSize: number;
8
+ /** Sequential block index (0-based) */
9
+ blockNo: number;
10
+ /** Total block count in file */
11
+ numBlocks: number;
12
+ /** Family ID (e.g. UF2_FAMILY_RP2040) */
13
+ familyId: number;
14
+ /** Firmware payload — payloadSize bytes */
15
+ data: Uint8Array;
16
+ }
17
+ export interface UF2ParseResult {
18
+ /** All flashable blocks, sorted by target address */
19
+ blocks: UF2Block[];
20
+ /** Lowest XIP target address found */
21
+ baseAddr: number;
22
+ /** Contiguous binary assembled from all blocks (0xFF-padded for gaps) */
23
+ binary: Uint8Array;
24
+ /** Family ID from the first flashable block */
25
+ familyId: number;
26
+ }
27
+ /**
28
+ * Returns true if `data` starts with UF2 magic bytes.
29
+ * Use this to distinguish a UF2 file from a raw binary.
30
+ */
31
+ export declare function isUf2(data: Uint8Array): boolean;
32
+ /**
33
+ * Parse a UF2 file into individual blocks and a contiguous binary image.
34
+ *
35
+ * - Skips blocks with the NOT_MAIN flag set.
36
+ * - Sorts blocks by target address before assembling.
37
+ * - Fills gaps between blocks with 0xFF.
38
+ *
39
+ * @throws STK500InvalidHexError if the file is structurally invalid.
40
+ */
41
+ export declare function parseUf2(data: Uint8Array): UF2ParseResult;
42
+ /**
43
+ * Convert a raw binary into a UF2 file.
44
+ *
45
+ * @param binary Raw firmware bytes
46
+ * @param baseAddr XIP start address (default: 0x10000000 for RP2040)
47
+ * @param familyId UF2 family ID (default: RP2040)
48
+ * @returns UF2 file as Uint8Array (numBlocks × 512 bytes)
49
+ */
50
+ export declare function binaryToUf2(binary: Uint8Array, baseAddr?: number, familyId?: number): Uint8Array;
51
+ //# sourceMappingURL=uf2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uf2.d.ts","sourceRoot":"","sources":["../../../src/protocol/picoboot/uf2.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,QAAQ;IACvB,sBAAsB;IACtB,KAAK,EAAQ,MAAM,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAG,MAAM,CAAC;IACpB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,OAAO,EAAM,MAAM,CAAC;IACpB,gCAAgC;IAChC,SAAS,EAAI,MAAM,CAAC;IACpB,yCAAyC;IACzC,QAAQ,EAAK,MAAM,CAAC;IACpB,2CAA2C;IAC3C,IAAI,EAAS,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,MAAM,EAAI,QAAQ,EAAE,CAAC;IACrB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,MAAM,EAAI,UAAU,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID;;;GAGG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAI/C;AAID;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CA2EzD;AAID;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAI,UAAU,EACpB,QAAQ,GAAE,MAA0B,EACpC,QAAQ,GAAE,MAA0B,GACnC,UAAU,CAsBZ"}
@@ -0,0 +1,119 @@
1
+ // UF2 file format parser and generator for RP2040 / RP2350 firmware.
2
+ // Reference: https://github.com/microsoft/uf2
3
+ //
4
+ // A UF2 file is a sequence of 512-byte blocks. Each block carries 256 bytes
5
+ // of firmware payload plus metadata. The Raspberry Pi Pico bootloader accepts
6
+ // UF2 files via USB drag-and-drop (MSD) or via the PICOBOOT protocol.
7
+ import { UF2_MAGIC_START0, UF2_MAGIC_START1, UF2_MAGIC_END, UF2_FLAG_FAMILY_ID, UF2_FLAG_NOT_MAIN, UF2_BLOCK_SIZE, UF2_PAYLOAD_SIZE, UF2_FAMILY_RP2040, RP2040_FLASH_BASE, } from './constants.js';
8
+ import { STK500InvalidHexError } from '../../core/errors.js';
9
+ // ── Detection ───────────────────────────────────────────────────────────────
10
+ /**
11
+ * Returns true if `data` starts with UF2 magic bytes.
12
+ * Use this to distinguish a UF2 file from a raw binary.
13
+ */
14
+ export function isUf2(data) {
15
+ if (data.length < 4)
16
+ return false;
17
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
18
+ return view.getUint32(0, true) === UF2_MAGIC_START0;
19
+ }
20
+ // ── Parser ──────────────────────────────────────────────────────────────────
21
+ /**
22
+ * Parse a UF2 file into individual blocks and a contiguous binary image.
23
+ *
24
+ * - Skips blocks with the NOT_MAIN flag set.
25
+ * - Sorts blocks by target address before assembling.
26
+ * - Fills gaps between blocks with 0xFF.
27
+ *
28
+ * @throws STK500InvalidHexError if the file is structurally invalid.
29
+ */
30
+ export function parseUf2(data) {
31
+ if (data.length === 0) {
32
+ throw new STK500InvalidHexError('UF2 data is empty');
33
+ }
34
+ if (data.length % UF2_BLOCK_SIZE !== 0) {
35
+ throw new STK500InvalidHexError(`UF2 file size (${data.length} bytes) is not a multiple of 512`);
36
+ }
37
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
38
+ const blocks = [];
39
+ const total = data.length / UF2_BLOCK_SIZE;
40
+ for (let i = 0; i < total; i++) {
41
+ const off = i * UF2_BLOCK_SIZE;
42
+ const magic0 = view.getUint32(off + 0, true);
43
+ const magic1 = view.getUint32(off + 4, true);
44
+ const magicEnd = view.getUint32(off + 508, true);
45
+ if (magic0 !== UF2_MAGIC_START0 || magic1 !== UF2_MAGIC_START1 || magicEnd !== UF2_MAGIC_END) {
46
+ throw new STK500InvalidHexError(`UF2 block ${i}: invalid magic bytes`);
47
+ }
48
+ const flags = view.getUint32(off + 8, true);
49
+ const targetAddr = view.getUint32(off + 12, true);
50
+ const payloadSize = view.getUint32(off + 16, true);
51
+ const blockNo = view.getUint32(off + 20, true);
52
+ const numBlocks = view.getUint32(off + 24, true);
53
+ const familyId = view.getUint32(off + 28, true);
54
+ // Skip blocks not intended for main flash
55
+ if (flags & UF2_FLAG_NOT_MAIN)
56
+ continue;
57
+ if (payloadSize > UF2_PAYLOAD_SIZE) {
58
+ throw new STK500InvalidHexError(`UF2 block ${i}: payloadSize ${payloadSize} exceeds maximum ${UF2_PAYLOAD_SIZE}`);
59
+ }
60
+ blocks.push({
61
+ flags,
62
+ targetAddr,
63
+ payloadSize,
64
+ blockNo,
65
+ numBlocks,
66
+ familyId,
67
+ data: data.slice(off + 32, off + 32 + payloadSize),
68
+ });
69
+ }
70
+ if (blocks.length === 0) {
71
+ throw new STK500InvalidHexError('UF2 file contains no flashable blocks');
72
+ }
73
+ // Sort ascending by target address
74
+ blocks.sort((a, b) => a.targetAddr - b.targetAddr);
75
+ const baseAddr = blocks[0].targetAddr;
76
+ const lastBlock = blocks[blocks.length - 1];
77
+ const totalBytes = lastBlock.targetAddr - baseAddr + lastBlock.payloadSize;
78
+ const binary = new Uint8Array(totalBytes).fill(0xFF);
79
+ for (const block of blocks) {
80
+ const offset = block.targetAddr - baseAddr;
81
+ binary.set(block.data.subarray(0, block.payloadSize), offset);
82
+ }
83
+ return {
84
+ blocks,
85
+ baseAddr,
86
+ binary,
87
+ familyId: blocks[0].familyId,
88
+ };
89
+ }
90
+ // ── Generator ───────────────────────────────────────────────────────────────
91
+ /**
92
+ * Convert a raw binary into a UF2 file.
93
+ *
94
+ * @param binary Raw firmware bytes
95
+ * @param baseAddr XIP start address (default: 0x10000000 for RP2040)
96
+ * @param familyId UF2 family ID (default: RP2040)
97
+ * @returns UF2 file as Uint8Array (numBlocks × 512 bytes)
98
+ */
99
+ export function binaryToUf2(binary, baseAddr = RP2040_FLASH_BASE, familyId = UF2_FAMILY_RP2040) {
100
+ const numBlocks = Math.ceil(binary.length / UF2_PAYLOAD_SIZE);
101
+ const out = new Uint8Array(numBlocks * UF2_BLOCK_SIZE);
102
+ const view = new DataView(out.buffer);
103
+ for (let i = 0; i < numBlocks; i++) {
104
+ const off = i * UF2_BLOCK_SIZE;
105
+ const addr = baseAddr + i * UF2_PAYLOAD_SIZE;
106
+ view.setUint32(off + 0, UF2_MAGIC_START0, true);
107
+ view.setUint32(off + 4, UF2_MAGIC_START1, true);
108
+ view.setUint32(off + 8, UF2_FLAG_FAMILY_ID, true);
109
+ view.setUint32(off + 12, addr, true);
110
+ view.setUint32(off + 16, UF2_PAYLOAD_SIZE, true);
111
+ view.setUint32(off + 20, i, true);
112
+ view.setUint32(off + 24, numBlocks, true);
113
+ view.setUint32(off + 28, familyId, true);
114
+ out.set(binary.slice(i * UF2_PAYLOAD_SIZE, (i + 1) * UF2_PAYLOAD_SIZE), off + 32);
115
+ view.setUint32(off + 508, UF2_MAGIC_END, true);
116
+ }
117
+ return out;
118
+ }
119
+ //# sourceMappingURL=uf2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uf2.js","sourceRoot":"","sources":["../../../src/protocol/picoboot/uf2.ts"],"names":[],"mappings":"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"}