Skip to content

Commit

Permalink
Support a file copy to flash a firmware.
Browse files Browse the repository at this point in the history
  • Loading branch information
yoichiro committed Sep 25, 2023
1 parent 00e98a1 commit 9e0e726
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 33 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"@types/w3c-web-hid": "^1.0.1",
"@types/w3c-web-serial": "^1.0.2",
"@types/w3c-web-usb": "^1.0.5",
"@types/wicg-file-system-access": "^2020.9.7",
"@typescript-eslint/eslint-plugin": "^4.10.0",
"@typescript-eslint/parser": "^4.10.0",
"babel-eslint": "^10.1.0",
Expand Down
75 changes: 43 additions & 32 deletions src/actions/firmware.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const firmwareActionsThunk = {
common.firmware.flashFirmwareDialog.bootloaderType!;
const flashMode = common.firmware.flashFirmwareDialog.flashMode;

let flashBytes: Buffer;
let flashBytes: Buffer | undefined;
if (flashMode === 'fetch_and_flash') {
const definitionDocument = entities.keyboardDefinitionDocument!;
sendEventToGoogleAnalytics('catalog/flash_firmware', {
Expand All @@ -211,41 +211,25 @@ export const firmwareActionsThunk = {
return;
}
const blob: Blob = fetchBlobResult.value.blob;
try {
flashBytes = intelHex.parse(
Buffer.from(new Uint8Array(await blob.arrayBuffer()))
).data;
} catch (error) {
console.error(error);
dispatch(
NotificationActions.addError(
'Reading the firmware binary failed.',
error
)
);
dispatch(FlashFirmwareDialogActions.appendLog(`Error: ${error}`));
dispatch(FlashFirmwareDialogActions.updateFlashing(false));
flashBytes = createFlashBytes(
Buffer.from(new Uint8Array(await blob.arrayBuffer())),
bootloaderType,
dispatch
);
if (flashBytes === undefined) {
return;
}
} else {
try {
flashBytes = intelHex.parse(
Buffer.from(
new Uint8Array(
common.firmware.uploadFirmwareDialog.firmwareFileBuffer!
)
)
).data;
} catch (error) {
console.error(error);
dispatch(
NotificationActions.addError(
'Reading the firmware binary failed.',
error
flashBytes = createFlashBytes(
Buffer.from(
new Uint8Array(
common.firmware.uploadFirmwareDialog.firmwareFileBuffer!
)
);
dispatch(FlashFirmwareDialogActions.appendLog(`Error: ${error}`));
dispatch(FlashFirmwareDialogActions.updateFlashing(false));
),
bootloaderType,
dispatch
);
if (flashBytes === undefined) {
return;
}
}
Expand Down Expand Up @@ -307,3 +291,30 @@ export const firmwareActionsThunk = {
}
},
};

const createFlashBytes = (
buffer: Buffer,
bootloaderType: IBootloaderType,
dispatch: ThunkDispatch<RootState, undefined, ActionTypes>
): Buffer | undefined => {
try {
switch (bootloaderType) {
case 'caterina':
case 'dfu':
return intelHex.parse(buffer).data;
case 'copy':
return buffer;
}
} catch (error) {
console.error(error);
dispatch(
NotificationActions.addError(
'Creating the firmware binary failed.',
error
)
);
dispatch(FlashFirmwareDialogActions.appendLog(`Error: ${error}`));
dispatch(FlashFirmwareDialogActions.updateFlashing(false));
return undefined;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export default function FirmwareForm(props: FirmwareFormProps) {
>
<MenuItem value="caterina">caterina</MenuItem>
<MenuItem value="dfu">dfu</MenuItem>
<MenuItem value="copy">copy</MenuItem>
</Select>
</FormControl>
</div>
Expand Down Expand Up @@ -602,6 +603,7 @@ function EditDialog(props: IEditDialogProps) {
>
<MenuItem value="caterina">caterina</MenuItem>
<MenuItem value="dfu">dfu</MenuItem>
<MenuItem value="copy">copy</MenuItem>
</Select>
</FormControl>
) : (
Expand Down
9 changes: 9 additions & 0 deletions src/services/firmware/FirmwareWriterWebApiImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { IUsb } from './usb/Usb';
import WebUsb from './usb/WebUsb';
import { IBootloader } from './Bootloader';
import { DfuBootloader } from './dfu/DfuBootloader';
import { WebFileSystem } from './copy/WebFileSystem';

const BAUD_RATE = 115200;
const BUFFER_SIZE = 81920;
Expand Down Expand Up @@ -55,6 +56,14 @@ export class FirmwareWriterWebApiImpl implements IFirmwareWriter {
}
const bootloader: IBootloader = createDfuBootloaderResult.bootloader!;
return await bootloader.write(flashBytes, eepromBytes, progress, phase);
} else if (bootloaderType === 'copy') {
const fileSystem = new WebFileSystem();
const openResult = await fileSystem.open();
if (!openResult.success) {
return openResult;
}
phase('opened');
return await fileSystem.write(flashBytes, eepromBytes, progress, phase);
} else {
throw new Error(`Unknown bootloader type: ${bootloaderType}`);
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/firmware/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const MCU: IMcuMap = {
},
};

export const ALL_BOOTLOADER_TYPE = ['caterina', 'dfu'] as const;
export const ALL_BOOTLOADER_TYPE = ['caterina', 'dfu', 'copy'] as const;
type bootloaderTypeTuple = typeof ALL_BOOTLOADER_TYPE;
export type IBootloaderType = bootloaderTypeTuple[number];

Expand Down
84 changes: 84 additions & 0 deletions src/services/firmware/copy/WebFileSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { IBootloader, IBootloaderReadResult } from '../Bootloader';
import {
FirmwareWriterPhaseListener,
FirmwareWriterProgressListener,
} from '../FirmwareWriter';
import { IResult } from '../Types';

export class WebFileSystem implements IBootloader {
private directoryHandle: any | undefined;

constructor() {
this.directoryHandle = undefined;
}

async open(): Promise<IResult> {
try {
this.directoryHandle = await window.showDirectoryPicker({
mode: 'readwrite',
});
return { success: true };
} catch (error) {
return {
success: false,
error: `Opening a directory failed: ${error}`,
cause: error,
};
}
}

async read(
// eslint-disable-next-line no-unused-vars
size: number,
// eslint-disable-next-line no-unused-vars
progress: FirmwareWriterProgressListener,
// eslint-disable-next-line no-unused-vars
phase: FirmwareWriterPhaseListener
): Promise<IBootloaderReadResult> {
throw new Error('This method never be called.');
}

async verify(
// eslint-disable-next-line no-unused-vars
bytes: Uint8Array,
// eslint-disable-next-line no-unused-vars
progress: FirmwareWriterProgressListener,
// eslint-disable-next-line no-unused-vars
phase: FirmwareWriterPhaseListener
): Promise<IResult> {
throw new Error('This method never be called.');
}

async write(
flashBytes: Uint8Array,
// eslint-disable-next-line no-unused-vars
eepromBytes: Uint8Array | null,
progress: FirmwareWriterProgressListener,
phase: FirmwareWriterPhaseListener
): Promise<IResult> {
if (this.directoryHandle === undefined) {
throw new Error('A target directory is not opened.');
}
try {
progress('Start writing firmware to a file.');
const fileHandle = await this.directoryHandle.getFileHandle(
'firmware.uf2',
{ create: true }
);
const writable = await fileHandle.createWritable();
await writable.write(flashBytes);
await writable.close();
progress('Writing firmware to a file is completed.');
phase('wrote');
this.directoryHandle = undefined;
phase('closed');
return { success: true };
} catch (error) {
return {
success: false,
error: `Writing firmware to a file failed: ${error}`,
cause: error,
};
}
}
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3839,6 +3839,11 @@
anymatch "^3.0.0"
source-map "^0.6.0"

"@types/wicg-file-system-access@^2020.9.7":
version "2020.9.7"
resolved "https://registry.yarnpkg.com/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.7.tgz#8cd797c9b81ed876cf41158b10a634623831fccf"
integrity sha512-fjEImGBMKeoFE2pgX17XkNPAmgZdVs3MZcEAN7uW6vj2UxJR47WxKvmcR8TBgKpozbc1Gqxb8KaT8/VUgWEmTQ==

"@types/yargs-parser@*":
version "20.2.1"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
Expand Down

0 comments on commit 9e0e726

Please sign in to comment.