github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/builder/nrfutil.go (about)

     1  package builder
     2  
     3  import (
     4  	"archive/zip"
     5  	"bytes"
     6  	"encoding/binary"
     7  	"encoding/json"
     8  	"os"
     9  
    10  	"github.com/sigurn/crc16"
    11  	"github.com/tinygo-org/tinygo/compileopts"
    12  )
    13  
    14  // Structure of the manifest.json file.
    15  type jsonManifest struct {
    16  	Manifest struct {
    17  		Application struct {
    18  			BinaryFile     string        `json:"bin_file"`
    19  			DataFile       string        `json:"dat_file"`
    20  			InitPacketData nrfInitPacket `json:"init_packet_data"`
    21  		} `json:"application"`
    22  		DFUVersion float64 `json:"dfu_version"` // yes, this is a JSON number, not a string
    23  	} `json:"manifest"`
    24  }
    25  
    26  // Structure of the init packet.
    27  // Source:
    28  // https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/master/lib/sdk11/components/libraries/bootloader_dfu/dfu_init.h#L47-L57
    29  type nrfInitPacket struct {
    30  	ApplicationVersion uint32   `json:"application_version"`
    31  	DeviceRevision     uint16   `json:"device_revision"`
    32  	DeviceType         uint16   `json:"device_type"`
    33  	FirmwareCRC16      uint16   `json:"firmware_crc16"`
    34  	SoftDeviceRequired []uint16 `json:"softdevice_req"` // this is actually a variable length array
    35  }
    36  
    37  // Create the init packet (the contents of application.dat).
    38  func (p nrfInitPacket) createInitPacket() []byte {
    39  	buf := &bytes.Buffer{}
    40  	binary.Write(buf, binary.LittleEndian, p.DeviceType)                      // uint16_t device_type;
    41  	binary.Write(buf, binary.LittleEndian, p.DeviceRevision)                  // uint16_t device_rev;
    42  	binary.Write(buf, binary.LittleEndian, p.ApplicationVersion)              // uint32_t app_version;
    43  	binary.Write(buf, binary.LittleEndian, uint16(len(p.SoftDeviceRequired))) // uint16_t softdevice_len;
    44  	binary.Write(buf, binary.LittleEndian, p.SoftDeviceRequired)              // uint16_t softdevice[1];
    45  	binary.Write(buf, binary.LittleEndian, p.FirmwareCRC16)
    46  	return buf.Bytes()
    47  }
    48  
    49  // Make a Nordic DFU firmware image from an ELF file.
    50  func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string) error {
    51  	// Read ELF file as input and convert it to a binary image file.
    52  	_, data, err := extractROM(infile)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	// Create the zip file in memory.
    58  	// It won't be very large anyway.
    59  	buf := &bytes.Buffer{}
    60  	w := zip.NewWriter(buf)
    61  
    62  	// Write the application binary to the zip file.
    63  	binw, err := w.Create("application.bin")
    64  	if err != nil {
    65  		return err
    66  	}
    67  	_, err = binw.Write(data)
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	// Create the init packet.
    73  	initPacket := nrfInitPacket{
    74  		ApplicationVersion: 0xffff_ffff, // appears to be unused by the Adafruit bootloader
    75  		DeviceRevision:     0xffff,      // DFU_DEVICE_REVISION_EMPTY
    76  		DeviceType:         0x0052,      // ADAFRUIT_DEVICE_TYPE
    77  		FirmwareCRC16:      crc16.Checksum(data, crc16.MakeTable(crc16.CRC16_CCITT_FALSE)),
    78  		SoftDeviceRequired: []uint16{0xfffe}, // DFU_SOFTDEVICE_ANY
    79  	}
    80  
    81  	// Write the init packet to the zip file.
    82  	datw, err := w.Create("application.dat")
    83  	if err != nil {
    84  		return err
    85  	}
    86  	_, err = datw.Write(initPacket.createInitPacket())
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	// Create the JSON manifest.
    92  	manifest := &jsonManifest{}
    93  	manifest.Manifest.Application.BinaryFile = "application.bin"
    94  	manifest.Manifest.Application.DataFile = "application.dat"
    95  	manifest.Manifest.Application.InitPacketData = initPacket
    96  	manifest.Manifest.DFUVersion = 0.5
    97  
    98  	// Write the JSON manifest to the file.
    99  	jsonw, err := w.Create("manifest.json")
   100  	if err != nil {
   101  		return err
   102  	}
   103  	enc := json.NewEncoder(jsonw)
   104  	enc.SetIndent("", "    ")
   105  	err = enc.Encode(manifest)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	// Finish the zip file.
   111  	err = w.Close()
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	return os.WriteFile(outfile, buf.Bytes(), 0o666)
   117  }