Skip to content

Commit a24ebf4

Browse files
Merge pull request #53 from balena-io-modules/aethernet/allow-custom-sb-assets
minor: allow passing custom assets to start SB protected CM4
2 parents 04a6aca + 5d15666 commit a24ebf4

7 files changed

Lines changed: 182 additions & 142 deletions

File tree

.eslintrc.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
extends: ["./node_modules/@balena/lint/config/.eslintrc.js"],
3+
root: true,
4+
ignorePatterns: ["node_modules/"],
5+
rules: {
6+
"no-bitwise": "off",
7+
},
8+
};

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
build
22
node_modules
33
*.swo
4+
extra-folder*

lib/index.ts

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import { usb } from 'usb';
99
import { OutEndpoint } from 'usb/dist/usb/endpoint';
10-
import { Interface } from 'usb/dist/usb/interface';
10+
import type { Interface } from 'usb/dist/usb/interface';
1111
import * as _debug from 'debug';
1212
import { EventEmitter } from 'events';
1313
import { readFile } from 'fs/promises';
@@ -340,27 +340,41 @@ const getDeviceId = (device: usb.Device): string => {
340340
return `${device.busNumber}:${device.deviceAddress}`;
341341
};
342342

343-
const safeReadFile = async (filename: string): Promise<Buffer | undefined> => {
343+
const getFileBuffer = async (
344+
device: usb.Device,
345+
filename: string,
346+
extraFolder?: string,
347+
): Promise<Buffer | undefined> => {
344348
try {
345-
return await readFile(Path.join(__dirname, '..', 'blobs', filename));
349+
if (extraFolder) {
350+
const extraBuffer = await readFile(Path.join(extraFolder, filename));
351+
if (extraBuffer !== undefined) {
352+
debug(`Sending buffer from ${extraFolder}/${filename}`);
353+
return extraBuffer;
354+
}
355+
}
346356
} catch (e) {
347357
// no data
348358
}
349-
};
350359

351-
const getFileBuffer = async (
352-
device: usb.Device,
353-
filename: string,
354-
): Promise<Buffer | undefined> => {
355-
const folder =
356-
device.deviceDescriptor.idProduct === USB_PRODUCT_ID_BCM2711_BOOT
357-
? 'cm4'
358-
: 'raspberrypi';
359-
const buffer = await safeReadFile(Path.join(folder, filename));
360-
if (buffer === undefined) {
361-
debug("Can't read file", filename);
360+
try {
361+
const folder =
362+
device.deviceDescriptor.idProduct === USB_PRODUCT_ID_BCM2711_BOOT
363+
? 'cm4'
364+
: 'raspberrypi';
365+
366+
const buffer = await readFile(
367+
Path.join(__dirname, '..', 'blobs', folder, filename),
368+
);
369+
370+
if (buffer === undefined) {
371+
debug("Can't read file", filename);
372+
}
373+
374+
return buffer;
375+
} catch (e) {
376+
// no data
362377
}
363-
return buffer;
364378
};
365379

366380
/**
@@ -468,8 +482,12 @@ export class UsbbootScanner extends EventEmitter {
468482
// So we keep track of attached devices ids in attachedDeviceIds to not run it twice.
469483
private attachedDeviceIds = new Set<string>();
470484

471-
constructor() {
485+
private extraFolder: string | undefined;
486+
487+
constructor(extraFolder?: string) {
472488
super();
489+
this.extraFolder = extraFolder;
490+
debug(`Extra folder: ${extraFolder}`);
473491
this.boundAttachDevice = this.attachDevice.bind(this);
474492
this.boundDetachDevice = this.detachDevice.bind(this);
475493
}
@@ -478,6 +496,7 @@ export class UsbbootScanner extends EventEmitter {
478496
debug('Waiting for BCM2835/6/7/2711');
479497

480498
// Prepare already connected devices
499+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
481500
usb.getDeviceList().map(this.boundAttachDevice);
482501

483502
// At this point all devices from `usg.getDeviceList()` above
@@ -488,8 +507,7 @@ export class UsbbootScanner extends EventEmitter {
488507
// Watch for devices detaching
489508
usb.on('detach', this.boundDetachDevice);
490509

491-
// ts-ignore because of a confusion between NodeJS.Timer and number
492-
// @ts-ignore
510+
// @ts-expect-error because of a confusion between NodeJS.Timer and number
493511
this.interval = setInterval(() => {
494512
// usb.getDeviceList().forEach(this.boundAttachDevice);
495513
}, POLLING_INTERVAL_MS);
@@ -548,7 +566,7 @@ export class UsbbootScanner extends EventEmitter {
548566

549567
const usbbootDevice = this.get(device);
550568
let forceSecondstage = false;
551-
if (device.deviceDescriptor.iSerialNumber == usbbootDevice?.last_serial) {
569+
if (device.deviceDescriptor.iSerialNumber === usbbootDevice?.last_serial) {
552570
if (usbbootDevice.step > 0) {
553571
forceSecondstage = true;
554572
}
@@ -565,23 +583,29 @@ export class UsbbootScanner extends EventEmitter {
565583
if (!isUsbBootCapableUSBDevice$(device)) {
566584
return;
567585
}
568-
debug('Found serial number', device.deviceDescriptor.iSerialNumber, `${forceSecondstage ? " => Forced second stage" : ""}`);
586+
debug(
587+
'Found serial number',
588+
device.deviceDescriptor.iSerialNumber,
589+
`${forceSecondstage ? ' => Forced second stage' : ''}`,
590+
);
569591
debug('port id', devicePortId(device));
570592
try {
571593
const { endpoint } = initializeDevice(device);
572594
// cm: 0; cm4: 3
573595
if (
574596
(device.deviceDescriptor.iSerialNumber === 0 ||
575-
device.deviceDescriptor.iSerialNumber === 3) && !forceSecondstage
597+
device.deviceDescriptor.iSerialNumber === 3) &&
598+
!forceSecondstage
576599
) {
577600
debug('Sending bootcode.bin', devicePortId(device));
578601
this.step(device, 0);
579602
await secondStageBoot(device, endpoint);
580603
// The device will now detach and reattach with iSerialNumber 1.
581604
// This takes approximately 1.5 seconds
582605
} else {
606+
const extraFolder = this.extraFolder;
583607
debug('Second stage boot server', devicePortId(device));
584-
await this.fileServer(device, endpoint, 2);
608+
await this.fileServer(device, endpoint, 2, extraFolder);
585609
}
586610
device.close();
587611
} catch (error) {
@@ -623,7 +647,9 @@ export class UsbbootScanner extends EventEmitter {
623647
device: usb.Device,
624648
endpoint: OutEndpoint,
625649
step: number,
650+
extraFolder?: string,
626651
) {
652+
// eslint-disable-next-line no-constant-condition
627653
while (true) {
628654
let data;
629655
try {
@@ -652,7 +678,11 @@ export class UsbbootScanner extends EventEmitter {
652678
message.command === FileMessageCommand.GetFileSize ||
653679
message.command === FileMessageCommand.ReadFile
654680
) {
655-
const buffer = await getFileBuffer(device, message.filename);
681+
const buffer = await getFileBuffer(
682+
device,
683+
message.filename,
684+
extraFolder,
685+
);
656686
if (buffer === undefined) {
657687
debug(`Couldn't find ${message.filename}`, devicePortId(device));
658688
await sendSize(device, 0);

lib/main.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1+
import { argv } from 'process';
2+
import * as fs from 'fs';
13
import type { UsbbootDevice } from './';
24
import { UsbbootScanner } from './';
35

6+
const usbBootExtraFolder = argv.length > 2 ? isValidPath(argv[2]) : undefined;
7+
8+
function isValidPath(path: string) {
9+
if (fs.existsSync(path)) {
10+
return path;
11+
} else {
12+
throw new Error(
13+
'Invalid path provided as argument. Please provide a valid path if you want to use alternative boot assets.',
14+
);
15+
}
16+
}
17+
418
const main = () => {
5-
const scanner = new UsbbootScanner();
19+
const scanner = new UsbbootScanner(usbBootExtraFolder);
620
scanner.on('attach', (usbbootDevice: UsbbootDevice) => {
721
console.log('device attached', usbbootDevice.portId);
822
usbbootDevice.on('progress', (progress: number) => {

0 commit comments

Comments
 (0)