287 lines
8 KiB
Markdown
287 lines
8 KiB
Markdown
# HID Flashing Format
|
|
|
|
HF2 (HID Flashing Format) is a protocol and message format intended for
|
|
communication with embedded devices. Basic functions supported include:
|
|
|
|
* serial communication for `printf()` debugging etc.
|
|
* flashing (updating device firmware)
|
|
* debugger interfaces
|
|
|
|
It is optimized for packet formats, where packets are around 64 bytes long.
|
|
It will work for smaller packets, of at least a few bytes, or bigger
|
|
ones, but be less efficient.
|
|
|
|
In particular, it is suitable for running over USB HID (Human Interface Device),
|
|
which is widely supported in various operating systems without the need for kernel-space
|
|
drivers. It is also possible to run the protocol over a WebUSB link with either a single
|
|
interrupt endpoint or two bulk endpoints, as well as using just the control pipe,
|
|
allowing direct access from supported browsers.
|
|
|
|
## Raw message format
|
|
|
|
HF2 messages are composed of packets. Packets are up to 64 bytes long.
|
|
Messages sent from host to device and vice versa have the same basic format.
|
|
|
|
The first byte of each packet indicates:
|
|
* length of the remaining data (payload) in the packet, in the lower 6 bits, i.e., between 0 and 63 inclusive
|
|
* the type of the packet, in the two high bits.
|
|
|
|
| Bit 7 | Bit 6 | Hex | Meaning
|
|
|-------|-------|------|----------------------------------------------
|
|
| 0 | 0 | 0x00 | Inner packet of a command message
|
|
| 0 | 1 | 0x40 | Final packet of a command message
|
|
| 1 | 0 | 0x80 | Serial `stdout`
|
|
| 1 | 1 | 0xC0 | Serial `stderr`
|
|
|
|
Serial messages are thus between 0 and 63 bytes in length.
|
|
|
|
Command messages can have any length (though devices will typically limit
|
|
it to the native flash page size + 64 bytes). They consist of a zero or more
|
|
inner packets, followed by a single final packet. Any of these packets
|
|
can carry between 0 and 63 bytes of payload.
|
|
|
|
For example:
|
|
|
|
```
|
|
Packet 0: 83 01 02 03 AB FF FF FF
|
|
Packet 1: 85 04 05 06 07 08
|
|
Packet 2: 80 DE 42 42 42 42 FF FF
|
|
Packet 3: D0 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 FF FF FF
|
|
--->
|
|
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
|
|
```
|
|
|
|
Note that packets can be longer than the first byte requires (packets 0, 2, and 3 are).
|
|
The additional data should be discarded.
|
|
This is due to various HID implementations imposing an exact 64 byte packet size.
|
|
|
|
Different command messages cannot be interleaved.
|
|
|
|
Serial messages should not be interleaved with command messages (though it would be
|
|
technically possible).
|
|
|
|
## Higher-level message format
|
|
|
|
### Serial messages
|
|
|
|
Serial messages are meant for `printf()` style debugging
|
|
and general simple data output from the device.
|
|
If the device supports only one serial channel, it should support
|
|
the `stdout` channel. Otherwise, `stdout` is meant
|
|
for data output (eg., logging measurements), and `stderr`
|
|
is meant for `printf()` debugging.
|
|
|
|
The logging application on the host may choose to send these
|
|
two channels into a single output stream.
|
|
|
|
Serial messages contain between `0` and `63` bytes of payload data.
|
|
Size of `0` can be used as a keep-alive packet if needed.
|
|
|
|
### Command messages
|
|
|
|
Command structure:
|
|
|
|
```c
|
|
struct Command {
|
|
uint32_t command_id;
|
|
uint16_t tag;
|
|
uint8_t reserved0;
|
|
uint8_t reserved1;
|
|
uint8_t data[...];
|
|
};
|
|
|
|
struct Response {
|
|
uint16_t tag;
|
|
uint8_t status;
|
|
uint8_t status_info;
|
|
uint8_t data[...];
|
|
};
|
|
```
|
|
|
|
All words in HF2 are little endian.
|
|
|
|
The `tag` is an arbitrary number set by the host, for example as sequence
|
|
number. The response should repeat the `tag`.
|
|
|
|
The two reserved bytes in the command should be sent as zero and ignored by the device.
|
|
|
|
The response status is one of the following:
|
|
* `0x00` - command understood and executed correctly
|
|
* `0x01` - command not understood
|
|
* `0x02` - command execution error
|
|
|
|
Note, that embedded devices might crash on invalid arguments, instead
|
|
of returning errors. OTOH, the devices should always handle invalid commands with
|
|
`0x01` status.
|
|
|
|
In case of non-zero status, the `status_info` field can contain additional information.
|
|
|
|
The host shall not send a new command, until the previous one was responded to.
|
|
TODO does this make sense? maybe just let USB flow control handle this?
|
|
|
|
## Standard commands
|
|
|
|
Below we list standard commands. Not all commands have to be supported by all
|
|
devices.
|
|
|
|
When the C fragment states `no results`, it means just a response
|
|
with zero status and no additional data should be expected.
|
|
|
|
### BININFO (0x0001)
|
|
|
|
This command states the current mode of the device:
|
|
* ``mode == 0x01`` - bootloader, and thus flashing of user-space programs is allowed
|
|
* ``mode == 0x02`` - user-space mode.
|
|
It also returns the size of flash page size (flashing needs to be done on page-by-page basis),
|
|
and the maximum size of message. It is always the case that
|
|
``max_message_size >= flash_page_size + 64``.
|
|
|
|
```c
|
|
struct HF2_BININFO_Result {
|
|
uint32_t mode;
|
|
uint32_t flash_page_size;
|
|
uint32_t flash_num_pages;
|
|
uint32_t max_message_size;
|
|
uint32_t family_id; // optional
|
|
};
|
|
```
|
|
|
|
### INFO (0x0002)
|
|
|
|
Various device information.
|
|
The result is a character array. See `INFO_UF2.TXT` in UF2 format for details.
|
|
|
|
```c
|
|
// no arguments
|
|
struct HF2_INFO_Result {
|
|
uint8_t info[...];
|
|
};
|
|
```
|
|
|
|
|
|
### RESET INTO APP (0x0003)
|
|
|
|
Reset the device into user-space app. Usually, no response at all will arrive for this command.
|
|
|
|
```c
|
|
// no arguments, no result
|
|
```
|
|
|
|
|
|
### RESET INTO (0x0004)
|
|
|
|
Reset the device into bootloader, usually for flashing. Usually, no response at all will arrive for this command.
|
|
|
|
```c
|
|
// no arguments, no result
|
|
```
|
|
|
|
### START FLASH (0x0005)
|
|
|
|
When issued in bootloader mode, it has no effect.
|
|
In user-space mode it causes handover to bootloader.
|
|
A `BININFO` command can be issued to verify that.
|
|
|
|
```c
|
|
// no arguments, no result
|
|
```
|
|
|
|
### WRITE FLASH PAGE (0x0006)
|
|
|
|
Write a single page of flash memory.
|
|
|
|
```c
|
|
struct HF2_WRITE_FLASH_PAGE_Command {
|
|
uint32_t target_addr;
|
|
uint8_t data[flash_page_size];
|
|
};
|
|
// no result
|
|
```
|
|
|
|
### CHKSUM PAGES (0x0007)
|
|
|
|
Compute checksum of a number of pages.
|
|
Maximum value for ``num_pages`` is ``max_message_size / 2 - 2``.
|
|
The checksum algorithm used is CRC-16-CCITT.
|
|
|
|
```c
|
|
struct HF2_CHKSUM_PAGES_Command {
|
|
uint32_t target_addr;
|
|
uint32_t num_pages;
|
|
};
|
|
struct HF2_CHKSUM_PAGES_Result {
|
|
uint16_t chksums[0 /* num_pages */];
|
|
};
|
|
```
|
|
|
|
### READ WORDS (0x0008)
|
|
|
|
Read a number of words from memory.
|
|
Memory is read word by word (and not byte by byte), and ``target_addr`` must
|
|
be suitably aligned. This is to support reading of special IO regions.
|
|
|
|
```c
|
|
struct HF2_READ_WORDS_Command {
|
|
uint32_t target_addr;
|
|
uint32_t num_words;
|
|
};
|
|
struct HF2_READ_WORDS_Result {
|
|
uint32_t words[num_words];
|
|
};
|
|
```
|
|
|
|
### WRITE WORDS (0x0009)
|
|
|
|
Dual of READ WORDS, with the same constraints.
|
|
|
|
```c
|
|
struct HF2_WRITE_WORDS_Command {
|
|
uint32_t target_addr;
|
|
uint32_t num_words;
|
|
uint32_t words[num_words];
|
|
};
|
|
// no result
|
|
```
|
|
|
|
### DMESG (0x0010)
|
|
|
|
Return internal log buffer if any.
|
|
The result is a character array.
|
|
|
|
```c
|
|
// no arguments
|
|
struct HF2_DMESG_Result {
|
|
uint8_t logs[...];
|
|
};
|
|
```
|
|
|
|
|
|
## Extensibility
|
|
|
|
The HF2 protocol is easy to extend with new command messages. The command ids
|
|
you introduce should be chosen at random. This ensures very low probability of
|
|
conflict between different extensions.
|
|
|
|
While numbers like `0x42420123`, `0x10001`, or `0xdeaff00d` may look random,
|
|
others are likely to use them as well and a conflict might occur.
|
|
Please also do not use numbers below `0xffff`, as these are standardized here.
|
|
Ideally, use one of the following commands (or similar) to generate a random number:
|
|
|
|
```bash
|
|
node -p "require('crypto').randomBytes(4).toString('hex')"
|
|
# or slightly worse:
|
|
printf "%04x%04x\n" $RANDOM $RANDOM
|
|
```
|
|
|
|
If you change the behavior of a command, even just extend what it can do, and
|
|
there is even a remote possibility of devices or interface applications using
|
|
it in the wild, it's best to introduce a new command, and possibly have the
|
|
device handle both (fall-through `switch` cases are useful here).
|
|
|
|
## Detection of HF2 devices
|
|
|
|
## Notes
|
|
|
|
If the device exposes both HID and WebUSB, it has to use two separate
|
|
interfaces.
|
|
|