feat: add bootloader DFU protocol validation
This commit is contained in:
@ -1,21 +1,29 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
class DfuV1FirmwareMetadata {
|
||||
const DfuV1FirmwareMetadata({
|
||||
class BootloaderDfuFirmwareMetadata {
|
||||
const BootloaderDfuFirmwareMetadata({
|
||||
required this.totalLength,
|
||||
required this.crc32,
|
||||
required this.appStart,
|
||||
required this.imageVersion,
|
||||
required this.sessionId,
|
||||
required this.flags,
|
||||
required this.vectorStackPointer,
|
||||
required this.vectorReset,
|
||||
});
|
||||
|
||||
final int totalLength;
|
||||
final int crc32;
|
||||
final int appStart;
|
||||
final int imageVersion;
|
||||
final int sessionId;
|
||||
final int flags;
|
||||
final int vectorStackPointer;
|
||||
final int vectorReset;
|
||||
}
|
||||
|
||||
class DfuV1PreparedFirmware {
|
||||
const DfuV1PreparedFirmware({
|
||||
class BootloaderDfuPreparedFirmware {
|
||||
const BootloaderDfuPreparedFirmware({
|
||||
required this.fileName,
|
||||
required this.fileBytes,
|
||||
required this.metadata,
|
||||
@ -25,7 +33,7 @@ class DfuV1PreparedFirmware {
|
||||
final String fileName;
|
||||
final String? filePath;
|
||||
final Uint8List fileBytes;
|
||||
final DfuV1FirmwareMetadata metadata;
|
||||
final BootloaderDfuFirmwareMetadata metadata;
|
||||
}
|
||||
|
||||
enum FirmwareSelectionFailureReason {
|
||||
@ -33,6 +41,9 @@ enum FirmwareSelectionFailureReason {
|
||||
malformedSelection,
|
||||
unsupportedExtension,
|
||||
emptyFile,
|
||||
imageTooSmall,
|
||||
imageTooLarge,
|
||||
invalidVectorTable,
|
||||
readFailed,
|
||||
}
|
||||
|
||||
@ -52,7 +63,7 @@ class FirmwareFileSelectionResult {
|
||||
this.failure,
|
||||
});
|
||||
|
||||
final DfuV1PreparedFirmware? firmware;
|
||||
final BootloaderDfuPreparedFirmware? firmware;
|
||||
final FirmwareSelectionFailure? failure;
|
||||
|
||||
bool get isSuccess => firmware != null;
|
||||
@ -60,7 +71,8 @@ class FirmwareFileSelectionResult {
|
||||
bool get isCanceled =>
|
||||
failure?.reason == FirmwareSelectionFailureReason.canceled;
|
||||
|
||||
static FirmwareFileSelectionResult success(DfuV1PreparedFirmware firmware) {
|
||||
static FirmwareFileSelectionResult success(
|
||||
BootloaderDfuPreparedFirmware firmware) {
|
||||
return FirmwareFileSelectionResult._(firmware: firmware);
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,8 @@ const String universalShifterDfuDataCharacteristicUuid =
|
||||
'0993826f-0ee4-4b37-9614-d13ecba40009';
|
||||
const String universalShifterDfuAckCharacteristicUuid =
|
||||
'0993826f-0ee4-4b37-9614-d13ecba4000a';
|
||||
const String universalShifterDfuStatusCharacteristicUuid =
|
||||
'0993826f-0ee4-4b37-9614-d13ecba4000a';
|
||||
const String ftmsServiceUuid = '00001826-0000-1000-8000-00805f9b34fb';
|
||||
const String batteryServiceUuid = '0000180f-0000-1000-8000-00805f9b34fb';
|
||||
const String batteryLevelCharacteristicUuid =
|
||||
@ -36,14 +38,26 @@ bool isFtmsUuid(Uuid uuid) {
|
||||
const int universalShifterDfuOpcodeStart = 0x01;
|
||||
const int universalShifterDfuOpcodeFinish = 0x02;
|
||||
const int universalShifterDfuOpcodeAbort = 0x03;
|
||||
const int universalShifterDfuOpcodeGetStatus = 0x04;
|
||||
|
||||
const int universalShifterDfuFrameSizeBytes = 64;
|
||||
const int universalShifterDfuFramePayloadSizeBytes = 63;
|
||||
const int universalShifterBootloaderDfuDataHeaderSizeBytes = 9;
|
||||
const int universalShifterBootloaderDfuMaxPayloadSizeBytes =
|
||||
universalShifterDfuFrameSizeBytes -
|
||||
universalShifterBootloaderDfuDataHeaderSizeBytes;
|
||||
const int universalShifterBootloaderDfuStatusSizeBytes = 6;
|
||||
const int universalShifterAttWriteOverheadBytes = 3;
|
||||
const int universalShifterDfuMinimumMtu =
|
||||
universalShifterDfuFrameSizeBytes + universalShifterAttWriteOverheadBytes;
|
||||
const int universalShifterDfuPreferredMtu = 128;
|
||||
|
||||
const int universalShifterDfuAppStart = 0x00030000;
|
||||
const int universalShifterDfuAppSlotSizeBytes = 0x00042000;
|
||||
const int universalShifterDfuMinimumImageLengthBytes = 8;
|
||||
const int universalShifterDfuRamStart = 0x20000000;
|
||||
const int universalShifterDfuRamEnd = 0x20010000;
|
||||
|
||||
const int universalShifterDfuFlagEncrypted = 0x01;
|
||||
const int universalShifterDfuFlagSigned = 0x02;
|
||||
const int universalShifterDfuFlagNone = 0x00;
|
||||
@ -134,6 +148,49 @@ class DfuUpdateProgress {
|
||||
state == DfuUpdateState.failed;
|
||||
}
|
||||
|
||||
enum DfuBootloaderStatusCode {
|
||||
ok(0x00),
|
||||
parseError(0x01),
|
||||
stateError(0x02),
|
||||
boundsError(0x03),
|
||||
crcError(0x04),
|
||||
flashError(0x05),
|
||||
unsupportedError(0x06),
|
||||
vectorError(0x07),
|
||||
queueFull(0x08),
|
||||
bootMetadataError(0x09),
|
||||
unknown(-1);
|
||||
|
||||
const DfuBootloaderStatusCode(this.value);
|
||||
|
||||
final int value;
|
||||
|
||||
static DfuBootloaderStatusCode fromRaw(int value) {
|
||||
for (final code in values) {
|
||||
if (code.value == value) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return DfuBootloaderStatusCode.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
class DfuBootloaderStatus {
|
||||
const DfuBootloaderStatus({
|
||||
required this.code,
|
||||
required this.rawCode,
|
||||
required this.sessionId,
|
||||
required this.expectedOffset,
|
||||
});
|
||||
|
||||
final DfuBootloaderStatusCode code;
|
||||
final int rawCode;
|
||||
final int sessionId;
|
||||
final int expectedOffset;
|
||||
|
||||
bool get isOk => code == DfuBootloaderStatusCode.ok;
|
||||
}
|
||||
|
||||
enum DfuPreflightFailureReason {
|
||||
deviceNotConnected,
|
||||
wrongConnectedDevice,
|
||||
@ -253,7 +310,8 @@ enum UniversalShifterCommand {
|
||||
stopScan(0x02),
|
||||
connectToDevice(0x03),
|
||||
disconnect(0x04),
|
||||
turnOff(0x05);
|
||||
turnOff(0x05),
|
||||
enterDfu(0x06);
|
||||
|
||||
const UniversalShifterCommand(this.value);
|
||||
final int value;
|
||||
|
||||
Reference in New Issue
Block a user