From ef339ab482ccfad5bd26643884c32c21910ec382 Mon Sep 17 00:00:00 2001 From: Dinesh Harjani Date: Wed, 3 Jul 2024 11:22:10 +0100 Subject: [PATCH] McuMgrPackage-based Start Firmware Upgrade API With McuMgrPackage as an intermediary to handle .bin files, .zip files for both McuBoot / McuMgr and SUIT, the API front-end can be simplified for the user. This is reflected in the Example app itself. --- .../FirmwareUpgradeViewController.swift | 34 +++++-------------- .../Managers/DFU/FirmwareUpgradeManager.swift | 29 ++++++++++++++++ Source/McuMgrPackage.swift | 27 +++++++++------ 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/Example/Example/View Controllers/Manager/FirmwareUpgradeViewController.swift b/Example/Example/View Controllers/Manager/FirmwareUpgradeViewController.swift index 0396b6f..24b5471 100644 --- a/Example/Example/View Controllers/Manager/FirmwareUpgradeViewController.swift +++ b/Example/Example/View Controllers/Manager/FirmwareUpgradeViewController.swift @@ -58,11 +58,12 @@ final class FirmwareUpgradeViewController: UIViewController, McuMgrViewControlle } @IBAction func start(_ sender: UIButton) { - if let envelope = package?.envelope { + guard let package else { return } + if package.isForSUIT { // SUIT has "no mode" to select // (We use modes in the code only, but SUIT has no concept of upload modes) - startFirmwareUpgrade(envelope: envelope) - } else if let package { + startFirmwareUpgrade(package: package) + } else { selectMode(for: package) } } @@ -142,7 +143,7 @@ final class FirmwareUpgradeViewController: UIViewController, McuMgrViewControlle } private func setByteAlignment() { - let alertController = UIAlertController(title: "Byte alignment", message: nil, preferredStyle: .actionSheet) + let alertController = UIAlertController(title: "Byte Alignment", message: nil, preferredStyle: .actionSheet) ImageUploadAlignment.allCases.forEach { alignmentValue in let text = "\(alignmentValue)" alertController.addAction(UIAlertAction(title: text, style: .default) { @@ -159,7 +160,7 @@ final class FirmwareUpgradeViewController: UIViewController, McuMgrViewControlle } private func selectMode(for package: McuMgrPackage) { - let alertController = UIAlertController(title: "Select mode", message: nil, preferredStyle: .actionSheet) + let alertController = UIAlertController(title: "Select Mode", message: nil, preferredStyle: .actionSheet) FirmwareUpgradeMode.allCases.forEach { upgradeMode in let text = "\(upgradeMode)" alertController.addAction(UIAlertAction(title: text, style: .default) { @@ -187,26 +188,7 @@ final class FirmwareUpgradeViewController: UIViewController, McuMgrViewControlle private func startFirmwareUpgrade(package: McuMgrPackage) { do { - dfuManagerConfiguration.suitMode = false - try dfuManager.start(images: package.images, using: dfuManagerConfiguration) - } catch { - status.textColor = .systemRed - status.text = error.localizedDescription - actionStart.isEnabled = false - } - } - - private func startFirmwareUpgrade(envelope: McuMgrSuitEnvelope) { - do { - // sha256 is the currently only supported mode. - // The rest are optional to implement in SUIT. - guard let sha256Hash = envelope.digest.hash(for: .sha256) else { - throw McuMgrSuitParseError.supportedAlgorithmNotFound - } - - dfuManagerConfiguration.suitMode = true - dfuManagerConfiguration.upgradeMode = .uploadOnly - try dfuManager.start(hash: sha256Hash, data: envelope.data, using: dfuManagerConfiguration) + try dfuManager.start(package: package, using: dfuManagerConfiguration) } catch { status.textColor = .systemRed status.text = error.localizedDescription @@ -387,7 +369,7 @@ extension FirmwareUpgradeViewController: UIDocumentMenuDelegate, UIDocumentPicke if let envelope = package.envelope { fileHash.text = envelope.digest.hashString() } else { - fileHash.text = try package.hashString() + fileHash.text = package.hashString() } fileHash.numberOfLines = 0 diff --git a/Source/Managers/DFU/FirmwareUpgradeManager.swift b/Source/Managers/DFU/FirmwareUpgradeManager.swift index 2dee9f2..87af718 100644 --- a/Source/Managers/DFU/FirmwareUpgradeManager.swift +++ b/Source/Managers/DFU/FirmwareUpgradeManager.swift @@ -79,6 +79,34 @@ public class FirmwareUpgradeManager: FirmwareUpgradeController, ConnectionObserv // MARK: Control Functions + /// Start the firmware upgrade. + /// + /// This is the full-featured API to start DFU update, including support for Multi-Image uploads, DirectXIP, and SUIT. It is a seamless API. + /// - parameter package: The (`McrMgrPackage`) to upload. + /// - parameter configuration: Fine-tuning of details regarding the upgrade process. + public func start(package: McuMgrPackage, + using configuration: FirmwareUpgradeConfiguration = FirmwareUpgradeConfiguration()) throws { + guard let envelope = package.envelope else { + // McuMgr mode. + var mcuMgrConfiguration = configuration + mcuMgrConfiguration.suitMode = false + try start(images: package.images, using: mcuMgrConfiguration) + return + } + + // sha256 is the currently only supported mode. + // The rest are optional to implement in SUIT. + guard let sha256Hash = envelope.digest.hash(for: .sha256) else { + throw McuMgrSuitParseError.supportedAlgorithmNotFound + } + let image = ImageManager.Image(image: 0, hash: sha256Hash, data: envelope.data) + + var suitConfiguration = configuration + suitConfiguration.suitMode = true + suitConfiguration.upgradeMode = .uploadOnly + try start(images: [image], using: suitConfiguration) + } + /// Start the firmware upgrade. /// /// Use this convenience call of ``start(images:using:)`` if you're only @@ -86,6 +114,7 @@ public class FirmwareUpgradeManager: FirmwareUpgradeController, ConnectionObserv /// - parameter hash: The hash of the Image to be uploaded, used for comparison with the target firmware. /// - parameter data: `Data` to upload to App Core (Image 0). /// - parameter configuration: Fine-tuning of details regarding the upgrade process. + @available(*, deprecated, message: "start(package:using:) is now a far more convenient call. Therefore this API is henceforth marked as deprecated and will be removed in a future release.") public func start(hash: Data, data: Data, using configuration: FirmwareUpgradeConfiguration = FirmwareUpgradeConfiguration()) throws { try start(images: [ImageManager.Image(image: 0, hash: hash, data: data)], using: configuration) diff --git a/Source/McuMgrPackage.swift b/Source/McuMgrPackage.swift index a25f9dc..014dc5d 100644 --- a/Source/McuMgrPackage.swift +++ b/Source/McuMgrPackage.swift @@ -22,7 +22,7 @@ public struct McuMgrPackage { public init(from url: URL) throws { switch UTI.forFile(url) { case .bin: - self.images = try Self.extractImageFromBinFile(from: url) + self.images = try [ImageManager.Image(fromBinFile: url)] self.envelope = nil self.resources = nil case .zip: @@ -43,6 +43,8 @@ public struct McuMgrPackage { // MARK: - API + public var isForSUIT: Bool { envelope != nil } + public func imageName(at index: Int) -> String { guard let name = images[index].name else { let coreName: String @@ -78,7 +80,7 @@ public struct McuMgrPackage { return sizeString } - public func hashString() throws -> String { + public func hashString() -> String { var result = "" for (i, image) in images.enumerated() { let hashString = image.hash.hexEncodedString(options: .upperCase) @@ -122,12 +124,6 @@ public extension McuMgrPackage { fileprivate extension McuMgrPackage { - static func extractImageFromBinFile(from url: URL) throws -> [ImageManager.Image] { - let binData = try Data(contentsOf: url) - let binHash = try McuMgrImage(data: binData).hash - return [ImageManager.Image(image: 0, hash: binHash, data: binData)] - } - static func extractImageFromSuitFile(from url: URL) throws -> Self { let envelope = try McuMgrSuitEnvelope(from: url) let algorithm = McuMgrSuitDigest.Algorithm.sha256 @@ -181,9 +177,7 @@ fileprivate extension McuMgrPackage { guard let imageURL = unzippedURLs.first(where: { $0.absoluteString.contains(manifestFile.file) }) else { throw McuMgrPackage.Error.manifestImageNotFound } - let imageData = try Data(contentsOf: imageURL) - let imageHash = try McuMgrImage(data: imageData).hash - return ImageManager.Image(manifestFile, hash: imageHash, data: imageData) + return try ImageManager.Image(fromBinFile: imageURL) } envelope = nil resources = nil @@ -195,3 +189,14 @@ fileprivate extension McuMgrPackage { return McuMgrPackage(images: images, envelope: envelope, resources: resources) } } + +// MARK: - Image Extension + +fileprivate extension ImageManager.Image { + + init(fromBinFile url: URL) throws { + let binData = try Data(contentsOf: url) + let binHash = try McuMgrImage(data: binData).hash + self.init(image: 0, hash: binHash, data: binData) + } +}