|
| 1 | +# HID Flashing Format |
| 2 | + |
| 3 | +HF2 (HID Flashing Format) is a protocol and message format intended for |
| 4 | +communication with embedded devices. Basic functions supported include: |
| 5 | + |
| 6 | +* serial communication for `printf()` debugging etc. |
| 7 | +* flashing (updating device firmware) |
| 8 | +* debugger interfaces |
| 9 | + |
| 10 | +It is optimized for packet formats, where packets are around 64 bytes long. |
| 11 | +It will work for smaller packets, of at least a few bytes, or bigger |
| 12 | +ones, but be less efficient. |
| 13 | + |
| 14 | +In particular, it is suitable for running over USB HID (Human Interface Device), |
| 15 | +which is widely supported in various operating systems without the need for kernel-space |
| 16 | +drivers. It is also possible to run the protocol over a WebUSB link with either a single |
| 17 | +interrupt endpoint or two bulk endpoints, allowing direct access from supported |
| 18 | +browsers. |
| 19 | + |
| 20 | +## Raw message format |
| 21 | + |
| 22 | +HF2 messages are composed of packets. Packets are up to 64 bytes long. |
| 23 | +Messages sent from host to device and vice versa have the same basic format. |
| 24 | + |
| 25 | +The first byte of each packet indicates: |
| 26 | +* length of the remaining data (payload) in the packet, in the lower 6 bits, i.e., between 0 and 63 inclusive |
| 27 | +* the type of the packet, in the two high bits. |
| 28 | + |
| 29 | +| Bit 7 | Bit 6 | Hex | Meaning |
| 30 | +|-------|-------|------|---------------------------------------------- |
| 31 | +| 0 | 0 | 0x00 | Inner packet of a command message |
| 32 | +| 0 | 1 | 0x40 | Final packet of a command message |
| 33 | +| 1 | 0 | 0x80 | Serial `stdout` |
| 34 | +| 1 | 1 | 0xC0 | Serial `stderr` |
| 35 | + |
| 36 | +Serial messages are thus between 0 and 63 bytes in length. |
| 37 | + |
| 38 | +Command messages can have any length (though devices will typically limit |
| 39 | +it to the native flash page size + 64 bytes). They consist of a zero or more |
| 40 | +inner packets, followed by a single final packet. Any of these packets |
| 41 | +can carry between 0 and 63 bytes of payload. |
| 42 | + |
| 43 | +For example: |
| 44 | + |
| 45 | +``` |
| 46 | +Packet 0: 83 01 02 03 AB FF FF FF |
| 47 | +Packet 1: 85 04 05 06 07 08 |
| 48 | +Packet 2: 80 DE 42 42 42 42 FF FF |
| 49 | +Packet 3: D0 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 FF FF FF |
| 50 | +---> |
| 51 | +Decoded: 01 02 03 04 05 06 07 08 D0 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 |
| 52 | +``` |
| 53 | + |
| 54 | +Note that packets can be longer than the first byte requires (packets 0, 2, and 3 are). |
| 55 | +The additional data should be discarded. |
| 56 | +This is due to various HID implementations imposing an exact 64 byte packet size. |
| 57 | + |
| 58 | +Different command messages cannot be interleaved. |
| 59 | + |
| 60 | +Serial messages should not be interleaved with command messages (though it would be |
| 61 | +technically possible). |
| 62 | + |
| 63 | +## Higher-level message format |
| 64 | + |
| 65 | +### Serial messages |
| 66 | + |
| 67 | +Serial messages are meant for `printf()` style debugging |
| 68 | +and general simple data output from the device. |
| 69 | +If the device supports only one serial channel, it should support |
| 70 | +the `stdout` channel. Otherwise, `stdout` is meant |
| 71 | +for data output (eg., logging measurements), and `stderr` |
| 72 | +is meant for `printf()` debugging. |
| 73 | + |
| 74 | +The logging application on the host may choose to send these |
| 75 | +two channels into a single output stream. |
| 76 | + |
| 77 | +Serial messages contain between `0` and `63` bytes of payload data. |
| 78 | +Size of `0` can be used as a keep-alive packet if needed. |
| 79 | + |
| 80 | +### Command messages |
| 81 | + |
| 82 | +Command structure: |
| 83 | + |
| 84 | +```c |
| 85 | +struct Command { |
| 86 | + uint32_t command_id; |
| 87 | + uint16_t tag; |
| 88 | + uint8_t reserved0; |
| 89 | + uint8_t reserved1; |
| 90 | + uint8_t data[...]; |
| 91 | +}; |
| 92 | + |
| 93 | +struct Response { |
| 94 | + uint16_t tag; |
| 95 | + uint8_t status; |
| 96 | + uint8_t status_info; |
| 97 | + uint8_t data[...]; |
| 98 | +}; |
| 99 | +``` |
| 100 | + |
| 101 | +All words in HF2 are little endian. |
| 102 | + |
| 103 | +The `tag` is an arbitrary number set by the host, for example as sequence |
| 104 | +number. The response should repeat the `tag`. |
| 105 | + |
| 106 | +The two reserved bytes in the command should be sent as zero and ignored by the device. |
| 107 | + |
| 108 | +The response status is one of the following: |
| 109 | +* `0x00` - command understood and executed correctly |
| 110 | +* `0x01` - command not understood |
| 111 | +* `0x02` - command execution error |
| 112 | + |
| 113 | +Note, that embedded devices might crash on invalid arguments, instead |
| 114 | +of returning errors. OTOH, the devices should always handle invalid commands with |
| 115 | +`0x01` status. |
| 116 | + |
| 117 | +In case of non-zero status, the `status_info` field can contain additional information. |
| 118 | + |
| 119 | +The host shall not send a new command, until the previous one was responded to. |
| 120 | +TODO does this make sense? maybe just let USB flow control handle this? |
| 121 | + |
| 122 | +## Standard commands |
| 123 | + |
| 124 | +Below we list standard commands. Not all commands have to be supported by all |
| 125 | +devices. |
| 126 | + |
| 127 | +When the C fragment states `no results`, it means just a response |
| 128 | +with zero status and no additional data should be expected. |
| 129 | + |
| 130 | +### BININFO (0x0001) |
| 131 | + |
| 132 | +This command states the current mode of the device: |
| 133 | +* ``mode == 0x01`` - bootloader, and thus flashing of user-space programs is allowed |
| 134 | +* ``mode == 0x02`` - user-space mode. |
| 135 | +It also returns the size of flash page size (flashing needs to be done on page-by-page basis), |
| 136 | +and the maximum size of message. It is always the case that |
| 137 | +``max_message_size >= flash_page_size + 64``. |
| 138 | + |
| 139 | +```c |
| 140 | +struct HF2_BININFO_Result { |
| 141 | + uint32_t mode; |
| 142 | + uint32_t flash_page_size; |
| 143 | + uint32_t flash_num_pages; |
| 144 | + uint32_t max_message_size; |
| 145 | +}; |
| 146 | +``` |
| 147 | + |
| 148 | +### INFO (0x0002) |
| 149 | + |
| 150 | +Various device information. |
| 151 | +The result is a character array. See `INFO_UF2.TXT` in UF2 format for details. |
| 152 | + |
| 153 | +```c |
| 154 | +// no arguments |
| 155 | +struct HF2_INFO_Result { |
| 156 | + uint8_t info[...]; |
| 157 | +}; |
| 158 | +``` |
| 159 | + |
| 160 | + |
| 161 | +### RESET INTO APP (0x0003) |
| 162 | + |
| 163 | +Reset the device into user-space app. Usually, no response at all will arrive for this command. |
| 164 | + |
| 165 | +```c |
| 166 | +// no arguments, no result |
| 167 | +``` |
| 168 | + |
| 169 | + |
| 170 | +### RESET INTO (0x0004) |
| 171 | + |
| 172 | +Reset the device into bootloader, usually for flashing. Usually, no response at all will arrive for this command. |
| 173 | + |
| 174 | +```c |
| 175 | +// no arguments, no result |
| 176 | +``` |
| 177 | + |
| 178 | +### START FLASH (0x0005) |
| 179 | + |
| 180 | +When issued in bootloader mode, it has no effect. |
| 181 | +In user-space mode it causes handover to bootloader. |
| 182 | +A `BININFO` command can be issued to verify that. |
| 183 | + |
| 184 | +```c |
| 185 | +// no arguments, no result |
| 186 | +``` |
| 187 | + |
| 188 | +### WRITE FLASH PAGE (0x0006) |
| 189 | + |
| 190 | +Write a single page of flash memory. |
| 191 | + |
| 192 | +```c |
| 193 | +struct HF2_WRITE_FLASH_PAGE_Command { |
| 194 | + uint32_t target_addr; |
| 195 | + uint8_t data[flash_page_size]; |
| 196 | +}; |
| 197 | +// no result |
| 198 | +``` |
| 199 | + |
| 200 | +### CHKSUM PAGES (0x0007) |
| 201 | + |
| 202 | +Compute checksum of a number of pages. |
| 203 | +Maximum value for ``num_pages`` is ``max_message_size / 2 - 2``. |
| 204 | +The checksum algorithm used is CRC-16-CCITT. |
| 205 | + |
| 206 | +```c |
| 207 | +struct HF2_CHKSUM_PAGES_Command { |
| 208 | + uint32_t target_addr; |
| 209 | + uint32_t num_pages; |
| 210 | +}; |
| 211 | +struct HF2_CHKSUM_PAGES_Result { |
| 212 | + uint16_t chksums[0 /* num_pages */]; |
| 213 | +}; |
| 214 | +``` |
| 215 | + |
| 216 | +### READ WORDS (0x0008) |
| 217 | + |
| 218 | +Read a number of words from memory. |
| 219 | +Memory is read word by word (and not byte by byte), and ``target_addr`` must |
| 220 | +be suitably aligned. This is to support reading of special IO regions. |
| 221 | + |
| 222 | +```c |
| 223 | +struct HF2_READ_WORDS_Command { |
| 224 | + uint32_t target_addr; |
| 225 | + uint32_t num_words; |
| 226 | +}; |
| 227 | +struct HF2_READ_WORDS_Result { |
| 228 | + uint32_t words[num_words]; |
| 229 | +}; |
| 230 | +``` |
| 231 | + |
| 232 | +### WRITE WORDS (0x0009) |
| 233 | + |
| 234 | +Dual of READ WORDS, with the same constraints. |
| 235 | + |
| 236 | +```c |
| 237 | +struct HF2_WRITE_WORDS_Command { |
| 238 | + uint32_t target_addr; |
| 239 | + uint32_t num_words; |
| 240 | + uint32_t words[num_words]; |
| 241 | +}; |
| 242 | +// no result |
| 243 | +``` |
| 244 | + |
| 245 | +## Extensibility |
| 246 | + |
| 247 | +The HF2 protocol is easy to extend with new command messages. The command ids |
| 248 | +you introduce should be chosen at random. This ensures very low probability of |
| 249 | +conflict between different extensions. |
| 250 | + |
| 251 | +While numbers like `0x42420123`, `0x10001`, or `0xdeaff00d` may look random, |
| 252 | +others are likely to use them as well and a conflict might occur. |
| 253 | +Please also do not use numbers below `0xffff`, as these are standardized here. |
| 254 | +Ideally, use one of the following commands (or similar) to generate a random number: |
| 255 | + |
| 256 | +```bash |
| 257 | +node -p "require('crypto').randomBytes(4).toString('hex')" |
| 258 | +# or slightly worse: |
| 259 | +printf "%04x%04x\n" $RANDOM $RANDOM |
| 260 | +``` |
| 261 | + |
| 262 | +If you change the behavior of a command, even just extend what it can do, and |
| 263 | +there is even a remote possibility of devices or interface applications using |
| 264 | +it in the wild, it's best to introduce a new command, and possibly have the |
| 265 | +device handle both (fall-through `switch` cases are useful here). |
| 266 | + |
| 267 | +## Detection of HF2 devices |
| 268 | + |
| 269 | +## Notes |
| 270 | + |
| 271 | +If the device exposes both HID and WebUSB, it has to use two separate |
| 272 | +interfaces. |
| 273 | + |
0 commit comments