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 }