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 ,
@@ -46,6 +47,11 @@ import {
4647 UsbCommand ,
4748 UsbVariables
4849} from './constants.js' ;
50+ import {
51+ MODULE_FLASH_STATE ,
52+ MODULE_FLASH_STATE_TYPE ,
53+ ModuleFlashResponse ,
54+ } from './models/module-flash-state.js' ;
4955import {
5056 DebugInfo ,
5157 Duration ,
@@ -61,6 +67,7 @@ import {
6167 readBootloaderFirmwareFromHexFileAsync ,
6268 waitForDevice
6369} from './util.js' ;
70+ import crc16 from './utils/crc16.js' ;
6471import { generateDeviceSerialNumber } from './utils/generate-device-serial-number.js' ;
6572import { convertMsToDuration , convertSlaveI2cErrorBuffer , snooze , waitUntil } from './utils/index.js' ;
6673import { normalizeStatusBuffer } from './utils/normalize-status-buffer.js' ;
@@ -198,7 +205,7 @@ export class UhkOperations {
198205 const peripheral = new SerialPeripheral ( reenumerateResult . serialPath ) ;
199206 const mcuManager = new McuManager ( peripheral ) ;
200207 this . logService . misc ( `[UhkOperations] Read ${ device . logName } firmware from file` ) ;
201- const configData = fs . readFileSync ( firmwarePath ) ;
208+ const configData = await readFile ( firmwarePath ) ;
202209 this . logService . misc ( '[UhkOperations] Write memory with mcumgr' ) ;
203210 await mcuManager . imageUpload ( configData ) ;
204211 this . logService . misc ( '[UhkOperations] Reset mcu bootloader' ) ;
@@ -276,7 +283,7 @@ export class UhkOperations {
276283 await kboot . flashEraseAllUnsecure ( ) ;
277284
278285 this . logService . misc ( `[UhkOperations] Read "${ module . name } " firmware from file` ) ;
279- const configData = fs . readFileSync ( firmwarePath ) ;
286+ const configData = await readFile ( firmwarePath ) ;
280287
281288 this . logService . misc ( '[UhkOperations] Write memory' ) ;
282289 await kboot . configureI2c ( module . i2cAddress ) ;
@@ -305,6 +312,51 @@ export class UhkOperations {
305312 this . logService . misc ( `[UhkOperations] "${ module . name } " firmware successfully flashed` ) ;
306313 }
307314
315+ public async updateModuleWithKbootNative (
316+ firmwarePath : string ,
317+ device : UhkDeviceProduct ,
318+ module : UhkModule
319+ ) : Promise < void > {
320+ this . logService . misc ( `[UhkOperations][kboot-native] Start flashing "${ module . name } " module firmware` ) ;
321+ await this . device . reenumerate ( {
322+ device,
323+ enumerationMode : EnumerationModes . NormalKeyboard ,
324+ } ) ;
325+ await this . device . close ( ) ;
326+ await snooze ( 1000 ) ;
327+
328+ const configData = await readFile ( firmwarePath ) as any ;
329+ this . logService . misc ( '[UhkOperations][kboot-native] sending firmware to the keyboard' ) ;
330+ await this . sendConfigToKeyboard ( configData , UsbCommand . WriteModuleFirmware ) ;
331+
332+ this . logService . misc ( '[UhkOperations][kboot-native] validating module firmware' ) ;
333+ const crc = crc16 ( configData ) ;
334+ this . logService . misc ( `[UhkOperations][kboot-native] module firmware crc: ${ crc } ` ) ;
335+ const isValid = await this . isBufferValid ( ConfigBufferId . moduleFirmware , configData . length , crc ) ;
336+
337+ if ( ! isValid ) {
338+ this . logService . error ( '[UhkOperations][kboot-native] module firmware crc validation failed' ) ;
339+ throw new Error ( 'Module firmware crc validation failed' ) ;
340+ }
341+
342+ this . logService . misc ( '[UhkOperations][kboot-native] flashing module' ) ;
343+ await this . flashModule ( module ) ;
344+
345+ this . logService . misc ( '[UhkOperations][kboot-native] wait until flashing finished' ) ;
346+ const response = await this . waitUntilModuleFlashFinished ( ) ;
347+
348+ if ( response . state === MODULE_FLASH_STATE . Error ) {
349+ this . logService . error ( `[UhkOperations][kboot-native] error occurred in the module flashing process. Error code: ${ response . errorCode } ` ) ;
350+
351+ throw new Error ( `Error occurred in the module flashing process. Error code: ${ response . errorCode } ` ) ;
352+ }
353+
354+ this . logService . misc ( '[UhkOperations][kboot-native] wait until flashing finished' ) ;
355+ await this . waitUntilModuleFlashFinished ( ) ;
356+
357+ this . logService . misc ( `[UhkOperations][kboot-native] "${ module . name } " firmware successfully flashed` ) ;
358+ }
359+
308360 /**
309361 * Return with the actual UserConfiguration from UHK Device
310362 * @returns {Promise<Buffer> }
@@ -423,7 +475,7 @@ export class UhkOperations {
423475
424476 const resultBuffer = new UhkBuffer ( UHK_EEPROM_SIZE )
425477 userConfiguration . toBinary ( resultBuffer )
426- await this . sendConfigToKeyboard ( resultBuffer . getBufferContent ( ) , true ) ;
478+ await this . sendConfigToKeyboard ( resultBuffer . getBufferContent ( ) , UsbCommand . WriteStagingUserConfig ) ;
427479 await this . applyConfiguration ( ) ;
428480 this . logService . usbOps ( '[DeviceOperation] USB[T]: Write user configuration to EEPROM' ) ;
429481 await this . writeConfigToEeprom ( ConfigBufferId . validatedUserConfig ) ;
@@ -455,7 +507,7 @@ export class UhkOperations {
455507 hardwareConfig . toBinary ( hardwareBuffer ) ;
456508 const buffer = hardwareBuffer . getBufferContent ( ) ;
457509
458- await this . sendConfigToKeyboard ( buffer , false ) ;
510+ await this . sendConfigToKeyboard ( buffer , UsbCommand . WriteHardwareConfig ) ;
459511 await this . writeConfigToEeprom ( ConfigBufferId . hardwareConfig ) ;
460512 await this . waitUntilKeyboardBusy ( ) ;
461513 }
@@ -483,6 +535,25 @@ export class UhkOperations {
483535 }
484536 }
485537
538+ public async waitUntilModuleFlashFinished ( ) : Promise < ModuleFlashResponse > {
539+ while ( true ) {
540+ const response = await this . getModuleFlashState ( ) ;
541+ if ( response . state === MODULE_FLASH_STATE . Erasing ) {
542+ this . logService . misc ( '[DeviceOperation] Module flash erasing, wait...' ) ;
543+ await snooze ( 200 ) ;
544+ continue ;
545+ }
546+
547+ if ( response . state === MODULE_FLASH_STATE . Writing ) {
548+ this . logService . misc ( '[DeviceOperation] Module flash writing, wait...' ) ;
549+ await snooze ( 200 ) ;
550+ continue ;
551+ }
552+
553+ return response ;
554+ }
555+ }
556+
486557 public async waitForKbootIdle ( moduleName : string ) : Promise < boolean > {
487558 while ( true ) {
488559 const buffer = await this . device . write ( Buffer . from ( [ UsbCommand . GetProperty , DevicePropertyIds . CurrentKbootCommand ] ) ) ;
@@ -498,6 +569,15 @@ export class UhkOperations {
498569 }
499570 }
500571
572+ public async getModuleFlashState ( ) : Promise < ModuleFlashResponse > {
573+ const buffer = await this . device . write ( Buffer . from ( [ UsbCommand . GetModuleFlashState ] ) ) ;
574+
575+ return {
576+ state : buffer [ 0 ] as MODULE_FLASH_STATE_TYPE ,
577+ errorCode : buffer [ 1 ]
578+ }
579+ }
580+
501581 public async getModuleProperty ( { module, property } : GetModulePropertyArguments ) : Promise < UhkBuffer > {
502582 const moduleSlotName = getSlotIdName ( module ) ;
503583
@@ -651,17 +731,24 @@ export class UhkOperations {
651731 public async eraseHardwareConfig ( ) : Promise < void > {
652732 this . logService . usbOps ( '[DeviceOperation] USB[T]: Erase hardware configuration' ) ;
653733 const buffer = Buffer . from ( Array ( 64 ) . fill ( 0xff ) ) ;
654- await this . sendConfigToKeyboard ( buffer , false ) ;
734+ await this . sendConfigToKeyboard ( buffer , UsbCommand . WriteHardwareConfig ) ;
655735 await this . writeConfigToEeprom ( ConfigBufferId . hardwareConfig ) ;
656736 }
657737
658738 public async eraseUserConfig ( ) : Promise < void > {
659739 this . logService . usbOps ( '[DeviceOperation] USB[T]: Erase user configuration' ) ;
660740 const buffer = Buffer . from ( Array ( 2 ** 15 - 64 ) . fill ( 0xff ) ) ;
661- await this . sendConfigToKeyboard ( buffer , true ) ;
741+ await this . sendConfigToKeyboard ( buffer , UsbCommand . WriteStagingUserConfig ) ;
662742 await this . writeConfigToEeprom ( ConfigBufferId . stagingUserConfig ) ;
663743 }
664744
745+ public async flashModule ( module : UhkModule ) : Promise < void > {
746+ this . logService . usbOps ( '[DeviceOperation] USB[T]: Flash module' ) ;
747+ const buffer = Buffer . from ( [ UsbCommand . FlashModule , module . slotId ] ) ;
748+
749+ await this . device . write ( buffer ) ;
750+ }
751+
665752 public async switchKeymap ( keymapAbbreviation : string ) : Promise < void > {
666753 this . logService . usbOps ( '[DeviceOperation] USB[T]: Switch keymap' ) ;
667754 const keymapAbbreviationAscii = keymapAbbreviation . split ( '' ) . map ( char => char . charCodeAt ( 0 ) ) ;
@@ -909,15 +996,11 @@ export class UhkOperations {
909996 /**
910997 * IpcMain handler. Send the UserConfiguration to the UHK Device and send a response with the result.
911998 * @param {Buffer } buffer - UserConfiguration buffer
912- * @param {Boolean } isUserConfiguration - User or Hardware configuration
999+ * @param {UsbCommand } command - Represent which configuration area write to the device
9131000 * @returns {Promise<void> }
9141001 * @private
9151002 */
916- private async sendConfigToKeyboard ( buffer : Buffer , isUserConfiguration ) : Promise < void > {
917- const command = isUserConfiguration
918- ? UsbCommand . WriteStagingUserConfig
919- : UsbCommand . WriteHardwareConfig ;
920-
1003+ private async sendConfigToKeyboard ( buffer : Buffer , command : UsbCommand ) : Promise < void > {
9211004 const fragments = getTransferBuffers ( command , buffer ) ;
9221005 for ( const fragment of fragments ) {
9231006 await this . device . write ( fragment ) ;
@@ -937,4 +1020,17 @@ export class UhkOperations {
9371020
9381021 await this . device . write ( buffer ) ;
9391022 }
1023+
1024+ public async isBufferValid ( bufferId : ConfigBufferId , expectedSize : number , expectedCrc : number ) : Promise < boolean > {
1025+ this . logService . usbOps ( '[DeviceOperation] USB[T]: Validate Buffer CRC' ) ;
1026+ const buffer = Buffer . alloc ( 6 ) ;
1027+ buffer . writeUInt8 ( UsbCommand . ValidateBufferCrc , 0 ) ;
1028+ buffer . writeUInt8 ( bufferId , 1 ) ;
1029+ buffer . writeUInt16LE ( expectedSize , 2 ) ;
1030+ buffer . writeUInt16LE ( expectedCrc , 4 ) ;
1031+
1032+ const response = await this . device . write ( buffer ) ;
1033+
1034+ return response [ 1 ] === 0 ;
1035+ }
9401036}
0 commit comments