Skip to content

Commit f600dd3

Browse files
authored
Merge pull request #161 from NordicSemiconductor/test-and-confirm-fix
DirectXIP Test & Confirm Fix
2 parents 21f1169 + b21b496 commit f600dd3

File tree

1 file changed

+54
-36
lines changed

1 file changed

+54
-36
lines changed

Source/Managers/DFU/FirmwareUpgradeManager.swift

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,6 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
216216
}
217217
}
218218

219-
private func verify() {
220-
objc_sync_setState(.confirm)
221-
if !paused {
222-
// This will confirm the image on slot 0
223-
log(msg: "Sending Confirm command...", atLevel: .verbose)
224-
imageManager.confirm(callback: confirmCallback)
225-
}
226-
}
227-
228219
private func eraseAppSettings() {
229220
objc_sync_setState(.eraseAppSettings)
230221
log(msg: "Erasing app settings...", atLevel: .verbose)
@@ -240,6 +231,28 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
240231
}
241232
}
242233

234+
/**
235+
Called in .test&Confirm mode after uploaded images have been sent 'Test' command, they
236+
are tested, then Reset, and now we need to Confirm all Images.
237+
*/
238+
private func testAndConfirmAfterReset() {
239+
if let untestedImage = images.first(where: { $0.uploaded && !$0.tested }) {
240+
self.fail(error: FirmwareUpgradeError.untestedImageFound(image: untestedImage.image, slot: untestedImage.slot))
241+
return
242+
}
243+
244+
if let firstUnconfirmedImage = images.first(where: {
245+
$0.uploaded && !$0.confirmed && !$0.confirmSent }
246+
) {
247+
confirm(firstUnconfirmedImage)
248+
mark(firstUnconfirmedImage, as: \.confirmSent)
249+
} else {
250+
// in .testAndConfirm, we test all uploaded images before Reset.
251+
// So if we're here after Reset and there's nothing to confirm, we're done.
252+
self.success()
253+
}
254+
}
255+
243256
private func success() {
244257
objc_sync_setState(.success)
245258

@@ -288,12 +301,12 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
288301
case .upload:
289302
imageManager.continueUpload()
290303
case .test:
291-
guard let nextImageToTest = self.images.first(where: { !$0.tested }) else { return }
304+
guard let nextImageToTest = self.images.first(where: { $0.uploaded && !$0.tested }) else { return }
292305
test(nextImageToTest)
293306
case .reset:
294307
reset()
295308
case .confirm:
296-
guard let nextImageToConfirm = self.images.first(where: { !$0.confirmed }) else { return }
309+
guard let nextImageToConfirm = self.images.first(where: { $0.uploaded && !$0.confirmed }) else { return }
297310
confirm(nextImageToConfirm)
298311
default:
299312
break
@@ -570,29 +583,31 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
570583
return
571584
}
572585

