mirror of
https://github.com/shenmintao/aic8800d80.git
synced 2026-11-10 16:49:17 +00:00
fix: resolve chip firmware paths through the firmware API
Follow up issue #117: use each module chip enum and the real Wi-Fi device, preserve flat-layout fallback on missing files, and handle allocation and size failures. Based on the report by ggorbi99-dev.
This commit is contained in:
1 parent
5a63ce8fe0
commit
a482c871d5
4 files changed
+329
-20
No files matched your search
@@ -0,0 +1,31 @@
|
||||
# Firmware loading regression checks
|
||||
|
||||
Run `python3 tests/firmware/test_request_firmware.py` with a C compiler available.
|
||||
The fixture extracts both modules' real request and load functions and the
|
||||
Wi-Fi common caller. It checks all USB chip directory mappings, device context,
|
||||
flat-layout fallback only on `-ENOENT`, missing device data, non-USB/platform
|
||||
behavior, path overflow, invalid file sizes, and allocation failure. Kernel
|
||||
services are mocked; this does not exercise firmware startup or hardware.
|
||||
|
||||
`python3 tests/firmware/test_firmware.py --modules BUILD_DIR` also checks the
|
||||
installed inventory against the built modules' firmware declarations and the
|
||||
loader's probe error paths.
|
||||
|
||||
For a kernel build with the firmware API enabled:
|
||||
|
||||
```sh
|
||||
bash tests/kernel-build/build.sh /path/to/kernel/build CC=gcc-14 CONFIG_USE_FW_REQUEST=y
|
||||
```
|
||||
|
||||
The build default remains `CONFIG_USE_FW_REQUEST=n`. The `y` path searches the
|
||||
kernel firmware locations using chip-relative names such as
|
||||
`aic8800D80/fmacfw_8800d80_u02.bin`, then tries the bare name if the chip-relative
|
||||
file is absent. The two modules use different chip ID enums; their mappings
|
||||
must remain separate. Both retain the real USB device as the request context.
|
||||
Custom absolute `aic_fw_path` directories belong to the existing `n` path;
|
||||
the `y` path uses the kernel firmware search configuration instead.
|
||||
|
||||
Related: [issue #117](https://github.com/shenmintao/aic8800d80/issues/117).
|
||||
The path correction follows ggorbi99-dev's report, with the operational-driver
|
||||
IDs and its previously null device argument corrected as well. It complements
|
||||
the initramfs declarations; it does not establish the reporter's cold-boot fix.
|
||||
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Exercise the real firmware request/load functions with mocked kernel services."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
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]
|
||||
|
||||
|
||||
PRELUDE = r'''
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define CONFIG_USE_FW_REQUEST 1
|
||||
#define printk(...) ((void)0)
|
||||
#define AICWFDBG(...) ((void)0)
|
||||
typedef uint32_t u32;
|
||||
typedef int MD5_CTX;
|
||||
static void MD5Init(MD5_CTX *m) {}
|
||||
static void MD5Update(MD5_CTX *m, unsigned char *b, int size) {}
|
||||
static void MD5Final(MD5_CTX *m, unsigned char *b) {}
|
||||
struct firmware { size_t size; const unsigned char *data; };
|
||||
struct aic_usb_dev { int chipid; };
|
||||
struct aicwf_bus { struct { struct aic_usb_dev *usb; } bus_priv; };
|
||||
struct device { void *data; };
|
||||
struct rwnx_hw { struct device *dev; };
|
||||
static struct aic_usb_dev usb;
|
||||
static struct aicwf_bus bus = { .bus_priv.usb = &usb };
|
||||
static struct device dev = { .data = &bus };
|
||||
static struct device *expected_device;
|
||||
static struct rwnx_hw hw;
|
||||
static const unsigned char bytes[] = { 1, 3, 5, 7, 9 };
|
||||
static struct firmware fw = { sizeof(bytes), bytes };
|
||||
static const char *names[2];
|
||||
static int errors[2], requests, releases, allocation_fail;
|
||||
static void *dev_get_drvdata(struct device *d) { return d->data; }
|
||||
static int request_firmware(const struct firmware **out, const char *name,
|
||||
struct device *d) {
|
||||
assert(requests < 2 && names[requests]);
|
||||
assert(!strcmp(name, names[requests]));
|
||||
assert(d == expected_device);
|
||||
int err = errors[requests++];
|
||||
*out = err ? NULL : &fw;
|
||||
return err;
|
||||
}
|
||||
static void release_firmware(const struct firmware *f) {
|
||||
if (f) { assert(f == &fw); releases++; }
|
||||
}
|
||||
static void *vmalloc(size_t size) { return allocation_fail ? NULL : malloc(size); }
|
||||
static void setup(int chip, const char *name) {
|
||||
usb.chipid = chip; bus.bus_priv.usb = &usb; dev.data = &bus;
|
||||
hw.dev = expected_device = &dev;
|
||||
names[0] = name; names[1] = NULL;
|
||||
errors[0] = errors[1] = requests = releases = allocation_fail = 0;
|
||||
fw.size = sizeof(bytes);
|
||||
}
|
||||
'''
|
||||
|
||||
CHECKS = r'''
|
||||
static void success(void) {
|
||||
u32 *out = (void *)1;
|
||||
assert(load(&out, "fw.bin") == sizeof(bytes));
|
||||
assert(out && !memcmp(out, bytes, sizeof(bytes)));
|
||||
assert(releases == 1);
|
||||
free(out);
|
||||
}
|
||||
int main(void) {
|
||||
for (unsigned int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "%s/fw.bin", cases[i].dir);
|
||||
#if defined(CONFIG_PLATFORM_UBUNTU) && defined(AICWF_USB_SUPPORT)
|
||||
setup(cases[i].chip, path);
|
||||
#else
|
||||
setup(cases[i].chip, "fw.bin");
|
||||
#endif
|
||||
success(); assert(requests == 1);
|
||||
}
|
||||
setup(-1, "fw.bin"); success();
|
||||
setup(cases[0].chip, "fw.bin"); hw.dev = expected_device = NULL; success();
|
||||
setup(cases[0].chip, "fw.bin"); dev.data = NULL; success();
|
||||
setup(cases[0].chip, "fw.bin"); bus.bus_priv.usb = NULL; success();
|
||||
#if defined(CONFIG_PLATFORM_UBUNTU) && defined(AICWF_USB_SUPPORT)
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "%s/fw.bin", cases[0].dir);
|
||||
setup(cases[0].chip, path);
|
||||
errors[0] = -ENOENT; names[1] = "fw.bin";
|
||||
success(); assert(requests == 2);
|
||||
setup(cases[0].chip, path);
|
||||
errors[0] = -ENOENT; names[1] = "fw.bin"; errors[1] = -ENOENT;
|
||||
u32 *out = (void *)1;
|
||||
assert(load(&out, "fw.bin") == -ENOENT && !out && requests == 2);
|
||||
setup(cases[0].chip, path); errors[0] = -EACCES; out = (void *)1;
|
||||
assert(load(&out, "fw.bin") == -EACCES && !out && requests == 1);
|
||||
setup(cases[0].chip, path); errors[0] = -ENOMEM; out = (void *)1;
|
||||
assert(load(&out, "fw.bin") == -ENOMEM && !out && requests == 1);
|
||||
char long_name[300]; memset(long_name, 'a', sizeof(long_name));
|
||||
long_name[sizeof(long_name) - 1] = 0;
|
||||
setup(cases[0].chip, NULL); out = (void *)1;
|
||||
assert(load(&out, long_name) == -ENAMETOOLONG && !out && !requests);
|
||||
#endif
|
||||
u32 *buffer = (void *)1;
|
||||
setup(-1, "fw.bin"); allocation_fail = 1;
|
||||
assert(load(&buffer, "fw.bin") == -ENOMEM && !buffer && releases == 1);
|
||||
setup(-1, "fw.bin"); fw.size = 0; buffer = (void *)1;
|
||||
assert(load(&buffer, "fw.bin") == -EINVAL && !buffer && releases == 1);
|
||||
setup(-1, "fw.bin"); fw.size = (size_t)INT_MAX + 1; buffer = (void *)1;
|
||||
assert(load(&buffer, "fw.bin") == -EINVAL && !buffer && releases == 1);
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class RequestFirmwareTests(unittest.TestCase):
|
||||
def test_paths_device_context_and_failures(self):
|
||||
variants = {
|
||||
"aic_load_fw": ("aicbluetooth.c", "aic_load_firmware", {
|
||||
"aic8800": ["AIC8800", "AIC8801"],
|
||||
"aic8800DC": ["AIC8800DC", "AIC8800DW"],
|
||||
"aic8800D80": ["AIC8800D80", "AIC8800D81"],
|
||||
"aic8800D80X2": ["AIC8800D80X2", "AIC8800D81X2",
|
||||
"AIC8800D89X2", "AIC8800D40X2"],
|
||||
}),
|
||||
"aic8800_fdrv": ("rwnx_platform.c", "rwnx_load_firmware", {
|
||||
"aic8800": ["AIC8801"],
|
||||
"aic8800DC": ["AIC8800DC", "AIC8800DW"],
|
||||
"aic8800D80": ["AIC8800D81"],
|
||||
"aic8800D80X2": ["AIC8800D81X2", "AIC8800D89X2"],
|
||||
"aic8800D80N": ["AIC8800D80N"],
|
||||
"aic8800DLN": ["AIC8800DLN"],
|
||||
}),
|
||||
}
|
||||
for module, (filename, loader, mapping) in variants.items():
|
||||
folder = ROOT / "drivers/aic8800" / module
|
||||
source = (folder / filename).read_text()
|
||||
enums = re.search(r"enum AICWF_IC\s*\{.*?\};",
|
||||
(folder / "aicwf_usb.h").read_text(), re.S).group()
|
||||
code = PRELUDE + enums + function(source, "static int aic_request_firmware(")
|
||||
code += function(source, "static int " + loader + "(")
|
||||
if module == "aic8800_fdrv":
|
||||
code += function(source, "int rwnx_request_firmware_common(")
|
||||
call = "rwnx_request_firmware_common(&hw, out, name)"
|
||||
else:
|
||||
call = "aic_load_firmware(out, name, hw.dev)"
|
||||
code += "\nstatic int load(u32 **out, const char *name) { return " + call + "; }\n"
|
||||
code += "static const struct { int chip; const char *dir; } cases[] = {\n"
|
||||
for directory, chips in mapping.items():
|
||||
for chip in chips:
|
||||
code += '{PRODUCT_ID_' + chip + ', "' + directory + '"},\n'
|
||||
code += "};\n" + CHECKS
|
||||
for defines in ([], ["CONFIG_PLATFORM_UBUNTU", "AICWF_USB_SUPPORT"],
|
||||
["CONFIG_PLATFORM_UBUNTU"]):
|
||||
with self.subTest(module=module, defines=defines):
|
||||
with tempfile.TemporaryDirectory(prefix="aic-fw-request-") as temp:
|
||||
c_file, binary = Path(temp) / "test.c", Path(temp) / "test"
|
||||
c_file.write_text(code)
|
||||
subprocess.run([os.environ.get("CC", "cc"), "-std=gnu11",
|
||||
*["-D" + d for d in defines], "-o", str(binary),
|
||||
str(c_file)], check=True)
|
||||
subprocess.run([str(binary)], check=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user