Skip to content

Commit 625b338

Browse files
authored
Add firmware flash command (#53)
* Add simple Flash function to run uploaders * Implemented new module Flashers to flash firmware and certificates * Add flash-firmware command * Enhance flash-firmware command examples * Add some logging * Add logic to flash certificates * Fix some utilities functions * Add String function to CommandData * Made IndexBoard struct public and enhanced some firmwareindex methods * Rebases fixes * Sleep a bit after opening port to flash NINA firmware * Implemented flash-firmware command * Fix issue with port closing * Fixed firmware flashing * Rename Latest IndexBoard field to LatestFirmware * Moved flash-firmware to flash firmware subcommand * Fix tests
1 parent 34e0cc7 commit 625b338

File tree

12 files changed

+1620
-100
lines changed

12 files changed

+1620
-100
lines changed

cli/firmware/firmware.go

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func NewCommand() *cobra.Command {
3333
Example: " " + os.Args[0] + " firmware ...",
3434
}
3535

36+
firmwareCmd.AddCommand(NewFlashCommand())
3637
firmwareCmd.AddCommand(newListCommand())
3738
return firmwareCmd
3839
}

cli/firmware/flash.go

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
FirmwareUploader
3+
Copyright (c) 2021 Arduino LLC. All right reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package firmware
21+
22+
import (
23+
"bytes"
24+
"os"
25+
"path/filepath"
26+
"strings"
27+
"time"
28+
29+
"github.com/arduino/FirmwareUploader/flasher"
30+
"github.com/arduino/FirmwareUploader/indexes"
31+
"github.com/arduino/FirmwareUploader/indexes/download"
32+
"github.com/arduino/FirmwareUploader/indexes/firmwareindex"
33+
programmer "github.com/arduino/FirmwareUploader/programmers"
34+
"github.com/arduino/arduino-cli/arduino/serialutils"
35+
"github.com/arduino/arduino-cli/cli/errorcodes"
36+
"github.com/arduino/arduino-cli/cli/feedback"
37+
"github.com/arduino/go-properties-orderedmap"
38+
"github.com/sirupsen/logrus"
39+
"github.com/spf13/cobra"
40+
)
41+
42+
var (
43+
fqbn string
44+
address string
45+
module string
46+
)
47+
48+
// NewCommand created a new `version` command
49+
func NewFlashCommand() *cobra.Command {
50+
command := &cobra.Command{
51+
Use: "flash",
52+
Short: "Flashes firmwares to board.",
53+
Long: "Flashes specified module firmware to board at specified address. Module name and version can be omitted to install latest version.",
54+
Example: "" +
55+
" " + os.Args[0] + " flash --fqbn arduino:samd:mkr1000 --address COM10 --module [email protected]\n" +
56+
" " + os.Args[0] + " flash -b arduino:samd:mkr1000 -a COM10 -m WINC15000\n" +
57+
" " + os.Args[0] + " flash -b arduino:samd:mkr1000 -a COM10\n",
58+
Args: cobra.NoArgs,
59+
Run: run,
60+
}
61+
62+
command.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:samd:mkr1000, arduino:mbed_nano:nanorp2040connect")
63+
command.Flags().StringVarP(&address, "address", "a", "", "Upload port, e.g.: COM10, /dev/ttyACM0")
64+
command.Flags().StringVarP(&module, "module", "m", "", "Firmware module ID, e.g.: WINC1500, NINA")
65+
return command
66+
}
67+
68+
func run(cmd *cobra.Command, args []string) {
69+
packageIndex, err := indexes.GetPackageIndex()
70+
if err != nil {
71+
feedback.Errorf("Can't load package index: %s", err)
72+
os.Exit(errorcodes.ErrGeneric)
73+
}
74+
75+
firmwareIndex, err := indexes.GetFirmwareIndex()
76+
if err != nil {
77+
feedback.Errorf("Can't load firmware index: %s", err)
78+
os.Exit(errorcodes.ErrGeneric)
79+
}
80+
81+
if fqbn == "" {
82+
feedback.Errorf("Error during firmware flashing: missing board fqbn")
83+
os.Exit(errorcodes.ErrBadArgument)
84+
}
85+
86+
if address == "" {
87+
feedback.Errorf("Error during firmware flashing: missing board address")
88+
os.Exit(errorcodes.ErrBadArgument)
89+
}
90+
91+
board := firmwareIndex.GetBoard(fqbn)
92+
if board == nil {
93+
feedback.Errorf("Can't find board with %s fqbn", fqbn)
94+
os.Exit(errorcodes.ErrBadArgument)
95+
}
96+
97+
// Get module name if not specified
98+
moduleName := ""
99+
moduleVersion := ""
100+
if module == "" {
101+
moduleName = board.Module
102+
} else {
103+
moduleSplit := strings.Split(module, "@")
104+
if len(moduleSplit) == 2 {
105+
moduleName = moduleSplit[0]
106+
moduleVersion = moduleSplit[1]
107+
} else {
108+
moduleName = module
109+
}
110+
}
111+
// Normalize module name
112+
moduleName = strings.ToUpper(moduleName)
113+
114+
var firmware *firmwareindex.IndexFirmware
115+
if moduleVersion == "" {
116+
firmware = board.LatestFirmware
117+
} else {
118+
firmware = board.GetFirmware(moduleVersion)
119+
}
120+
if firmware == nil {
121+
feedback.Errorf("Error getting firmware for board: %s", fqbn)
122+
os.Exit(errorcodes.ErrGeneric)
123+
}
124+
125+
firmwareFile, err := download.DownloadFirmware(firmware)
126+
if err != nil {
127+
feedback.Errorf("Error downloading firmware from %s: %s", firmware.URL, err)
128+
os.Exit(errorcodes.ErrGeneric)
129+
}
130+
131+
toolRelease := indexes.GetToolRelease(packageIndex, board.Uploader)
132+
if toolRelease == nil {
133+
feedback.Errorf("Error getting upload tool %s for board %s", board.Uploader, board.Fqbn)
134+
os.Exit(errorcodes.ErrGeneric)
135+
}
136+
uploadToolDir, err := download.DownloadTool(toolRelease)
137+
if err != nil {
138+
feedback.Errorf("Error downloading tool %s: %s", board.Uploader, err)
139+
os.Exit(errorcodes.ErrGeneric)
140+
}
141+
142+
loaderSketchPath, err := download.DownloadLoaderSketch(board.LoaderSketch)
143+
if err != nil {
144+
feedback.Errorf("Error downloading loader sketch from %s: %s", board.LoaderSketch.URL, err)
145+
os.Exit(errorcodes.ErrGeneric)
146+
}
147+
148+
loaderSketch := strings.ReplaceAll(loaderSketchPath.String(), loaderSketchPath.Ext(), "")
149+
150+
uploaderCommand := board.GetUploaderCommand()
151+
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{tool_dir}", filepath.FromSlash(uploadToolDir.String()))
152+
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{serial.port.file}", address)
153+
uploaderCommand = strings.ReplaceAll(uploaderCommand, "{loader.sketch}", loaderSketch)
154+
155+
commandLine, err := properties.SplitQuotedString(uploaderCommand, "\"", false)
156+
if err != nil {
157+
feedback.Errorf(`Error splitting command line "%s": %s`, uploaderCommand, err)
158+
os.Exit(errorcodes.ErrGeneric)
159+
}
160+
161+
// Check if board needs a 1200bps touch for upload
162+
if board.UploadTouch {
163+
logrus.Info("Putting board into bootloader mode")
164+
_, err := serialutils.Reset(address, board.UploadWait, nil)
165+
if err != nil {
166+
feedback.Errorf("Error during firmware flashing: missing board address")
167+
os.Exit(errorcodes.ErrGeneric)
168+
}
169+
}
170+
171+
// Flash loader Sketch
172+
flashOut := new(bytes.Buffer)
173+
flashErr := new(bytes.Buffer)
174+
// var err error
175+
if feedback.GetFormat() == feedback.JSON {
176+
err = programmer.Flash(commandLine, flashOut, flashErr)
177+
} else {
178+
err = programmer.Flash(commandLine, os.Stdout, os.Stderr)
179+
}
180+
if err != nil {
181+
feedback.Errorf("Error during firmware flashing: %s", err)
182+
os.Exit(errorcodes.ErrGeneric)
183+
}
184+
185+
// Wait a bit after flashing the loader sketch for the board to become
186+
// available again.
187+
time.Sleep(1 * time.Second)
188+
189+
// Get flasher depending on which module to use
190+
var f flasher.Flasher
191+
switch moduleName {
192+
case "NINA":
193+
f, err = flasher.NewNinaFlasher(address)
194+
case "SARA":
195+
f, err = flasher.NewSaraFlasher(address)
196+
case "WINC":
197+
f, err = flasher.NewWincFlasher(address)
198+
}
199+
if err != nil {
200+
feedback.Errorf("Error during firmware flashing: %s", err)
201+
os.Exit(errorcodes.ErrGeneric)
202+
}
203+
defer f.Close()
204+
205+
if err := f.FlashFirmware(firmwareFile); err != nil {
206+
feedback.Errorf("Error during firmware flashing: %s", err)
207+
os.Exit(errorcodes.ErrGeneric)
208+
}
209+
}

