Files
aic8800d80/tests/firmware/test_firmware.py
T
Shen Mintao 28e84ec90e fix(legacy-mcu1): declare matched initramfs firmware and return probe errors
Backport the issue #117 loader fix, declaring the 80 firmware/configuration files actually shipped by legacy-mcu1 rather than the main firmware set. Preserve the existing V3 binaries and loader profiles.

All three modules build against Linux 6.17 headers; inventory, built-module metadata and extracted probe error/re-enumeration regression checks pass. Hardware boot validation remains pending.
2026-10-28 10:22:28 +08:00

127 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Check initramfs firmware inventory and extracted probe error handling.
Run with Python 3 and a C compiler. Pass --modules BUILD_DIR to also check the
firmware metadata in built modules using modinfo. No modules are installed.
The probe test mocks USB and allocation, not kernel scheduling or hardware.
"""
from pathlib import Path
import argparse
import os
import re
import subprocess
import tempfile
import unittest
parser = argparse.ArgumentParser()
parser.add_argument("--modules", type=Path)
args, remaining = parser.parse_known_args()
ROOT = Path(__file__).resolve().parents[2]
def function(source, signature):
start = source.index(signature)
brace = source.index("{", start)
end, depth = brace + 1, 1
while depth:
depth += (source[end] == "{") - (source[end] == "}")
end += 1
return source[start:end]
class FirmwareTests(unittest.TestCase):
def test_declared_files_match_installed_inventory(self):
expected = {p.relative_to(ROOT / "fw").as_posix()
for p in (ROOT / "fw").rglob("*") if p.is_file()}
header = (ROOT / "drivers/aic8800/aicwf_firmware.h").read_text()
declared = re.findall(r'MODULE_FIRMWARE\("([^"]+)"\)', header)
self.assertEqual(len(declared), len(set(declared)))
self.assertEqual(set(declared), expected)
@unittest.skipUnless(args.modules, "pass --modules to inspect built modules")
def test_built_modules_declare_installed_files(self):
expected = {p.relative_to(ROOT / "fw").as_posix()
for p in (ROOT / "fw").rglob("*") if p.is_file()}
for name in ("aic_load_fw", "aic8800_fdrv"):
with self.subTest(module=name):
module = args.modules / name / (name + ".ko")
declared = subprocess.check_output(
["modinfo", "-F", "firmware", str(module)], text=True).splitlines()
self.assertEqual(set(declared), expected)
def test_probe_errors_and_intentional_reenumeration(self):
source = (ROOT / "drivers/aic8800/aic_load_fw/aicwf_usb.c").read_text()
probe = function(source, "static int aicwf_usb_probe(")
# The real post-allocation probe and cleanup, with allocations supplied
# by the fixture, exercises the actual failure labels and return value.
body = probe[probe.index("ret = aicwf_bus_start(bus_if);"):probe.rfind("}")]
prelude = r'''
#include <assert.h>
#include <stdbool.h>
#include <errno.h>
#include <stddef.h>
#define USB_SPEED_HIGH 2
#define USB_SPEED_SUPER 3
#define PRODUCT_ID_AIC8801 1
#define PRODUCT_ID_AIC8800D81 2
#define PRODUCT_ID_AIC8800D81X2 3
#define PRODUCT_ID_AIC8800D89X2 4
#define usb_err(...) ((void)0)
#define printk(...) ((void)0)
#define kfree(...) ((void)0)
struct usb_device { int speed; };
struct aic_usb_dev { int chipid; bool app_cmp; void *rx_priv; };
static int config_result, download_result, cleanup, downloads, reboots, fw_loaded;
static struct aic_usb_dev device;
static struct usb_device usb_device;
static int aicwf_bus_start(void *bus) { return 0; }
static void aic_bt_platform_init(struct aic_usb_dev *dev) {}
static void system_reboot(struct aic_usb_dev *dev) { reboots++; }
static void rwnx_send_reboot(struct aic_usb_dev *dev) { reboots++; }
static int system_config(struct aic_usb_dev *dev) { return config_result; }
static int aicfw_download_fw(struct aic_usb_dev *dev) { downloads++; return download_result; }
static void aicwf_bus_deinit(void *dev) { cleanup++; }
static void aicwf_rx_deinit(void *dev) {}
static void aicwf_usb_deinit(void *dev) {}
static void usb_set_intfdata(void *intf, void *data) {}
static int probe(void) {
int ret = 0;
struct aic_usb_dev *usb_dev = &device;
struct usb_device *usb = &usb_device;
void *bus_if = NULL, *dev = NULL, *intf = NULL;
'''
checks = r'''
}
static void setup(void) {
config_result = download_result = cleanup = downloads = reboots = fw_loaded = 0;
device.chipid = 0; device.app_cmp = false; usb_device.speed = USB_SPEED_HIGH;
}
int main(void) {
setup(); assert(probe() == 0 && device.app_cmp && fw_loaded && downloads == 1);
setup(); config_result = -EIO;
assert(probe() == -EIO && cleanup == 1 && downloads == 0 && !fw_loaded);
setup(); config_result = 1;
assert(probe() == -EIO && cleanup == 1 && downloads == 0);
setup(); download_result = -ENOMEM;
assert(probe() == -ENOMEM && cleanup == 1 && !device.app_cmp && !fw_loaded);
setup(); download_result = 1; assert(probe() == -EIO && cleanup == 1);
setup(); device.chipid = PRODUCT_ID_AIC8800D81;
assert(probe() == 0 && reboots == 1 && cleanup == 1 && downloads == 0);
setup(); usb_device.speed = 1;
assert(probe() == 0 && reboots == 1 && cleanup == 1 && downloads == 0);
return 0;
}
'''
with tempfile.TemporaryDirectory(prefix="aic-firmware-test-") as temp:
c_file = Path(temp) / "probe.c"
binary = Path(temp) / "probe"
c_file.write_text(prelude + body + checks)
subprocess.run([os.environ.get("CC", "cc"), "-std=gnu11", "-o",
str(binary), str(c_file)], check=True)
subprocess.run([str(binary)], check=True)
if __name__ == "__main__":
unittest.main(argv=[__file__] + remaining)