Skip to content

Commit af8f87a

Browse files
authored
Merge pull request #447 from NordicSemiconductor/feature/new-secure-dfu-op-codes
New Secure DFU op codes, including Abort
2 parents 63dbdbe + 63e0cba commit af8f87a

File tree

5 files changed

+104
-27
lines changed

5 files changed

+104
-27
lines changed

iOSDFULibrary/Classes/Implementation/GenericDFU/DFUPeripheral.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ internal class BaseDFUPeripheral<TD : BasePeripheralDelegate> : NSObject, BaseDF
200200
delegate = nil
201201
}
202202

203+
func resetDevice() {
204+
if let peripheral = peripheral, peripheral.state != .disconnected {
205+
disconnect()
206+
} else {
207+
peripheralDidDisconnect()
208+
}
209+
}
210+
203211
// MARK: - DFU Controller API
204212

205213
func pause() -> Bool {
@@ -405,17 +413,6 @@ internal class BaseDFUPeripheral<TD : BasePeripheralDelegate> : NSObject, BaseDF
405413
delegate?.peripheralDidDisconnect()
406414
}
407415

408-
/**
409-
This method should reset the device, preferably switching it to application mode.
410-
*/
411-
func resetDevice() {
412-
if let peripheral = peripheral, peripheral.state != .disconnected {
413-
disconnect()
414-
} else {
415-
peripheralDidDisconnect()
416-
}
417-
}
418-
419416
// MARK: - Private methods
420417