573-
for image in self.images {
574-
// Check that the image in secondary slot is pending (i.e. test succeeded).
575-
guard let secondary = responseImages.first(where: { $0.image == image.image && $0.slot == 1 }) else {
576-
self.fail(error: FirmwareUpgradeError.unknown("Unable to find secondary slot for image \(image.image) in Test Response."))
586+
for image in self.images where !image.tested {
587+
guard let targetSlot = responseImages.first(where: {
588+
$0.image == image.image && Data($0.hash) == image.hash
589+
}) else {
590+
self.fail(error: FirmwareUpgradeError.unknown("Unable to find Uploaded Image \(image.image) Slot \(image.slot) in Test Response."))
577591
return
578592
}
579593

580-
guard secondary.pending else {
594+
// Check the target image is pending (i.e. test succeeded).
595+
guard targetSlot.pending else {
581596
// For every image we upload, we need to send it the TEST Command.
582597
guard image.tested else {
583598
self.test(image)
584599
return
585600
}
586601

587-
// If we've sent it the TEST Command, the secondary slot must be in pending state to pass test.
588-
self.fail(error: FirmwareUpgradeError.unknown("Image \(image.image) is not in a pending state."))
602+
// If we've sent it the TEST Command, the slot must be in pending state to pass test.
603+
self.fail(error: FirmwareUpgradeError.unknown("Image \(image.image) Slot \(image.slot) is not in a pending state."))
589604
return
590605
}
591606
self.mark(image, as: \.tested)
592607
}
593608

594609
// Test image succeeded. Begin device reset.
595-
self.log(msg: "All test commands sent", atLevel: .application)
610+
self.log(msg: "All Test commands sent", atLevel: .application)
596611
self.reset()
597612
}
598613

@@ -628,14 +643,9 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
628643
return
629644
}
630645

631-
for image in self.images {
646+
for image in self.images where !image.confirmed {
632647
switch self.configuration.upgradeMode {
633648
case .confirmOnly:
634-
// Check if the image was already confirmed.
635-
if image.confirmed {
636-
continue
637-
}
638-
639649
guard let targetSlot = responseImages.first(where: {
640650
$0.image == image.image && Data($0.hash) == image.hash
641651
}) else {
@@ -668,17 +678,20 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
668678

669679
self.mark(image, as: \.confirmed)
670680
case .testAndConfirm:
671-
if let primary = responseImages.first(where: { $0.image == image.image && $0.slot == 0 }) {
672-
// If Primary is available, check that the upgrade image has successfully booted.
673-
if Data(primary.hash) != image.hash {
674-
self.fail(error: FirmwareUpgradeError.unknown("Device failed to boot into Image \(primary.image)."))
675-
return
681+
if let targetSlot = responseImages.first(where: {
682+
$0.image == image.image && Data($0.hash) == image.hash
683+
}) {
684+
if targetSlot.active || targetSlot.permanent {
685+
// Image booted. All okay.
686+
self.mark(image, as: \.confirmed)
687+
continue
676688
}
677-
// Check that the new image is in confirmed state.
678-
if !primary.confirmed {
679-
self.fail(error: FirmwareUpgradeError.unknown("Image \(primary.image) is not in a confirmed state."))
689+
690+
if image.confirmSent && !targetSlot.confirmed {
691+
self.fail(error: FirmwareUpgradeError.unknown("Image \(targetSlot.image) Slot \(targetSlot.slot) was sent Confirm command but is not in a Confirmed state."))
680692
return
681693
}
694+
682695
self.mark(image, as: \.confirmed)
683696
}
684697
case .testOnly:
@@ -687,11 +700,13 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
687700
}
688701
}
689702

703+
self.log(msg: "Work Complete!", atLevel: .verbose)
690704
self.log(msg: "Upgrade complete", atLevel: .application)
691705
switch self.configuration.upgradeMode {
692706
case .confirmOnly:
693707
self.reset()
694708
case .testAndConfirm:
709+
// No need to reset again.
695710
self.success()
696711
case .testOnly:
697712
// Impossible!
@@ -818,7 +833,7 @@ public class FirmwareUpgradeManager : FirmwareUpgradeController, ConnectionObser
818833
case .reset:
819834
switch self.configuration.upgradeMode {
820835
case .testAndConfirm:
821-
self.verify()
836+
self.testAndConfirmAfterReset()
822837
default:
823838
self.log(msg: "Upgrade complete", atLevel: .application)
824839
self.success()
@@ -945,15 +960,15 @@ extension FirmwareUpgradeManager: ImageUploadDelegate {
945960
// We might send 'Confirm', but the firmware might not change the flag to reflect it.
946961
// If we don't track this internally, we could enter into an infinite loop always trying
947962
// to Confirm an image.
948-
self.mark(firstUnconfirmedImage, as: \.confirmSent)
963+
mark(firstUnconfirmedImage, as: \.confirmSent)
949964
return
950965
} else {
951966
// If there's no image left to Confirm, then we Reset.
952967
reset()
953968
return
954969
}
955970
case .testOnly, .testAndConfirm:
956-
if let firstUntestedImage = images.first(where: { !$0.tested }) {
971+
if let firstUntestedImage = images.first(where: { $0.uploaded && !$0.tested }) {
957972
test(firstUntestedImage)
958973
return
959974
}
@@ -970,6 +985,7 @@ public enum FirmwareUpgradeError: Error {
970985
case unknown(String)
971986
case invalidResponse(McuMgrResponse)
972987
case connectionFailedAfterReset
988+
case untestedImageFound(image: Int, slot: Int)
973989
}
974990

975991
extension FirmwareUpgradeError: LocalizedError {
@@ -982,6 +998,8 @@ extension FirmwareUpgradeError: LocalizedError {
982998
return "Invalid response: \(response)."
983999
case .connectionFailedAfterReset:
9841000
return "Connection failed after reset."
1001+
case .untestedImageFound(let image, let slot):
1002+
return "Image \(image) Slot \(slot) found to be not Tested after Reset. After Reset, all Uploaded Images are expected to have been Tested (Verified)."
9851003
}
9861004
}
9871005

0 commit comments

Comments
 (0)