Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

McuMgrPackage-based Start Firmware Upgrade API #229

Merged
merged 1 commit into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
29 changes: 29 additions & 0 deletions Source/Managers/DFU/FirmwareUpgradeManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,42 @@ 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
/// updating the App Core (i.e. no Multi-Image).
/// - 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)
Expand Down
27 changes: 16 additions & 11 deletions Source/McuMgrPackage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
}