mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
6435f62dcc
This avoids a dependency on nrfutil. I have verified that it creates equivalent zip files to a wasp-os DFU zip file I downloaded here: https://github.com/wasp-os/wasp-os/releases/ I have also tested that it produces valid DFU files that can be uploaded using the dfu.py program here to my PineTime: https://github.com/wasp-os/ota-dfu-python/tree/3d6fd30d33c2b20bc86ff6b9269fddf4a1d4c7c6 There are some minor differences in the generated file that should not matter in practice (JSON whitespace, firmware file name, zip compression).
118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
package builder
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"os"
|
|
|
|
"github.com/sigurn/crc16"
|
|
"github.com/tinygo-org/tinygo/compileopts"
|
|
)
|
|
|
|
// Structure of the manifest.json file.
|
|
type jsonManifest struct {
|
|
Manifest struct {
|
|
Application struct {
|
|
BinaryFile string `json:"bin_file"`
|
|
DataFile string `json:"dat_file"`
|
|
InitPacketData nrfInitPacket `json:"init_packet_data"`
|
|
} `json:"application"`
|
|
DFUVersion float64 `json:"dfu_version"` // yes, this is a JSON number, not a string
|
|
} `json:"manifest"`
|
|
}
|
|
|
|
// Structure of the init packet.
|
|
// Source:
|
|
// https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/master/lib/sdk11/components/libraries/bootloader_dfu/dfu_init.h#L47-L57
|
|
type nrfInitPacket struct {
|
|
ApplicationVersion uint32 `json:"application_version"`
|
|
DeviceRevision uint16 `json:"device_revision"`
|
|
DeviceType uint16 `json:"device_type"`
|
|
FirmwareCRC16 uint16 `json:"firmware_crc16"`
|
|
SoftDeviceRequired []uint16 `json:"softdevice_req"` // this is actually a variable length array
|
|
}
|
|
|
|
// Create the init packet (the contents of application.dat).
|
|
func (p nrfInitPacket) createInitPacket() []byte {
|
|
buf := &bytes.Buffer{}
|
|
binary.Write(buf, binary.LittleEndian, p.DeviceType) // uint16_t device_type;
|
|
binary.Write(buf, binary.LittleEndian, p.DeviceRevision) // uint16_t device_rev;
|
|
binary.Write(buf, binary.LittleEndian, p.ApplicationVersion) // uint32_t app_version;
|
|
binary.Write(buf, binary.LittleEndian, uint16(len(p.SoftDeviceRequired))) // uint16_t softdevice_len;
|
|
binary.Write(buf, binary.LittleEndian, p.SoftDeviceRequired) // uint16_t softdevice[1];
|
|
binary.Write(buf, binary.LittleEndian, p.FirmwareCRC16)
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// Make a Nordic DFU firmware image from an ELF file.
|
|
func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string) error {
|
|
// Read ELF file as input and convert it to a binary image file.
|
|
_, data, err := extractROM(infile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create the zip file in memory.
|
|
// It won't be very large anyway.
|
|
buf := &bytes.Buffer{}
|
|
w := zip.NewWriter(buf)
|
|
|
|
// Write the application binary to the zip file.
|
|
binw, err := w.Create("application.bin")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = binw.Write(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create the init packet.
|
|
initPacket := nrfInitPacket{
|
|
ApplicationVersion: 0xffff_ffff, // appears to be unused by the Adafruit bootloader
|
|
DeviceRevision: 0xffff, // DFU_DEVICE_REVISION_EMPTY
|
|
DeviceType: 0x0052, // ADAFRUIT_DEVICE_TYPE
|
|
FirmwareCRC16: crc16.Checksum(data, crc16.MakeTable(crc16.CRC16_CCITT_FALSE)),
|
|
SoftDeviceRequired: []uint16{0xfffe}, // DFU_SOFTDEVICE_ANY
|
|
}
|
|
|
|
// Write the init packet to the zip file.
|
|
datw, err := w.Create("application.dat")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = datw.Write(initPacket.createInitPacket())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create the JSON manifest.
|
|
manifest := &jsonManifest{}
|
|
manifest.Manifest.Application.BinaryFile = "application.bin"
|
|
manifest.Manifest.Application.DataFile = "application.dat"
|
|
manifest.Manifest.Application.InitPacketData = initPacket
|
|
manifest.Manifest.DFUVersion = 0.5
|
|
|
|
// Write the JSON manifest to the file.
|
|
jsonw, err := w.Create("manifest.json")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
enc := json.NewEncoder(jsonw)
|
|
enc.SetIndent("", " ")
|
|
err = enc.Encode(manifest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Finish the zip file.
|
|
err = w.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.WriteFile(outfile, buf.Bytes(), 0o666)
|
|
}
|