mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-14 10:35:11 +00:00
firmware/sagas: refactor firmwareFlashUsbDfu
This allows the firmwareFlashUsbDfu to take an arbitrary binary blob instead of a Pybricks firmware.zip file. This will allow it to be used to restore the official LEGO firmware as well.
This commit is contained in:
committed by
David Lechner
parent
71c8605773
commit
cf99ab223d
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020-2022 The Pybricks Authors
|
||||
|
||||
import { FirmwareReaderError } from '@pybricks/firmware';
|
||||
import { FirmwareReaderError, HubType } from '@pybricks/firmware';
|
||||
import { createAction } from '../actions';
|
||||
import { Hub } from '../components/hubPicker';
|
||||
|
||||
@@ -360,14 +360,14 @@ export const didFailToFinish = createAction(didFailToFinishCreator);
|
||||
|
||||
/**
|
||||
* Low-level action to flash firmware using LEGO's DFU over USB.
|
||||
* @param data The firmware zip file data.
|
||||
* @param hubName A custom hub name or an empty string to use the default name.
|
||||
* @param firmware The firmware binary blob.
|
||||
* @param hubType The hub type the firmware blob is for.
|
||||
*/
|
||||
export const firmwareFlashUsbDfu = createAction(
|
||||
(data: ArrayBuffer, hubName: string) => ({
|
||||
(firmware: ArrayBuffer, hubType: HubType) => ({
|
||||
type: 'firmware.action.flashUsbDfu',
|
||||
data,
|
||||
hubName,
|
||||
firmware,
|
||||
hubType,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import type { CreateToast } from '../../toasterTypes';
|
||||
import { useI18n } from './i18n';
|
||||
|
||||
const FirmwareMismatch: React.VoidFunctionComponent = () => {
|
||||
const i18n = useI18n();
|
||||
return <p>{i18n.translate('firmwareMismatch.message')}</p>;
|
||||
};
|
||||
|
||||
export const firmwareMismatch: CreateToast = (onAction) => {
|
||||
return {
|
||||
message: <FirmwareMismatch />,
|
||||
icon: 'error',
|
||||
intent: Intent.DANGER,
|
||||
onDismiss: () => onAction('dismiss'),
|
||||
};
|
||||
};
|
||||
@@ -2,7 +2,6 @@
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { dfuError } from './DfuError';
|
||||
import { firmwareMismatch } from './FirmwareMismatch';
|
||||
import { flashProgress } from './FlashProgress';
|
||||
import { noDfuHub } from './NoDfuHub';
|
||||
import { noDfuInterface } from './NoDfuInterface';
|
||||
@@ -11,7 +10,6 @@ import { releaseButton } from './ReleaseButton';
|
||||
|
||||
export default {
|
||||
dfuError,
|
||||
firmwareMismatch,
|
||||
flashProgress,
|
||||
noDfuHub,
|
||||
noDfuInterface,
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
"noDfuInterface": {
|
||||
"message": "This is very unusual. The USB device did not contain the expected interface."
|
||||
},
|
||||
"firmwareMismatch": {
|
||||
"message": "Cannot flash firmware. The firmware file is for a different kind of hub."
|
||||
},
|
||||
"flashProgress": {
|
||||
"erasing": "Erasing internal flash memory: {percent}",
|
||||
"flashing": "Writing new firmware: {percent}"
|
||||
|
||||
+48
-32
@@ -616,13 +616,6 @@ function* handleFlashFirmware(action: ReturnType<typeof flashFirmware>): Generat
|
||||
}
|
||||
}
|
||||
|
||||
/** Maps USB Product ID to LWP3 hub type ID */
|
||||
const productIdMap: ReadonlyMap<LegoUsbProductId, HubType> = new Map([
|
||||
[LegoUsbProductId.SpikePrimeBootloader, HubType.PrimeHub],
|
||||
[LegoUsbProductId.SpikeEssentialBootloader, HubType.EssentialHub],
|
||||
[LegoUsbProductId.MindstormsRobotInventorBootloader, HubType.PrimeHub],
|
||||
]);
|
||||
|
||||
// currently all hubs use the same start address
|
||||
const dfuFirmwareStartAddress = 0x08008000;
|
||||
|
||||
@@ -664,6 +657,31 @@ function* handleDfuWriteProcess(event: {
|
||||
);
|
||||
}
|
||||
|
||||
function getUsbDeviceFiltersForHub(hubType: HubType): USBDeviceFilter[] {
|
||||
switch (hubType) {
|
||||
case HubType.PrimeHub:
|
||||
return [
|
||||
{
|
||||
vendorId: legoUsbVendorId,
|
||||
productId: LegoUsbProductId.SpikePrimeBootloader,
|
||||
},
|
||||
{
|
||||
vendorId: legoUsbVendorId,
|
||||
productId: LegoUsbProductId.MindstormsRobotInventorBootloader,
|
||||
},
|
||||
];
|
||||
case HubType.EssentialHub:
|
||||
return [
|
||||
{
|
||||
vendorId: legoUsbVendorId,
|
||||
productId: LegoUsbProductId.SpikeEssentialBootloader,
|
||||
},
|
||||
];
|
||||
default:
|
||||
throw new Error(`unsupported hub type: ${hubType}`);
|
||||
}
|
||||
}
|
||||
|
||||
function* handleFlashUsbDfu(action: ReturnType<typeof firmwareFlashUsbDfu>): Generator {
|
||||
const defer = new Array<() => void>();
|
||||
|
||||
@@ -678,21 +696,7 @@ function* handleFlashUsbDfu(action: ReturnType<typeof firmwareFlashUsbDfu>): Gen
|
||||
const device = yield* call(() =>
|
||||
navigator.usb
|
||||
.requestDevice({
|
||||
filters: [
|
||||
{
|
||||
vendorId: legoUsbVendorId,
|
||||
productId: LegoUsbProductId.SpikePrimeBootloader,
|
||||
},
|
||||
{
|
||||
vendorId: legoUsbVendorId,
|
||||
productId: LegoUsbProductId.SpikeEssentialBootloader,
|
||||
},
|
||||
{
|
||||
vendorId: legoUsbVendorId,
|
||||
productId:
|
||||
LegoUsbProductId.MindstormsRobotInventorBootloader,
|
||||
},
|
||||
],
|
||||
filters: getUsbDeviceFiltersForHub(action.hubType),
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err instanceof DOMException && err.name === 'NotFoundError') {
|
||||
@@ -749,16 +753,8 @@ function* handleFlashUsbDfu(action: ReturnType<typeof firmwareFlashUsbDfu>): Gen
|
||||
}),
|
||||
);
|
||||
|
||||
const { firmware, deviceId } = yield* loadFirmware(action.data, action.hubName);
|
||||
|
||||
if (deviceId !== productIdMap.get(device.productId)) {
|
||||
yield* put(alertsShowAlert('firmware', 'firmwareMismatch'));
|
||||
yield* put(firmwareDidFailToFlashUsbDfu());
|
||||
return;
|
||||
}
|
||||
|
||||
dfu.dfuseStartAddress = dfuFirmwareStartAddress;
|
||||
const writeProc = dfu.write(1024, firmware, true);
|
||||
const writeProc = dfu.write(1024, action.firmware, true);
|
||||
|
||||
const eraseProcessChan = eventChannel<{
|
||||
bytesSent: number;
|
||||
@@ -817,6 +813,7 @@ function* handleFlashUsbDfu(action: ReturnType<typeof firmwareFlashUsbDfu>): Gen
|
||||
// errors can happen, e.g. if the USB cable is disconnected while
|
||||
// flashing the firmware
|
||||
if (error) {
|
||||
// istanbul ignore if
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -844,6 +841,7 @@ function* handleFlashUsbDfu(action: ReturnType<typeof firmwareFlashUsbDfu>): Gen
|
||||
|
||||
yield* put(firmwareDidFlashUsbDfu());
|
||||
} catch (err) {
|
||||
// istanbul ignore if
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(err);
|
||||
}
|
||||
@@ -878,7 +876,25 @@ function* handleInstallPybricks(): Generator {
|
||||
yield* put(flashFirmware(accepted.firmwareZip, accepted.hubName));
|
||||
break;
|
||||
case 'usb-lego-dfu':
|
||||
yield* put(firmwareFlashUsbDfu(accepted.firmwareZip, accepted.hubName));
|
||||
try {
|
||||
const { firmware, deviceId } = yield* loadFirmware(
|
||||
accepted.firmwareZip,
|
||||
accepted.hubName,
|
||||
);
|
||||
|
||||
yield* put(firmwareFlashUsbDfu(firmware, deviceId));
|
||||
} catch (err) {
|
||||
// istanbul ignore if
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
yield* put(
|
||||
alertsShowAlert('alerts', 'unexpectedError', {
|
||||
error: ensureError(err),
|
||||
}),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user