Skip to content

Commit 7d03ee4

Browse files
committed
Add HF2 spec
1 parent 40f9609 commit 7d03ee4

File tree

4 files changed

+313
-25
lines changed

4 files changed

+313
-25
lines changed

.clang-format

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
BasedOnStyle: LLVM
2+
IndentWidth: 4
3+
UseTab: Never
4+
ColumnLimit: 100

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ flash.uf2
44
flash.bin
55

66
built/
7+
uf2tool/uf2hid.h

hf2.md

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
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

Comments
 (0)