Merge pull request #121 from shenmintao/fix/issue117-firmware-legacy

fix(legacy-mcu1): include matched firmware in initramfs and return probe errors
This commit is contained in:
MinQ authored and GitHub committed 2026-10-28 20:02:38 +08:00
commit 0ec30e1b9b
7 files changed
+245 -4

No files matched your search

+9
View File
@@ -35,6 +35,15 @@ cannot download dependencies because the database is stale, run
`sudo pacman -Syu` yourself, reboot if the kernel was updated, and rerun the
installer with headers matching the running kernel.
The USB modules declare the complete firmware set shipped in `fw/`, including
configuration files and extension patches, so initramfs tools can include those
files when they include the modules. Rerun the installer after updating the
driver to rebuild DKMS modules and refresh the current kernel's initramfs.
If the adapter only works after reloading its modules after boot, check that
the initramfs contains its `aic8800*/` firmware directory (issue #117).
Custom firmware paths supplied with `aic_fw_path` still need to be included
in the initramfs by the administrator.
> For more information, please refer to the [README.md](README.md) file.
<br>
+4
View File
@@ -9725,7 +9725,11 @@ MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver");
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
#endif
#ifdef AICWF_USB_SUPPORT
#include "../aicwf_firmware.h"
#else
MODULE_FIRMWARE(RWNX_CONFIG_FW_NAME);
#endif
MODULE_DESCRIPTION(RW_DRV_DESCRIPTION);
MODULE_VERSION(RWNX_VERS_MOD);
@@ -3972,6 +3972,7 @@ struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat)
}
#ifndef AICWF_USB_SUPPORT
#ifndef CONFIG_RWNX_SDM
MODULE_FIRMWARE(RWNX_AGC_FW_NAME);
MODULE_FIRMWARE(RWNX_FCU_FW_NAME);
@@ -3981,5 +3982,6 @@ MODULE_FIRMWARE(RWNX_MAC_FW_NAME);
#ifndef CONFIG_RWNX_TL4
MODULE_FIRMWARE(RWNX_MAC_FW_NAME2);
#endif
#endif /* !AICWF_USB_SUPPORT */
@@ -9,7 +9,6 @@
#include "aicwf_txq_prealloc.h"
#define DRV_CONFIG_FW_NAME "fw.bin"
#define DRV_DESCRIPTION "AIC BLUETOOTH"
#define DRV_COPYRIGHT "Copyright(c) 2015-2020 AICSemi"
#define DRV_AUTHOR "AICSemi"
@@ -75,7 +74,7 @@ MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver");
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
#endif
MODULE_FIRMWARE(DRV_CONFIG_FW_NAME);
#include "../aicwf_firmware.h"
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERS_MOD);
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+8 -2
View File
@@ -1773,11 +1773,17 @@ static int aicwf_usb_probe(struct usb_interface *intf, const struct usb_device_i
goto out_free_bus;
}
if (system_config(usb_dev)) {
ret = system_config(usb_dev);
if (ret) {
if (ret > 0)
ret = -EIO;
goto out_free_bus;
}
if (aicfw_download_fw(usb_dev)){
ret = aicfw_download_fw(usb_dev);
if (ret) {
if (ret > 0)
ret = -EIO;
goto out_free_bus;
}
+95
View File
@@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Files installed below /lib/firmware by install.sh.
*
* Both USB modules may be loaded in an initramfs. Declare the complete matched
* firmware set, including configuration and extension patches, so initramfs
* generators can copy it along with either module. Keep this list in sync with
* fw/; tests/firmware/test_firmware.py checks the installed paths.
*/
#ifndef AICWF_FIRMWARE_H
#define AICWF_FIRMWARE_H
MODULE_FIRMWARE("aic8800/aic_userconfig.txt");
MODULE_FIRMWARE("aic8800/fmacfw.bin");
MODULE_FIRMWARE("aic8800/fmacfw_m2d.bin");
MODULE_FIRMWARE("aic8800/fmacfw_no_msg_ep.bin");
MODULE_FIRMWARE("aic8800/fmacfw_no_msg_ep_rf.bin");
MODULE_FIRMWARE("aic8800/fmacfw_rf.bin");
MODULE_FIRMWARE("aic8800/fw_adid.bin");
MODULE_FIRMWARE("aic8800/fw_adid_rf.bin");
MODULE_FIRMWARE("aic8800/fw_adid_u03.bin");
MODULE_FIRMWARE("aic8800/fw_ble_scan.bin");
MODULE_FIRMWARE("aic8800/fw_ble_scan_ad_filter_dcdc.bin");
MODULE_FIRMWARE("aic8800/fw_ble_scan_ad_filter_ldo.bin");
MODULE_FIRMWARE("aic8800/fw_patch.bin");
MODULE_FIRMWARE("aic8800/fw_patch_rf.bin");
MODULE_FIRMWARE("aic8800/fw_patch_table.bin");
MODULE_FIRMWARE("aic8800/fw_patch_table_u03.bin");
MODULE_FIRMWARE("aic8800/fw_patch_u03.bin");
MODULE_FIRMWARE("aic8800/m2d_ota.bin");
MODULE_FIRMWARE("aic8800D80/aic_userconfig_8800d80.txt");
MODULE_FIRMWARE("aic8800D80/calibmode_8800d80.bin");
MODULE_FIRMWARE("aic8800D80/fmacfw_8800d80_u02.bin");
MODULE_FIRMWARE("aic8800D80/fmacfw_8800d80_u02_ipc.bin");
MODULE_FIRMWARE("aic8800D80/fw_adid_8800d80_u02.bin");
MODULE_FIRMWARE("aic8800D80/fw_ble_scan_ad_filter.bin");
MODULE_FIRMWARE("aic8800D80/fw_patch_8800d80_u02.bin");
MODULE_FIRMWARE("aic8800D80/fw_patch_table_8800d80_u02.bin");
MODULE_FIRMWARE("aic8800D80/lmacfw_rf_8800d80_u02.bin");
MODULE_FIRMWARE("aic8800D80N/aic_powerlimit_8800d80n.txt");
MODULE_FIRMWARE("aic8800D80N/aic_userconfig_8800d80n.txt");
MODULE_FIRMWARE("aic8800D80N/fmacfw_btusb_8800d80n_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fmacfw_calib_8800d80n_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fmacfw_cinit_8800d80n_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fmacfw_patch_8800d80n_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fmacfw_patch_tbl_8800d80n_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fw_patch_8800d80n_rf_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fw_patch_8800d80n_rf_u02_ext0.bin");
MODULE_FIRMWARE("aic8800D80N/fw_patch_8800d80n_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fw_patch_8800d80n_u02_ext0.bin");
MODULE_FIRMWARE("aic8800D80N/fw_patch_table_8800d80n_rf_u02.bin");
MODULE_FIRMWARE("aic8800D80N/fw_patch_table_8800d80n_u02.bin");
MODULE_FIRMWARE("aic8800D80N/lmacfw_rf_8800d80n.bin");
MODULE_FIRMWARE("aic8800D80X2/aic_powerlimit_8800d80x2.txt");
MODULE_FIRMWARE("aic8800D80X2/aic_userconfig_8800d80x2.txt");
MODULE_FIRMWARE("aic8800D80X2/aicbt.conf");
MODULE_FIRMWARE("aic8800D80X2/fmacfw_8800d80x2.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_adid_8800d80x2_u03.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_adid_8800d80x2_u05.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_patch_8800d80x2_u03.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_patch_8800d80x2_u05.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_patch_8800d80x2_u05_ext0.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_patch_8800d80x2_u05_ext1.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_patch_table_8800d80x2_u03.bin");
MODULE_FIRMWARE("aic8800D80X2/fw_patch_table_8800d80x2_u05.bin");
MODULE_FIRMWARE("aic8800D80X2/lmacfw_rf_8800d80x2.bin");
MODULE_FIRMWARE("aic8800DC/aic_powerlimit_8800dc.txt");
MODULE_FIRMWARE("aic8800DC/aic_powerlimit_8800dw.txt");
MODULE_FIRMWARE("aic8800DC/aic_userconfig_8800dc.txt");
MODULE_FIRMWARE("aic8800DC/aic_userconfig_8800dw.txt");
MODULE_FIRMWARE("aic8800DC/aic_userconfig_8800dw_u2.txt");
MODULE_FIRMWARE("aic8800DC/aic_userconfig_8800dw_w311.txt");
MODULE_FIRMWARE("aic8800DC/fmacfw_calib_8800dc_h_u02.bin");
MODULE_FIRMWARE("aic8800DC/fmacfw_calib_8800dc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fmacfw_patch_8800dc_h_u02.bin");
MODULE_FIRMWARE("aic8800DC/fmacfw_patch_8800dc_ipc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fmacfw_patch_8800dc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fmacfw_patch_tbl_8800dc_h_u02.bin");
MODULE_FIRMWARE("aic8800DC/fmacfw_patch_tbl_8800dc_ipc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fmacfw_patch_tbl_8800dc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fw_adid_8800dc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fw_adid_8800dc_u02h.bin");
MODULE_FIRMWARE("aic8800DC/fw_patch_8800dc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fw_patch_8800dc_u02_ext0.bin");
MODULE_FIRMWARE("aic8800DC/fw_patch_8800dc_u02h.bin");
MODULE_FIRMWARE("aic8800DC/fw_patch_table_8800dc_u02.bin");
MODULE_FIRMWARE("aic8800DC/fw_patch_table_8800dc_u02h.bin");
MODULE_FIRMWARE("aic8800DC/lmacfw_rf_8800dc.bin");
MODULE_FIRMWARE("aic8800DLN/aic_powerlimit_8800dln.txt");
MODULE_FIRMWARE("aic8800DLN/aic_userconfig_8800dln.txt");
MODULE_FIRMWARE("aic8800DLN/fmacfw_gaintbl_8800dln.bin");
MODULE_FIRMWARE("aic8800DLN/fmacfw_initvar_8800dln.bin");
#endif /* AICWF_FIRMWARE_H */
+126
View File
@@ -0,0 +1,126 @@
#!/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)