feat: kboot package (#894)

* feat: kboot package

* feat: kboot package

* fix: wait 1 sec after device is available

* test: fix unit test

* refactor: clean unused codes

* doc: improve readme.md

* doc: improve readme.md

* test: fix unit test

* chore: fix lint settings

* style: fix linting issues
This commit is contained in:
Róbert Kiss
2019-01-18 17:37:31 +01:00
committed by László Monda
parent bfc08edfce
commit 3964698cf7
53 changed files with 1784 additions and 249 deletions

View File

@@ -0,0 +1,36 @@
import { BootloaderVersion, CommandResponse, Commands, KBoot, Peripheral, Properties, ResponseCodes, ResponseTags } from '../src';
import { TestPeripheral } from './test-peripheral';
describe('kboot', () => {
let kboot: KBoot;
let testPeripheral: Peripheral;
beforeEach(() => {
testPeripheral = new TestPeripheral();
kboot = new KBoot(testPeripheral);
});
describe('getBootloaderVersion', () => {
it('should works', async () => {
const sendCommandResponse: CommandResponse = {
code: ResponseCodes.Success,
tag: ResponseTags.Property,
// tslint:disable-next-line:max-line-length
raw: Buffer.from([0x03, 0x00, 0x0c, 0x00, 0xa7, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
};
spyOn(testPeripheral, 'sendCommand').and.returnValue(Promise.resolve(sendCommandResponse));
const version = await kboot.getBootloaderVersion();
const expectedVersion: BootloaderVersion = {
protocolName: 'K',
major: 2,
minor: 0,
bugfix: 0
};
expect(version).toEqual(expectedVersion);
expect(testPeripheral.sendCommand).toHaveBeenCalledWith({
command: Commands.GetProperty,
params: [1, 0, 0, 0, 0, 0, 0, 0]
});
});
});
});

View File

@@ -0,0 +1,27 @@
import { CommandOption, CommandResponse, DataOption, Peripheral, ResponseCodes, ResponseTags } from '../src';
export class TestPeripheral implements Peripheral {
close(): void {
}
open(): void {
}
sendCommand(options: CommandOption): Promise<CommandResponse> {
const response = {
tag: ResponseTags.Generic,
code: ResponseCodes.Success,
raw: new Buffer(0)
};
return Promise.resolve(response);
}
writeMemory(data: DataOption): Promise<void> {
return Promise.resolve();
}
readMemory(startAddress: number, count: number): Promise<Buffer> {
return Promise.resolve(new Buffer(0));
}
}

View File

@@ -0,0 +1,33 @@
import { execSync } from 'child_process';
import { join } from 'path';
import { readFileSync } from 'fs';
import * as MemoryMap from 'nrf-intel-hex';
export enum UhkReenumerationModes {
Bootloader = 'bootloader',
Buspal = 'buspal',
NormalKeyboard = 'normalKeyboard',
CompatibleKeyboard = 'compatibleKeyboard'
}
const USB_SCRIPTS_DIR = join(__dirname, '../../../usb');
export const reenumerate = (mode: UhkReenumerationModes): void => {
const reenumerateScriptFile = join(USB_SCRIPTS_DIR, 'reenumerate.js');
const command = [reenumerateScriptFile, mode.toString()].join(' ');
execSync(
command,
{
cwd: USB_SCRIPTS_DIR,
stdio: [0, 1, 2]
}
);
};
export const readBootloaderFirmwareFromHexFile = (): Map<any, any> => {
const hexFilePath = join(__dirname, '../../../../tmp/packages/firmware/devices/uhk60-right/firmware.hex');
const fileContent = readFileSync(hexFilePath, { encoding: 'utf8' });
const memoryMap = MemoryMap.fromHex(fileContent);
return memoryMap;
};

View File

@@ -0,0 +1,10 @@
import { encodeStringToParams } from '../../src/util';
describe('encodeStringToParams', () => {
xit('should convert 8 character to little endian 4 byte array', () => {
const result = encodeStringToParams('0403020108070605');
const expectedResult = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
expect(result).toEqual(expectedResult);
});
});

View File

@@ -0,0 +1,17 @@
import { getResponseCode, ResponseCodes } from '../../src';
describe('response-parser', () => {
describe('getResponseCode', () => {
it('should return with success', () => {
const buffer = Buffer.from([0x03, 0x00, 0x08, 0x00, 0xa7, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]);
const responseCode = getResponseCode(buffer);
expect(responseCode).toEqual(ResponseCodes.Success);
});
it('should return with UnknownProperty', () => {
const buffer = Buffer.from([0x03, 0x00, 0x08, 0x00, 0xa7, 0x00, 0x00, 0x01, 0x3c, 0x28, 0x00, 0x00]);
const responseCode = getResponseCode(buffer);
expect(responseCode).toEqual(ResponseCodes.UnknownProperty);
});
});
});

View File

@@ -0,0 +1,16 @@
import { CommandOption, Commands } from '../../../src';
import { encodeCommandOption } from '../../../src/util';
describe('usb encodeCommandOption', () => {
it('should convert correctly', () => {
const option: CommandOption = {
command: Commands.GetProperty,
params: [1, 0, 0, 0, 0, 0, 0, 0]
};
const result = encodeCommandOption(option);
// tslint:disable-next-line:max-line-length
const expected = [1, 0, 0x0c, 0, 0x07, 0x00, 0x00, 0x02, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
expect(result).toEqual(expected);
});
});

View File

@@ -0,0 +1,81 @@
import { reenumerate, UhkReenumerationModes, readBootloaderFirmwareFromHexFile } from './uhk-helpers';
import { BootloaderVersion, UsbPeripheral } from '../src';
import { KBoot } from '../src/kboot';
import { DataOption } from '../index';
xdescribe('UHK Integration tests', () => {
describe('bootloader', () => {
let usb: UsbPeripheral;
let kboot: KBoot;
beforeEach(() => {
reenumerate(UhkReenumerationModes.Bootloader);
usb = new UsbPeripheral({ vendorId: 0x1d50, productId: 0x6120 });
kboot = new KBoot(usb);
});
afterEach(() => {
if (usb) {
usb.close();
}
if (kboot) {
kboot.close();
}
});
it('get bootloader version', async () => {
const expectedVersion: BootloaderVersion = {
protocolName: 'K',
major: 2,
minor: 0,
bugfix: 0
};
const version = await kboot.getBootloaderVersion();
expect(version).toEqual(expectedVersion);
});
it('disable flash security', () => {
const backdoorKey = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
return kboot
.flashSecurityDisable(backdoorKey)
.catch(err => {
expect(err).toBeFalsy();
});
});
it('flash erase region', () => {
return kboot
.flashEraseRegion(0xc000, 475136)
.catch(err => {
expect(err).toBeFalsy();
});
});
it('read memory', () => {
const dataLength = 128;
return kboot
.readMemory(0xc000, dataLength)
.then((data: number[]) => {
expect(data).toBeTruthy();
expect(data.length).toEqual(dataLength);
})
.catch(err => {
expect(err).toBeFalsy();
});
});
it('write memory', async () => {
const bootloaderMemoryMap = readBootloaderFirmwareFromHexFile();
for (const [startAddress, data] of bootloaderMemoryMap.entries()) {
const dataOption: DataOption = {
startAddress,
data
};
await kboot.writeMemory(dataOption);
}
});
});
});