|
16 | 16 | package updater |
17 | 17 |
|
18 | 18 | import ( |
| 19 | + "bytes" |
19 | 20 | "context" |
20 | 21 | "encoding/hex" |
| 22 | + "errors" |
21 | 23 | "fmt" |
| 24 | + "log/slog" |
| 25 | + "os/exec" |
22 | 26 | "runtime" |
23 | 27 | "strconv" |
24 | 28 | "strings" |
@@ -112,27 +116,13 @@ type FlashEvent struct { |
112 | 116 | type FlashCallback func(FlashEvent) |
113 | 117 |
|
114 | 118 | func FlashBoard(ctx context.Context, downloadedImagePath *paths.Path, version string, preserveUser bool, callback FlashCallback) error { |
115 | | - flashDir, err := searchForFlashDir(downloadedImagePath) |
| 119 | + qdlPath, cleanup, err := installQdl() |
116 | 120 | if err != nil { |
117 | 121 | return err |
118 | 122 | } |
| 123 | + defer cleanup() |
119 | 124 |
|
120 | | - qdlDir, err := paths.MkTempDir("", "qdl-") |
121 | | - if err != nil { |
122 | | - return err |
123 | | - } |
124 | | - defer func() { _ = qdlDir.RemoveAll() }() |
125 | | - |
126 | | - qdlPath := qdlDir.Join("qdl") |
127 | | - if runtime.GOOS == "windows" { |
128 | | - qdlPath = qdlDir.Join("qdl.exe") |
129 | | - } |
130 | | - |
131 | | - err = qdlPath.WriteFile(artifacts.QdlBinary) |
132 | | - if err != nil { |
133 | | - return err |
134 | | - } |
135 | | - err = qdlPath.Chmod(0755) |
| 125 | + flashDir, err := searchForFlashDir(downloadedImagePath) |
136 | 126 | if err != nil { |
137 | 127 | return err |
138 | 128 | } |
@@ -239,6 +229,27 @@ func searchForFlashDir(extractPath *paths.Path) (*paths.Path, error) { |
239 | 229 | } |
240 | 230 | } |
241 | 231 |
|
| 232 | +func installQdl() (*paths.Path, func(), error) { |
| 233 | + qdlDir, err := paths.MkTempDir("", "qdl-") |
| 234 | + if err != nil { |
| 235 | + return nil, nil, err |
| 236 | + } |
| 237 | + |
| 238 | + qdlPath := qdlDir.Join("qdl") |
| 239 | + if runtime.GOOS == "windows" { |
| 240 | + qdlPath = qdlDir.Join("qdl.exe") |
| 241 | + } |
| 242 | + |
| 243 | + if err = qdlPath.WriteFile(artifacts.QdlBinary); err != nil { |
| 244 | + return nil, nil, err |
| 245 | + } |
| 246 | + if err = qdlPath.Chmod(0755); err != nil { |
| 247 | + return nil, nil, err |
| 248 | + } |
| 249 | + |
| 250 | + return qdlPath, func() { _ = qdlDir.RemoveAll() }, nil |
| 251 | +} |
| 252 | + |
242 | 253 | // Checks the board GPT table and counts the number of partitions, this tells if the board supports preserving or not user's data. |
243 | 254 | func checkBoardGPTTable(ctx context.Context, qdlPath, flashDir *paths.Path) error { |
244 | 255 | dumpBinPath := qdlPath.Parent().Join("dump.bin") |
@@ -288,3 +299,43 @@ func checkBoardGPTTable(ctx context.Context, qdlPath, flashDir *paths.Path) erro |
288 | 299 |
|
289 | 300 | return nil |
290 | 301 | } |
| 302 | + |
| 303 | +// WantForQdlDevice waits for a QDL device to be connected. |
| 304 | +// This is like and hack because QDL does not have a specific command to wait for a device, |
| 305 | +// so we use the read command with a dummy ELF and XML file to detect when a device is connected. |
| 306 | +func WaitForQdlDevice(ctx context.Context) error { |
| 307 | + qdlPath, cleanup, err := installQdl() |
| 308 | + if err != nil { |
| 309 | + return err |
| 310 | + } |
| 311 | + defer cleanup() |
| 312 | + |
| 313 | + readXMLPath := qdlPath.Parent().Join("read.xml") |
| 314 | + if err := readXMLPath.WriteFile(artifacts.ReadXML); err != nil { |
| 315 | + return err |
| 316 | + } |
| 317 | + |
| 318 | + dummyBin := qdlPath.Parent().Join("dummy.elf") |
| 319 | + if err := dummyBin.WriteFile([]byte{}); err != nil { |
| 320 | + return err |
| 321 | + } |
| 322 | + |
| 323 | + cmd, err := paths.NewProcess(nil, qdlPath.String(), dummyBin.String(), readXMLPath.String(), "--debug") |
| 324 | + if err != nil { |
| 325 | + return err |
| 326 | + } |
| 327 | + cmd.SetDir(qdlPath.Parent().String()) |
| 328 | + if out, err := cmd.RunAndCaptureCombinedOutput(ctx); err != nil { |
| 329 | + slog.Debug("wait for qdl device command exit", "out", string(out), "err", err) |
| 330 | + var exitErr *exec.ExitError |
| 331 | + if errors.As(err, &exitErr) { |
| 332 | + const qdlExpectErrorStr = "USB: using out-chunk-size" |
| 333 | + if exitErr.ExitCode() == 1 && bytes.Contains(out, []byte(qdlExpectErrorStr)) { |
| 334 | + return nil |
| 335 | + } |
| 336 | + } |
| 337 | + return fmt.Errorf("error waiting for QDL device: %w: %s", err, out) |
| 338 | + } else { |
| 339 | + return fmt.Errorf("no QDL device found: %s", out) |
| 340 | + } |
| 341 | +} |
0 commit comments