11import { McuManager , SerialPeripheral } from '@uhk/mcumgr' ;
22import * as fs from 'fs' ;
3+ import { readFile } from 'node:fs/promises' ;
34import { DataOption , KBoot , Properties , UsbPeripheral } from 'kboot' ;
45import {
56 ALL_UHK_DEVICES ,
@@ -45,6 +46,11 @@ import {
4546 UsbCommand ,
4647 UsbVariables
4748} from './constants.js' ;
49+ import {
50+ MODULE_FLASH_STATE ,
51+ MODULE_FLASH_STATE_TYPE ,
52+ ModuleFlashResponse ,
53+ } from './models/module-flash-state.js' ;
4854import {
4955 DebugInfo ,
5056 Duration ,
@@ -60,6 +66,7 @@ import {
6066 readBootloaderFirmwareFromHexFileAsync ,
6167 waitForDevice
6268} from './util.js' ;
69+ import crc16 from './utils/crc16.js' ;
6370import { generateDeviceSerialNumber } from './utils/generate-device-serial-number.js' ;
6471import { convertMsToDuration , convertSlaveI2cErrorBuffer , snooze , waitUntil } from './utils/index.js' ;
6572import { normalizeStatusBuffer } from './utils/normalize-status-buffer.js' ;
@@ -197,7 +204,7 @@ export class UhkOperations {
197204 const peripheral = new SerialPeripheral ( reenumerateResult . serialPath ) ;
198205 const mcuManager = new McuManager ( peripheral ) ;
199206 this . logService . misc ( `[UhkOperations] Read ${ device . logName } firmware from file` ) ;
200- const configData = fs . readFileSync ( firmwarePath ) ;
207+ const configData = await readFile ( firmwarePath ) ;
201208 this . logService . misc ( '[UhkOperations] Write memory with mcumgr' ) ;
202209 await mcuManager . imageUpload ( configData ) ;
203210 this . logService . misc ( '[UhkOperations] Reset mcu bootloader' ) ;
@@ -275,7 +282,7 @@ export class UhkOperations {
275282 await kboot . flashEraseAllUnsecure ( ) ;
276283
277284 this . logService . misc ( `[UhkOperations] Read "${ module . name } " firmware from file` ) ;
278- const configData = fs . readFileSync ( firmwarePath ) ;
285+ const configData = await readFile ( firmwarePath ) ;
279286
280287 this . logService . misc ( '[UhkOperations] Write memory' ) ;
281288 await kboot . configureI2c ( module . i2cAddress ) ;
@@ -304,6 +311,51 @@ export class UhkOperations {
304311 this . logService . misc ( `[UhkOperations] "${ module . name } " firmware successfully flashed` ) ;
305312 }
306313
314+ public async updateModuleWithKbootNative (
315+ firmwarePath : string ,
316+ device : UhkDeviceProduct ,
317+ module : UhkModule
318+ ) : Promise < void > {
319+ this . logService . misc ( `[UhkOperations][kboot-native] Start flashing "${ module . name } " module firmware` ) ;
320+ await this . device . reenumerate ( {
321+ device,
322+ enumerationMode : EnumerationModes . NormalKeyboard ,
323+ } ) ;
324+ await this . device . close ( ) ;
325+ await snooze ( 1000 ) ;
326+
327+ const configData = await readFile ( firmwarePath ) as any ;
328+ this . logService . misc ( '[UhkOperations][kboot-native] sending firmware to the keyboard' ) ;
329+ await this . sendConfigToKeyboard ( configData , UsbCommand . WriteModuleFirmware ) ;
330+
331+ this . logService . misc ( '[UhkOperations][kboot-native] validating module firmware' ) ;
332+ const crc = crc16 ( configData ) ;
333+ this . logService . misc ( `[UhkOperations][kboot-native] module firmware crc: ${ crc } ` ) ;
334+ const isValid = await this . isBufferValid ( ConfigBufferId . moduleFirmware , configData . length , crc ) ;
335+
336+ if ( ! isValid ) {
337+ this . logService . error ( '[UhkOperations][kboot-native] module firmware crc validation failed' ) ;
338+ throw new Error ( 'Module firmware crc validation failed' ) ;
339+ }
340+
341+ this . logService . misc ( '[UhkOperations][kboot-native] flashing module' ) ;
342+ await this . flashModule ( module ) ;
343+
344+ this . logService . misc ( '[UhkOperations][kboot-native] wait until flashing finished' ) ;
345+ const response = await this . waitUntilModuleFlashFinished ( ) ;
346+
347+ if ( response . state === MODULE_FLASH_STATE . Error ) {
348+ this . logService . error ( `[UhkOperations][kboot-native] error occurred in the module flashing process. Error code: ${ response . errorCode } ` ) ;
349+
350+ throw new Error ( `Error occurred in the module flashing process. Error code: ${ response . errorCode } ` ) ;
351+ }
352+
353+ this . logService . misc ( '[UhkOperations][kboot-native] wait until flashing finished' ) ;
354+ await this . waitUntilModuleFlashFinished ( ) ;
355+
356+ this . logService . misc ( `[UhkOperations][kboot-native] "${ module . name } " firmware successfully flashed` ) ;
357+ }
358+
307359 /**
308360 * Return with the actual UserConfiguration from UHK Device
309361 * @returns {Promise<Buffer> }
@@ -463,9 +515,15 @@ export class UhkOperations {
463515 const transferPercentRange = 0.83 ;
464516
465517 reportProgress ( preTransferPercent ) ;
466- await this . sendConfigToKeyboard ( configBuffer , true , ( percent ) => {
467- reportProgress ( preTransferPercent + Math . round ( percent * transferPercentRange ) ) ;
468- } ) ;
518+
519+ await this . sendConfigToKeyboard (
520+ resultBuffer . getBufferContent ( ) ,
521+ UsbCommand . WriteStagingUserConfig ,
522+ ( percent ) => {
523+ reportProgress ( preTransferPercent + Math . round ( percent * transferPercentRange ) ) ;
524+ } ,
525+ ) ;
526+
469527 reportProgress ( 86 ) ;
470528 await this . applyConfiguration ( ) ;
471529 reportProgress ( 90 ) ;
@@ -501,7 +559,7 @@ export class UhkOperations {
501559 hardwareConfig . toBinary ( hardwareBuffer ) ;
502560 const buffer = hardwareBuffer . getBufferContent ( ) ;
503561
504- await this . sendConfigToKeyboard ( buffer , false ) ;
562+ await this . sendConfigToKeyboard ( buffer , UsbCommand . WriteHardwareConfig ) ;
505563 await this . writeConfigToEeprom ( ConfigBufferId . hardwareConfig ) ;
506564 await this . waitUntilKeyboardBusy ( ) ;
507565 }
@@ -529,6 +587,25 @@ export class UhkOperations {
529587 }
530588 }
531589
590+ public async waitUntilModuleFlashFinished ( ) : Promise < ModuleFlashResponse > {
591+ while ( true ) {
592+ const response = await this . getModuleFlashState ( ) ;
593+ if ( response . state === MODULE_FLASH_STATE . Erasing ) {
594+ this . logService . misc ( '[DeviceOperation] Module flash erasing, wait...' ) ;
595+ await snooze ( 200 ) ;
596+ continue ;
597+ }
598+
599+ if ( response . state === MODULE_FLASH_STATE . Writing ) {
600+ this . logService . misc ( '[DeviceOperation] Module flash writing, wait...' ) ;
601+ await snooze ( 200 ) ;
602+ continue ;
603+ }
604+
605+ return response ;
606+ }
607+ }
608+
532609 public async waitForKbootIdle ( moduleName : string ) : Promise < boolean > {
533610 while ( true ) {
534611 const buffer = await this . device . write ( Buffer . from ( [ UsbCommand . GetProperty , DevicePropertyIds . CurrentKbootCommand ] ) ) ;
@@ -544,6 +621,15 @@ export class UhkOperations {
544621 }
545622 }
546623
624+ public async getModuleFlashState ( ) : Promise < ModuleFlashResponse > {
625+ const buffer = await this . device . write ( Buffer . from ( [ UsbCommand . GetModuleFlashState ] ) ) ;
626+
627+ return {
628+ state : buffer [ 0 ] as MODULE_FLASH_STATE_TYPE ,
629+ errorCode : buffer [ 1 ]
630+ }
631+ }
632+
547633 public async getModuleProperty ( { module, property } : GetModulePropertyArguments ) : Promise < UhkBuffer > {
548634 const moduleSlotName = getSlotIdName ( module ) ;
549635
@@ -697,17 +783,24 @@ export class UhkOperations {
697783 public async eraseHardwareConfig ( ) : Promise < void > {
698784 this . logService . usbOps ( '[DeviceOperation] USB[T]: Erase hardware configuration' ) ;
699785 const buffer = Buffer . from ( Array ( 64 ) . fill ( 0xff ) ) ;
700- await this . sendConfigToKeyboard ( buffer , false ) ;
786+ await this . sendConfigToKeyboard ( buffer , UsbCommand . WriteHardwareConfig ) ;
701787 await this . writeConfigToEeprom ( ConfigBufferId . hardwareConfig ) ;
702788 }
703789
704790 public async eraseUserConfig ( ) : Promise < void > {
705791 this . logService . usbOps ( '[DeviceOperation] USB[T]: Erase user configuration' ) ;
706792 const buffer = Buffer . from ( Array ( 2 ** 15 - 64 ) . fill ( 0xff ) ) ;
707- await this . sendConfigToKeyboard ( buffer , true ) ;
793+ await this . sendConfigToKeyboard ( buffer , UsbCommand . WriteStagingUserConfig ) ;
708794 await this . writeConfigToEeprom ( ConfigBufferId . stagingUserConfig ) ;
709795 }
710796
797+ public async flashModule ( module : UhkModule ) : Promise < void > {
798+ this . logService . usbOps ( '[DeviceOperation] USB[T]: Flash module' ) ;
799+ const buffer = Buffer . from ( [ UsbCommand . FlashModule , module . slotId ] ) ;
800+
801+ await this . device . write ( buffer ) ;
802+ }
803+
711804 public async switchKeymap ( keymapAbbreviation : string ) : Promise < void > {
712805 this . logService . usbOps ( '[DeviceOperation] USB[T]: Switch keymap' ) ;
713806 const keymapAbbreviationAscii = keymapAbbreviation . split ( '' ) . map ( char => char . charCodeAt ( 0 ) ) ;
@@ -958,19 +1051,15 @@ export class UhkOperations {
9581051 /**
9591052 * IpcMain handler. Send the UserConfiguration to the UHK Device and send a response with the result.
9601053 * @param {Buffer } buffer - UserConfiguration buffer
961- * @param {Boolean } isUserConfiguration - User or Hardware configuration
1054+ * @param {UsbCommand } command - Represent which configuration area write to the device
9621055 * @returns {Promise<void> }
9631056 * @private
9641057 */
9651058 private async sendConfigToKeyboard (
9661059 buffer : Buffer ,
967- isUserConfiguration ,
1060+ command : UsbCommand ,
9681061 onProgress ?: ( percent : number ) => void
9691062 ) : Promise < void > {
970- const command = isUserConfiguration
971- ? UsbCommand . WriteStagingUserConfig
972- : UsbCommand . WriteHardwareConfig ;
973-
9741063 const fragments = getTransferBuffers ( command , buffer ) ;
9751064 for ( let i = 0 ; i < fragments . length ; i ++ ) {
9761065 await this . device . write ( fragments [ i ] ) ;
@@ -1005,4 +1094,17 @@ export class UhkOperations {
10051094
10061095 await this . device . write ( buffer ) ;
10071096 }
1097+
1098+ public async isBufferValid ( bufferId : ConfigBufferId , expectedSize : number , expectedCrc : number ) : Promise < boolean > {
1099+ this . logService . usbOps ( '[DeviceOperation] USB[T]: Validate Buffer CRC' ) ;
1100+ const buffer = Buffer . alloc ( 6 ) ;
1101+ buffer . writeUInt8 ( UsbCommand . ValidateBufferCrc , 0 ) ;
1102+ buffer . writeUInt8 ( bufferId , 1 ) ;
1103+ buffer . writeUInt16LE ( expectedSize , 2 ) ;
1104+ buffer . writeUInt16LE ( expectedCrc , 4 ) ;
1105+
1106+ const response = await this . device . write ( buffer ) ;
1107+
1108+ return response [ 1 ] === 0 ;
1109+ }
10081110}
0 commit comments