cli/firmware/list.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func list(fqbn string) {
7171
BoardFQBN: board.Fqbn,
7272
Module: board.Module,
7373
FirmwareVersion: firmware.Version,
74-
Latest: board.Latest == firmware,
74+
Latest: board.LatestFirmware == firmware,
7575
})
7676
}
7777
}

flasher/certificate.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
FirmwareUploader
3+
Copyright (c) 2021 Arduino LLC. All right reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package flasher
21+
22+
import (
23+
"bytes"
24+
"crypto/rsa"
25+
"crypto/sha1"
26+
"crypto/x509"
27+
"crypto/x509/pkix"
28+
"encoding/asn1"
29+
"encoding/binary"
30+
"time"
31+
32+
"github.com/sirupsen/logrus"
33+
)
34+
35+
func calculateNameSha1(cert *x509.Certificate) (b []byte, err error) {
36+
nameSha1 := sha1.New()
37+
38+
var subjectDistinguishedNameSequence pkix.RDNSequence
39+
40+
if _, err = asn1.Unmarshal(cert.RawSubject, &subjectDistinguishedNameSequence); err != nil {
41+
logrus.Error(err)
42+
return nil, err
43+
}
44+
45+
for _, dn := range subjectDistinguishedNameSequence {
46+
nameSha1.Write([]byte(dn[0].Value.(string)))
47+
}
48+
49+
b = nameSha1.Sum(nil)
50+
51+
return
52+
}
53+
54+
func convertTime(time time.Time) ([]byte, error) {
55+
asn1Bytes, err := asn1.Marshal(time)
56+
if err != nil {
57+
logrus.Error(err)
58+
return nil, err
59+
}
60+
61+
b := bytes.Repeat([]byte{0x00}, 20) // value must be zero bytes
62+
copy(b, asn1Bytes[2:]) // copy but drop the first two bytes
63+
64+
return b, err
65+
}
66+
67+
func modulusN(publicKey rsa.PublicKey) []byte {
68+
return publicKey.N.Bytes()
69+
}
70+
71+
func publicExponent(publicKey rsa.PublicKey) []byte {
72+
b := make([]byte, 4)
73+
binary.BigEndian.PutUint32(b, uint32(publicKey.E))
74+
// strip leading zeros
75+
for b[0] == 0 {
76+
b = b[1:]
77+
}
78+
return b
79+
}
80+
81+
func uint16ToBytes(i int) []byte {
82+
b := make([]byte, 2)
83+
binary.LittleEndian.PutUint16(b, uint16(i))
84+
return b
85+
}

0 commit comments

Comments
 (0)