Skip to content
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
3 changes: 3 additions & 0 deletions packages/kboot/src/models/data-option.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
type ProgressCallback = (percent: number) => void;

export interface DataOption {
startAddress: number;
data: Buffer | Uint8Array;
onProgress?: ProgressCallback;
timeout?: number;
}
5 changes: 5 additions & 0 deletions packages/kboot/src/usb-peripheral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export class UsbPeripheral implements Peripheral {

async writeMemory(option: DataOption): Promise<void> {
try {
option.onProgress?.(0);

const command: CommandOption = {
command: Commands.WriteMemory,
hasDataPhase: true,
Expand Down Expand Up @@ -110,6 +112,7 @@ export class UsbPeripheral implements Peripheral {
await this._device.write(writeData);
// workaround to prevent main thread blocking
await snooze(1);
option.onProgress?.(Math.min(100, Math.round((i + slice.length) / option.data.length * 100)));
}

const receivedData = await this._device.read(option.timeout || 2000);
Expand All @@ -127,6 +130,8 @@ export class UsbPeripheral implements Peripheral {
const msg = `Non zero write memory final response! Response code: ${secondCommandResponse.code}`;
throw new Error(msg);
}

option.onProgress?.(100);
} catch (err) {
logger('Can not write memory data %O', err);
throw err;
Expand Down
2 changes: 1 addition & 1 deletion packages/kboot/test/test-peripheral.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CommandOption, CommandResponse, DataOption, Peripheral, ResponseCodes, ResponseTags } from '../src/index.js';

export class TestPeripheral implements Peripheral {
close(): void {
close(): Promise<void> {
}

async open(): Promise<void> {
Expand Down
7 changes: 6 additions & 1 deletion packages/mcumgr/src/mcumgr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from './constants.js';
import { ImageUploadRequest } from './models/image-upload-request.js';
import { ImageUploadResponseData } from './models/image-upload-response-data.js';
import { ProgressCallback } from './models/progress-callback.js';
import { NmpResponse } from './models/nmp.js';
import { Peripheral } from './peripheral.js';
import * as cbor from './util/cbor.js';
Expand Down Expand Up @@ -74,9 +75,10 @@ export class McuManager {
/**
* Upload a firmware/bootloader image to the device
*/
async imageUpload(buffer: Buffer): Promise<void> {
async imageUpload(buffer: Buffer, onProgress?: ProgressCallback): Promise<void> {
logger('Start send image upload command: %o', { bufferLength: buffer.byteLength });
let written = 0;
onProgress?.(0);

while (written < buffer.length) {
const message: ImageUploadRequest = {
Expand Down Expand Up @@ -104,7 +106,10 @@ export class McuManager {
}

logger('Image uploaded: %d', written / buffer.length * 100);
onProgress?.(Math.min(100, Math.round(written / buffer.length * 100)));
}

onProgress?.(100);
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/mcumgr/src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './image-upload-request.js';
export * from './nmp.js';
export * from './progress-callback.js';
1 change: 1 addition & 0 deletions packages/mcumgr/src/models/progress-callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type ProgressCallback = (percent: number) => void;
102 changes: 75 additions & 27 deletions packages/uhk-agent/src/services/device.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
escapeZephyrControlChars,
findUhkModuleById,
FIRMWARE_UPGRADE_METHODS,
FirmwareUpgradeConnectPrompt,
FirmwareUpgradeIpcResponse,
getHardwareConfigFromDeviceResponse,
getUserConfigFromDeviceResponse,
Expand All @@ -32,6 +33,7 @@ import {
LeftSlotModules,
LogService,
mapObjectToUserConfigBinaryBuffer,
ModuleFirmwareUpgradeProgress,
ModuleFirmwareUpgradeSkipInfo,
ModuleFirmwareUpgradeSkipReason,
ModuleInfo,
Expand Down Expand Up @@ -560,7 +562,7 @@ export class DeviceService {
moduleName: UHK_DONGLE.name,
newFirmwareChecksum: deviceConfig.md5,
} as CurrentlyUpdatingModuleInfo);
await dongleOperations.updateDeviceFirmware(dongleFirmwarePath, UHK_DONGLE);
await dongleOperations.updateDeviceFirmware(dongleFirmwarePath, UHK_DONGLE, this.createFirmwareProgressReporter(event.sender, UHK_DONGLE.name));
this.logService.misc('[DeviceService] Waiting for keyboard');
await waitForDevices(UHK_DONGLE.keyboard);
await dongleUhkDevice.close();
Expand Down Expand Up @@ -609,7 +611,7 @@ export class DeviceService {
newFirmwareChecksum: deviceConfig.md5,
moduleName: RIGHT_HALF_FIRMWARE_UPGRADE_MODULE_NAME,
} as CurrentlyUpdatingModuleInfo);
await this.operations.updateDeviceFirmware(deviceFirmwarePath, uhkDeviceProduct);
await this.operations.updateDeviceFirmware(deviceFirmwarePath, uhkDeviceProduct, this.createFirmwareProgressReporter(event.sender, RIGHT_HALF_FIRMWARE_UPGRADE_MODULE_NAME));
this.logService.misc('[DeviceService] Waiting for keyboard');
await waitForDevices(uhkDeviceProduct.keyboard);

Expand Down Expand Up @@ -673,28 +675,31 @@ export class DeviceService {
} as CurrentlyUpdatingModuleInfo);

if(uhkDeviceProduct.firmwareUpgradeMethod === FIRMWARE_UPGRADE_METHODS.MCUBOOT) {
if (!(await isUhkDeviceConnected(UHK_80_DEVICE_LEFT))) {
this.logService.misc('[DeviceService] To continue the firmware upgrade, now connect the left half via USB. (You can disconnect the right half or use a second USB cable.)');
}
await this.waitForUhkDeviceWithConnectPrompt(
event.sender,
UHK_80_DEVICE_LEFT,
'To continue the firmware upgrade, now connect the left half via USB. (You can disconnect the right half or use a second USB cable.)'
);

await waitForUhkDeviceConnected(UHK_80_DEVICE_LEFT);
await snooze(1000);
const firmwarePath = getDeviceFirmwarePath(UHK_80_DEVICE_LEFT, packageJson);
await this.operations.updateFirmwareWithMcuManager(firmwarePath, UHK_80_DEVICE_LEFT);

if (!(await isUhkDeviceConnected(uhkDeviceProduct))) {
this.logService.misc('[DeviceService] To finish the firmware upgrade, now connect the right half via USB. (You can disconnect the left half or use a second USB cable.)');
}
await this.operations.updateFirmwareWithMcuManager(firmwarePath, UHK_80_DEVICE_LEFT, this.createFirmwareProgressReporter(event.sender, leftModuleInfo.module.name));

await waitForUhkDeviceConnected(uhkDeviceProduct);
await this.waitForUhkDeviceWithConnectPrompt(
event.sender,
uhkDeviceProduct,
'To finish the firmware upgrade, now connect the right half via USB. (You can disconnect the left half or use a second USB cable.)'
);
}
else {
await this.operations
.updateModuleWithKboot(
getModuleFirmwarePath(leftModuleInfo.module, packageJson),
uhkDeviceProduct,
leftModuleInfo.module
);
.updateModuleWithKboot({
firmwarePath: getModuleFirmwarePath(leftModuleInfo.module, packageJson),
device: uhkDeviceProduct,
module: leftModuleInfo.module,
onProgress: this.createFirmwareProgressReporter(event.sender, leftModuleInfo.module.name),
prompt: (message) => this.firmwareUpgradePrompt(event.sender, message),
});
}
} else {
const moduleConfig = packageJson.modules.find(firmwareDevice => firmwareDevice.moduleId === leftModuleInfo.module.id);
Expand Down Expand Up @@ -752,11 +757,13 @@ export class DeviceService {
newFirmwareChecksum: moduleConfig.md5,
} as CurrentlyUpdatingModuleInfo);
await this.operations
.updateModuleWithKboot(
getModuleFirmwarePath(moduleInfo.module, packageJson),
uhkDeviceProduct,
moduleInfo.module
);
.updateModuleWithKboot({
firmwarePath: getModuleFirmwarePath(moduleInfo.module, packageJson),
device: uhkDeviceProduct,
module: moduleInfo.module,
onProgress: this.createFirmwareProgressReporter(event.sender, moduleInfo.module.name),
prompt: (message) => this.firmwareUpgradePrompt(event.sender, message),
});
this.logService.misc(`[DeviceService] "${moduleInfo.module.name}" firmware update done.`);
} else {
const moduleConfig = packageJson.modules.find(firmwareDevice => firmwareDevice.moduleId === moduleInfo.module.id);
Expand Down Expand Up @@ -815,6 +822,7 @@ export class DeviceService {
await this.dongleZephyrLogService.enable();
await this.leftHalfZephyrLogService.enable();

event.sender.send(IpcEvents.device.firmwareUpgradeConnectPrompt, undefined);
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
}

Expand Down Expand Up @@ -897,11 +905,11 @@ export class DeviceService {
this.logService.misc('[DeviceService] UHK Module: ', JSON.stringify(uhkModule));

await this.operations
.updateModuleWithKboot(
getModuleFirmwarePath(uhkModule, packageJson),
uhkDeviceProduct,
uhkModule
);
.updateModuleWithKboot({
firmwarePath: getModuleFirmwarePath(uhkModule, packageJson),
device: uhkDeviceProduct,
module: uhkModule
});

response.success = true;
} catch (error) {
Expand Down Expand Up @@ -1182,6 +1190,24 @@ export class DeviceService {
this.win.webContents.send(IpcEvents.device.statusBufferChanged, message);
}

private createFirmwareProgressReporter(eventSender: Electron.WebContents, moduleName: string) {
let lastProgress = 0;

return (percent: number) => {
const progress = Math.max(0, Math.min(100, Math.round(percent)));

if (progress === lastProgress) {
return;
}

lastProgress = progress;
eventSender.send(IpcEvents.device.moduleFirmwareUpgradeProgress, {
moduleName,
progress,
} as ModuleFirmwareUpgradeProgress);
};
}

/**
* HID API not support device attached and detached event.
* This method check the keyboard is attached to the computer or not.
Expand Down Expand Up @@ -1516,4 +1542,26 @@ export class DeviceService {
this.win.webContents.send(IpcEvents.device.zephyrLog, logEntry)
}
}

private async waitForUhkDeviceWithConnectPrompt(
eventSender: Electron.WebContents,
device: UhkDeviceProduct,
message: string
): Promise<void> {
if (!(await isUhkDeviceConnected(device))) {
this.firmwareUpgradePrompt(eventSender, message)
}

await waitForUhkDeviceConnected(device);

this.firmwareUpgradePrompt(eventSender, undefined)
}

private firmwareUpgradePrompt(
eventSender: Electron.WebContents,
message: string
): void {
this.logService.misc(`[DeviceService] firmware upgrade prompt: ${message}`);
eventSender.send(IpcEvents.device.firmwareUpgradeConnectPrompt, { message } as FirmwareUpgradeConnectPrompt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface FirmwareUpgradeConnectPrompt {
message: string;
}
4 changes: 4 additions & 0 deletions packages/uhk-common/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from './device-version-information.js';
export * from './dongle.js';
export * from './firmware-json.js';
export * from './firmware-repo-info.js';
export * from './firmware-upgrade-connect-prompt.js';
export * from './firmware-upgrade-method.js';
export * from './firmware-version.js';
export * from './firmware-version-info.js';
Expand All @@ -23,12 +24,15 @@ export * from './app-start-info.js';
export * from './configuration-reply.js';
export * from './version-information.js';
export * from './device-connection-state.js';
export * from './module-firmware-upgrade-progress.js';
export * from './module-firmware-upgrade-skip-info.js';
export * from './module-slot-to-i2c-adress.js';
export * from './module-slot-id.js';
export * from './module-version-info.js';
export * from './log-options.js';
export * from './hardware-modules.js';
export * from './progress-callback.js';
export * from './prompt-callback.js';
export * from './rgb-color-interface.js';
export * from './right-module-info.js';
export * from './right-slot-modules.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ModuleFirmwareUpgradeProgress {
moduleName: string;
progress: number;
}
1 change: 1 addition & 0 deletions packages/uhk-common/src/models/progress-callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type ProgressCallback = (percent: number) => void;
1 change: 1 addition & 0 deletions packages/uhk-common/src/models/prompt-callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type PromptCallback = (message: string) => void;
2 changes: 2 additions & 0 deletions packages/uhk-common/src/util/ipcEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export class Device {
public static readonly updateFirmware = 'device-update-firmware';
public static readonly updateFirmwareJson = 'device-update-firmware-json';
public static readonly updateFirmwareReply = 'device-update-firmware-reply';
public static readonly firmwareUpgradeConnectPrompt = 'device-firmware-upgrade-connect-prompt';
public static readonly moduleFirmwareUpgradeProgress = 'device-module-firmware-upgrade-progress';
public static readonly moduleFirmwareUpgradeSkip = 'device-module-firmware-upgrade-skip';
public static readonly moduleFirmwareUpgrading = 'device-module-firmware-upgrading';
public static readonly startConnectionPoller = 'device-start-connection-poller';
Expand Down
1 change: 1 addition & 0 deletions packages/uhk-usb/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './pairing-info.js';
export * from './reenumerate-option.js';
export * from './reenumerate-result.js';
export * from './tmp-firmware.js';
export * from './update-module-with-kboot-options.js';
18 changes: 18 additions & 0 deletions packages/uhk-usb/src/models/update-module-with-kboot-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
ProgressCallback,
PromptCallback,
UhkModule,
UhkDeviceProduct,
} from 'uhk-common';

export interface UpdateLeftModuleWithKbootOptions {
device: UhkDeviceProduct,
firmwarePath: string,
onProgress?: ProgressCallback
prompt?: PromptCallback
}


export interface UpdateModuleWithKbootOptions extends UpdateLeftModuleWithKbootOptions{
module: UhkModule,
}
Loading
Loading