421418
/**

iOSDFULibrary/Classes/Implementation/LegacyDFU/Services/LegacyDFUService.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import CoreBluetooth
4848
private var dfuControlPointCharacteristic : DFUControlPoint?
4949
private var dfuVersionCharacteristic : DFUVersion?
5050

51-
5251
/// This method returns true if DFU Control Point characteristc has been discovered.
5352
/// A device without this characteristic is not supported and even can't be resetted
5453
/// by sending a Reset command.

iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUControlPoint.swift

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,19 @@
3131
import CoreBluetooth
3232

3333
internal enum SecureDFUOpCode : UInt8 {
34+
case getProtocolVersion = 0x0 // not supported by this library
3435
case createObject = 0x01
3536
case setPRNValue = 0x02
3637
case calculateChecksum = 0x03
3738
case execute = 0x04
39+
// case no-such-op-code = 0x05
3840
case readObjectInfo = 0x06
41+
case getMtu = 0x07 // not supported by this library
42+
case write = 0x08 // not supported by this library
43+
case ping = 0x09 // not supported by this library
44+
case getHwVersion = 0x0A // not supported by this library
45+
case getFwVersion = 0x0B // not supported by this library
46+
case abort = 0x0C
3947
case responseCode = 0x60
4048

4149
var code: UInt8 {
@@ -98,17 +106,40 @@ internal enum SecureDFUProcedureType : UInt8 {
98106
}
99107
}
100108

109+
internal enum SecureDFUImageType : UInt8 {
110+
case softdevice = 0x00
111+
case application = 0x01
112+
case bootloader = 0x02
113+
114+
var description: String{
115+
switch self{
116+
case .softdevice: return "Soft Device"
117+
case .application: return "Application"
118+
case .bootloader: return "Bootloader"
119+
}
120+
}
121+
}
122+
101123
internal enum SecureDFURequest {
102-
case createCommandObject(withSize : UInt32)
103-
case createDataObject(withSize : UInt32)
124+
case getProtocolVersion
125+
case createCommandObject(withSize: UInt32)
126+
case createDataObject(withSize: UInt32)
104127
case readCommandObjectInfo
105128
case readDataObjectInfo
106-
case setPacketReceiptNotification(value : UInt16)
129+
case setPacketReceiptNotification(value: UInt16)
107130
case calculateChecksumCommand
108131
case executeCommand
132+
case getMtu
133+
case write(bytes: Data)
134+
case ping(id: UInt8)
135+
case getHwVersion
136+
case getFwVersion(image: SecureDFUImageType)
137+
case abort
109138

110139
var data : Data {
111140
switch self {
141+
case .getProtocolVersion:
142+
return Data([SecureDFUOpCode.getProtocolVersion.code])
112143
case .createDataObject(let aSize):
113144
var data = Data([SecureDFUOpCode.createObject.code, SecureDFUProcedureType.data.rawValue])
114145
data += aSize.littleEndian
@@ -117,10 +148,6 @@ internal enum SecureDFURequest {
117148
var data = Data([SecureDFUOpCode.createObject.code, SecureDFUProcedureType.command.rawValue])
118149
data += aSize.littleEndian
119150
return data
120-
case .readCommandObjectInfo:
121-
return Data([SecureDFUOpCode.readObjectInfo.code, SecureDFUProcedureType.command.rawValue])
122-
case .readDataObjectInfo:
123-
return Data([SecureDFUOpCode.readObjectInfo.code, SecureDFUProcedureType.data.rawValue])
124151
case .setPacketReceiptNotification(let aSize):
125152
var data = Data([SecureDFUOpCode.setPRNValue.code])
126153
data += aSize.littleEndian
@@ -129,19 +156,45 @@ internal enum SecureDFURequest {
129156
return Data([SecureDFUOpCode.calculateChecksum.code])
130157
case .executeCommand:
131158
return Data([SecureDFUOpCode.execute.code])
159+
case .readCommandObjectInfo:
160+
return Data([SecureDFUOpCode.readObjectInfo.code, SecureDFUProcedureType.command.rawValue])
161+
case .readDataObjectInfo:
162+
return Data([SecureDFUOpCode.readObjectInfo.code, SecureDFUProcedureType.data.rawValue])
163+
case .getMtu:
164+
return Data([SecureDFUOpCode.getMtu.code])
165+
case .write(let bytes):
166+
var data = Data([SecureDFUOpCode.write.code])
167+
data += bytes
168+
data += UInt16(bytes.count).littleEndian
169+
return data
170+
case .ping(let id):
171+
return Data([SecureDFUOpCode.ping.code, id])
172+
case .getHwVersion:
173+
return Data([SecureDFUOpCode.getHwVersion.code])
174+
case .getFwVersion(let image):
175+
return Data([SecureDFUOpCode.getFwVersion.code, image.rawValue])
176+
case .abort:
177+
return Data([SecureDFUOpCode.abort.code])
132178
}
133179
}
134180

135181
var description : String {
136182
switch self {
183+
case .getProtocolVersion: return "Get Protocol Version (Op Code = 0)"
137184
case .createCommandObject(let size): return "Create Command Object (Op Code = 1, Type = 1, Size: \(size)b)"
138185
case .createDataObject(let size): return "Create Data Object (Op Code = 1, Type = 2, Size: \(size)b)"
139-
case .readCommandObjectInfo: return "Read Command Object Info (Op Code = 6, Type = 1)"
140-
case .readDataObjectInfo: return "Read Data Object Info (Op Code = 6, Type = 2)"
141186
case .setPacketReceiptNotification(let number):
142187
return "Packet Receipt Notif Req (Op Code = 2, Value = \(number))"
143188
case .calculateChecksumCommand: return "Calculate Checksum (Op Code = 3)"
144189
case .executeCommand: return "Execute Object (Op Code = 4)"
190+
case .readCommandObjectInfo: return "Read Command Object Info (Op Code = 6, Type = 1)"
191+
case .readDataObjectInfo: return "Read Data Object Info (Op Code = 6, Type = 2)"
192+
case .getMtu: return "Get MTU (Op Code = 7)"
193+
case .write(let bytes): return "Write (Op Code = 8, Data = 0x\(bytes.hexString), Length = \(bytes.count))"
194+
case .ping(let id): return "Ping (Op Code = 9, ID = \(id))"
195+
case .getHwVersion: return "Get HW Version (Op Code = 10)"
196+
case .getFwVersion(let image): return "Get FW Version (Op Code = 11, Type = \(image.rawValue))"
197+
case .abort: return "Abort (Op Code = 12)"
145198
}
146199
}
147200
}
@@ -155,7 +208,7 @@ internal enum SecureDFUResultCode : UInt8 {
155208
case invalidObject = 0x05
156209
case signatureMismatch = 0x06
157210
case unsupportedType = 0x07
158-
case operationNotpermitted = 0x08
211+
case operationNotPermitted = 0x08
159212
case operationFailed = 0x0A
160213
case extendedError = 0x0B
161214

@@ -176,7 +229,7 @@ internal enum SecureDFUResultCode : UInt8 {
176229
case .insufficientResources: return "Insufficient resources"
177230
case .invalidObject: return "Invalid object"
178231
case .signatureMismatch: return "Signature mismatch"
179-
case .operationNotpermitted: return "Operation not permitted"
232+
case .operationNotPermitted: return "Operation not permitted"
180233
case .unsupportedType: return "Unsupported type"
181234
case .operationFailed: return "Operation failed"
182235
case .extendedError: return "Extended error"

iOSDFULibrary/Classes/Implementation/SecureDFU/Peripheral/SecureDFUPeripheral.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,4 +329,12 @@ internal class SecureDFUPeripheral : BaseCommonDFUPeripheral<SecureDFUExecutor,
329329
}
330330
)
331331
}
332+
333+
override func resetDevice() {
334+
guard let dfuService = dfuService, dfuService.supportsReset() else {
335+
super.resetDevice()
336+
return
337+
}
338+
dfuService.sendReset(onError: defaultErrorCallback)
339+
}
332340
}

iOSDFULibrary/Classes/Implementation/SecureDFU/Services/SecureDFUService.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ import CoreBluetooth
4747
private let service : CBService
4848
private var dfuPacketCharacteristic : SecureDFUPacket?
4949
private var dfuControlPointCharacteristic : SecureDFUControlPoint?
50+
51+
/// This method returns true if DFU Control Point characteristc has been discovered.
52+
/// A device without this characteristic is not supported and even can't be resetted
53+
/// by sending a Reset command.
54+
internal func supportsReset() -> Bool {
55+
// The Abort (0x0C) command has been added to DFU bootloader in SDK 15.
56+
// https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v15.0.0/lib_dfu_transport.html?cp=8_5_3_3_5_2
57+
// For earlier SDKs there is no way to reset the bootloader other than
58+
// disconnecting and waiting for it to time out after few minutes.
59+
return dfuControlPointCharacteristic != nil
60+
}
5061

5162
private var paused = false
5263
private var aborted = false
@@ -55,7 +66,7 @@ import CoreBluetooth
5566
private var success : Callback?
5667
/// A temporary callback used to report an operation error.
5768
private var report : ErrorCallback?
58-
/// A temporaty callback used to report progress status.
69+
/// A temporary callback used to report progress status.
5970
private var progressDelegate : DFUProgressDelegate?
6071
private var progressQueue : DispatchQueue?
6172

@@ -323,10 +334,19 @@ import CoreBluetooth
323334

324335
- parameter report: A callback called when writing characteristic failed.
325336
*/
326-
private func sendReset(onError report: @escaping ErrorCallback) {
337+
func sendReset(onError report: @escaping ErrorCallback) {
327338
aborted = true
328-
// There is no command to reset a Secure DFU device. We can just disconnect.
329-
targetPeripheral?.disconnect()
339+
// Upon sending the Abort request the device will immediately reboot in application
340+
// mode. There will be no notification with status success returned.
341+
dfuControlPointCharacteristic?.send(.abort,
342+
onSuccess: nil, // Device will disconnected immediately.
343+
onError: { [weak self] _, _ in
344+
// Seems like the Abort request is not supported (indicating SDK 12-14).
345+
// We can just disconnect. The bootloader should reset to app mode after
346+
// a timeout.
347+
self?.targetPeripheral?.disconnect()
348+
}
349+
)
330350
}
331351

332352
//MARK: - Packet commands

0 commit comments

Comments
 (0)