aboutsummaryrefslogtreecommitdiffhomepage
path: root/builder
diff options
context:
space:
mode:
Diffstat (limited to 'builder')
-rw-r--r--builder/build.go7
-rw-r--r--builder/nrfutil.go114
2 files changed, 103 insertions, 18 deletions
diff --git a/builder/build.go b/builder/build.go
index fcd7a2f07..d95519b33 100644
--- a/builder/build.go
+++ b/builder/build.go
@@ -909,13 +909,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
case "nrf-dfu":
// special format for nrfutil for Nordic chips
- tmphexpath := filepath.Join(tmpdir, "main.hex")
- err := objcopy(result.Executable, tmphexpath, "hex")
- if err != nil {
- return result, err
- }
result.Binary = filepath.Join(tmpdir, "main"+outext)
- err = makeDFUFirmwareImage(config.Options, tmphexpath, result.Binary)
+ err = makeDFUFirmwareImage(config.Options, result.Executable, result.Binary)
if err != nil {
return result, err
}
diff --git a/builder/nrfutil.go b/builder/nrfutil.go
index 73b04925c..6f33eb7c6 100644
--- a/builder/nrfutil.go
+++ b/builder/nrfutil.go
@@ -1,27 +1,117 @@
package builder
import (
- "fmt"
- "io"
- "os/exec"
+ "archive/zip"
+ "bytes"
+ "encoding/binary"
+ "encoding/json"
+ "os"
+ "github.com/sigurn/crc16"
"github.com/tinygo-org/tinygo/compileopts"
)
-// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrfutil%2FUG%2Fnrfutil%2Fnrfutil_intro.html
+// 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 {
- cmdLine := []string{"nrfutil", "pkg", "generate", "--hw-version", "52", "--sd-req", "0x0", "--debug-mode", "--application", infile, outfile}
+ // 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
+ }
- if options.PrintCommands != nil {
- options.PrintCommands(cmdLine[0], cmdLine[1:]...)
+ // 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
}
- cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
- cmd.Stdout = io.Discard
- err := cmd.Run()
+ // Write the init packet to the zip file.
+ datw, err := w.Create("application.dat")
if err != nil {
- return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
+ return err
}
- return nil
+ _, 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)
}