Merge pull request #2244 from balena-os/duplicate-overlays

Add support for repeated overlays
This commit is contained in:
flowzone-app[bot] 2024-02-27 19:29:23 +00:00 committed by GitHub
commit d75776131d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 32 deletions

View File

@ -26,9 +26,9 @@ function isArrayConfig(x: string): x is ArrayConfig {
return x != null && ARRAY_CONFIGS.includes(x as any);
}
// The DTOverlays type is a collection of DtParams
type DTParam = string;
type DTOverlays = { [name: string]: DTParam[] };
// We use the empty string as the identifier of the base
// overlay to allow handling the case where the user defines an
// empty overlay as `dtoverlay=`
const BASE_OVERLAY = '';
function isBaseParam(dtparam: string): boolean {
@ -119,8 +119,10 @@ export class ConfigTxt extends ConfigBackend {
const conf: ConfigTxtOptions = {};
const configStatements = configContents.split(/\r?\n/);
let currOverlay = BASE_OVERLAY;
const dtOverlays: DTOverlays = { [BASE_OVERLAY]: [] };
const baseParams: string[] = [];
const overlayQueue: Array<[string, string[]]> = [
[BASE_OVERLAY, baseParams],
];
for (const configStr of configStatements) {
// Don't show warnings for comments and empty lines
@ -140,6 +142,7 @@ export class ConfigTxt extends ConfigBackend {
} else {
// dtparams and dtoverlays need to be treated as a special case
if (key === 'dtparam') {
const [, currParams] = overlayQueue[overlayQueue.length - 1];
// The specification allows multiple params in a line
const params = value.split(',');
params.forEach((param) => {
@ -147,9 +150,9 @@ export class ConfigTxt extends ConfigBackend {
// We make sure to put the base param in the right overlays
// since RPI doesn't seem to be too strict about the ordering
// when it comes to these base params
dtOverlays[BASE_OVERLAY].push(value);
baseParams.push(value);
} else {
dtOverlays[currOverlay].push(value);
currParams.push(value);
}
});
} else if (key === 'dtoverlay') {
@ -157,21 +160,15 @@ export class ConfigTxt extends ConfigBackend {
// we don't validate that the value is well formed
const [overlay, ...params] = value.split(',');
// Update the DTO for next dtparam
currOverlay = overlay;
if (dtOverlays[overlay] == null) {
dtOverlays[overlay] = [];
}
// Add params to the list
dtOverlays[overlay].push(...params);
// A new dtoverlay statement means we add a new entry to the
// queue with the given params list
overlayQueue.push([overlay, params]);
} else {
// Otherwise push the new value to the array
const arrayConf = conf[key] == null ? [] : conf[key]!;
arrayConf.push(value);
if (conf[key] == null) {
conf[key] = arrayConf;
conf[key] = [];
}
conf[key]!.push(value);
}
}
continue;
@ -188,19 +185,16 @@ export class ConfigTxt extends ConfigBackend {
}
}
// Convert the base overlay to global dtparams
const baseOverlay = dtOverlays[BASE_OVERLAY];
delete dtOverlays[BASE_OVERLAY];
if (baseOverlay.length > 0) {
conf.dtparam = baseOverlay;
}
// Convert dtoverlays to array format
const overlayEntries = Object.entries(dtOverlays);
if (overlayEntries.length > 0) {
conf.dtoverlay = overlayEntries.map(([overlay, params]) =>
[overlay, ...params].join(','),
);
for (const [overlay, params] of overlayQueue) {
// Convert the base overlay to global dtparams
if (overlay === BASE_OVERLAY && params.length > 0) {
conf.dtparam = conf.dtparam != null ? conf.dtparam : [];
conf.dtparam.push(...params);
} else if (overlay !== BASE_OVERLAY) {
// Convert dtoverlays to array format
conf.dtoverlay = conf.dtoverlay != null ? conf.dtoverlay : [];
conf.dtoverlay.push([overlay, ...params].join(','));
}
}
return conf;

View File

@ -45,6 +45,52 @@ describe('config/config-txt', () => {
await tfs.restore();
});
it('correctly parses a config.txt file with repeated overlays', async () => {
const tfs = await testfs({
[hostUtils.pathOnBoot('config.txt')]: stripIndent`
gpu_mem=64
avoid_warnings=1
dtoverlay=vc4-kms-v3d
dtoverlay=ads1015
dtparam=addr=0x48
dtparam=cha_cfg=4
dtparam=chb_cfg=5
dtparam=chc_cfg=6
dtparam=chd_cfg=7
dtoverlay=ads1015
dtparam=addr=0x49
dtparam=chc_enable=false
dtparam=chd_enable=false
dtparam=cha_cfg=0
dtparam=chb_cfg=3
dtparam=i2c_arm=on
dtparam=spi=on
dtparam=audio=on
enable_uart=0
gpio=8=pd
gpio=17=op,dh
`,
}).enable();
const configTxt = new ConfigTxt();
// Will try to parse /test/data/mnt/boot/config.txt
await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
dtparam: ['i2c_arm=on', 'spi=on', 'audio=on'],
dtoverlay: [
'vc4-kms-v3d',
'ads1015,addr=0x48,cha_cfg=4,chb_cfg=5,chc_cfg=6,chd_cfg=7',
'ads1015,addr=0x49,chc_enable=false,chd_enable=false,cha_cfg=0,chb_cfg=3',
],
enable_uart: '0',
avoid_warnings: '1',
gpio: ['8=pd', '17=op,dh'],
gpu_mem: '64',
});
await tfs.restore();
});
it('correctly parses a config.txt file with an empty overlay', async () => {
const tfs = await testfs({
[hostUtils.pathOnBoot('config.txt')]: stripIndent`
@ -55,6 +101,7 @@ describe('config/config-txt', () => {
avoid_warnings=1
dtoverlay=
dtparam=i2c=on
dtparam=lala=on
dtparam=audio=on
dtoverlay=ads7846
gpu_mem=16
@ -66,7 +113,7 @@ describe('config/config-txt', () => {
// Will try to parse /test/data/mnt/boot/config.txt
await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
dtparam: ['i2c=on', 'audio=on'],
dtparam: ['i2c=on', 'audio=on', 'lala=on'],
dtoverlay: [
'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13,gpio_out_pin=17',
'ads7846',