mirror of
https://github.com/shenmintao/aic8800d80.git
synced 2026-11-10 16:49:17 +00:00
fix: isolate the 0x8820 loader profile per USB device
Derive the FMAC-only profile from each device readback instead of a module-global latch. A regular D80 must still load its patches after another adapter reports product 0x8820. Reject a busy connect with EBUSY without resetting the state of an operation already in flight. Extracted-function tests cover alternating devices, reprobe, cache setup, configuration errors and unchanged busy states. All three modules build against Linux 6.17 headers. Hardware retest of this revision and Bluetooth remain pending.
This commit is contained in:
1 parent
73e0a57128
commit
35d815c0ae
6 files changed
+159
-10
No files matched your search
@@ -0,0 +1,16 @@
|
||||
# Product 0x8820 loader checks
|
||||
|
||||
Run `python3 tests/issue119-8820/test_loader.py` with a C compiler available.
|
||||
The test extracts the actual system configuration, normal-mode upload branch,
|
||||
and busy-connect guard and runs them against mocked USB requests. It checks
|
||||
that alternating/reprobing M80 and regular D80 devices preserves each loader
|
||||
sequence, retains the MCU1 cache fix, propagates configuration errors, and
|
||||
rejects a new connect without overwriting an operation already in progress.
|
||||
|
||||
These checks do not establish hardware or Bluetooth compatibility. Issue #118
|
||||
reports working Wi-Fi on one UGREEN `a69c:5723`, chip revision 7, MCU1 unit
|
||||
with product `0x8820` and the branch's 327,037-byte V3 FMAC. Bluetooth pairing
|
||||
and audio remain unverified. Product `0x8820` is not a rule to skip patches
|
||||
on every MCU1 device, and the report does not establish that every device in
|
||||
issue #79 works with this firmware.
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Exercise extracted loader paths with mocked USB requests, without hardware."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def block(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]
|
||||
|
||||
|
||||
loader = (ROOT / "drivers/aic8800/aic_load_fw/aic_compat_8800d80.c").read_text()
|
||||
header = (ROOT / "drivers/aic8800/aic_load_fw/aic_compat_8800d80.h").read_text()
|
||||
header = "\n".join(line for line in header.splitlines() if not line.startswith("#include"))
|
||||
config = block(loader, "int system_config_8800d80(")
|
||||
normal = block(loader, "if(testmode == FW_NORMAL_MODE)")
|
||||
connect = (ROOT / "drivers/aic8800/aic8800_fdrv/rwnx_main.c").read_text()
|
||||
guard = block(connect[connect.index("static int rwnx_cfg80211_connect("):],
|
||||
"if((int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING")
|
||||
prelude = r'''
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
typedef uint32_t u32;
|
||||
typedef uint8_t u8;
|
||||
#define printk(...) ((void)0)
|
||||
#define AICWFDBG(...) ((void)0)
|
||||
#define CHIP_REV_U01 1
|
||||
#define CHIP_REV_U02 3
|
||||
#define CHIP_REV_U03 7
|
||||
#define FW_NORMAL_MODE 0
|
||||
#define HOST_START_APP_AUTO 0
|
||||
struct aic_usb_dev {
|
||||
bool d80_m80_variant;
|
||||
u32 id, cache;
|
||||
int syscfg_writes, syscfg_masks, patches, fmacs, starts;
|
||||
};
|
||||
struct dbg_mem_read_cfm { u32 memdata; };
|
||||
struct aicbt_patch_info_t { u32 addr_adid, addr_patch; };
|
||||
static u8 chip_id, chip_mcu_id;
|
||||
static int testmode = FW_NORMAL_MODE, fail_read, fail_write;
|
||||
static u32 syscfg_tbl_8800d80[0][2], syscfg_tbl_masked_8800d80[0][3];
|
||||
static int rwnx_send_dbg_mem_read_req(struct aic_usb_dev *dev, u32 addr,
|
||||
struct dbg_mem_read_cfm *cfm) {
|
||||
if (fail_read) return -EIO;
|
||||
cfm->memdata = addr == 0x40500000 ? dev->id : dev->cache; return 0;
|
||||
}
|
||||
static int rwnx_send_dbg_mem_write_req(struct aic_usb_dev *dev, u32 addr, u32 val) {
|
||||
if (fail_write) return -EIO;
|
||||
if (addr == 0x40100020) dev->cache = val;
|
||||
if (addr == 0x40500010) { assert(val == 2); dev->syscfg_writes++; }
|
||||
return 0;
|
||||
}
|
||||
static int rwnx_send_dbg_mem_mask_write_req(struct aic_usb_dev *dev, u32 addr, u32 mask, u32 val) {
|
||||
assert(addr == 0x40506024 && mask == 0xff && val == 0xdf);
|
||||
dev->syscfg_masks++; return 0;
|
||||
}
|
||||
static int rwnx_plat_bin_fw_upload_android(struct aic_usb_dev *dev, u32 addr, const char *name) {
|
||||
if (name[0] == 'f' && name[1] == 'm') {
|
||||
assert(addr == 0x120000); dev->fmacs++;
|
||||
} else { dev->patches++; }
|
||||
return 0;
|
||||
}
|
||||
static int aicbt_ext_patch_data_load(struct aic_usb_dev *dev, struct aicbt_patch_info_t *info, const char *name) {
|
||||
dev->patches++; return 0;
|
||||
}
|
||||
static int aicbt_patch_table_load(struct aic_usb_dev *dev, void *head) {
|
||||
dev->patches++; return 0;
|
||||
}
|
||||
static int rwnx_send_dbg_start_app_req(struct aic_usb_dev *dev, u32 addr, int mode) {
|
||||
assert(addr == 0x120000); dev->starts++; return 0;
|
||||
}
|
||||
'''
|
||||
normal_wrapper = r'''
|
||||
int aicwf_patch_config_8800d80(struct aic_usb_dev *dev) { dev->patches++; return 0; }
|
||||
static int load_normal(struct aic_usb_dev *usb_dev) {
|
||||
struct aicbt_patch_info_t patch_info = {0};
|
||||
void *head = 0;
|
||||
'''
|
||||
busy_wrapper = r'''
|
||||
return 0;
|
||||
}
|
||||
enum { RWNX_DRV_STATUS_DISCONNECTED, RWNX_DRV_STATUS_CONNECTING,
|
||||
RWNX_DRV_STATUS_DISCONNECTING };
|
||||
struct vif { int drv_conn_state; };
|
||||
#define atomic_read(p) (*(p))
|
||||
static void rwnx_set_conn_state(struct vif *vif, int *state, int val) { *state = val; }
|
||||
static int busy(struct vif *rwnx_vif) {
|
||||
'''
|
||||
checks = r'''
|
||||
return 0;
|
||||
}
|
||||
int main(void) {
|
||||
struct aic_usb_dev m80 = {.id = 0x00078820}, d80 = {.id = 0x00078800};
|
||||
/* Configuring a second adapter must not change the first's load sequence. */
|
||||
assert(system_config_8800d80(&m80) == 0);
|
||||
assert(system_config_8800d80(&d80) == 0);
|
||||
assert(load_normal(&m80) == 0 && m80.patches == 0 && m80.fmacs == 1 && m80.starts == 1);
|
||||
assert(load_normal(&d80) == 0 && d80.patches == 5 && d80.fmacs == 1 && d80.starts == 1);
|
||||
assert(m80.syscfg_writes == 1 && m80.syscfg_masks == 1 && (m80.cache & 1));
|
||||
assert(d80.syscfg_writes == 0 && d80.syscfg_masks == 0 && (d80.cache & 1));
|
||||
/* Reusing a zeroed/reprobed device, and a fresh device after an M80. */
|
||||
m80.id = 0x00078800; m80.patches = m80.fmacs = m80.starts = 0;
|
||||
assert(system_config_8800d80(&m80) == 0);
|
||||
assert(load_normal(&m80) == 0 && m80.patches == 5);
|
||||
d80.id = 0x00078820; d80.patches = d80.fmacs = d80.starts = 0;
|
||||
assert(system_config_8800d80(&d80) == 0);
|
||||
assert(load_normal(&d80) == 0 && d80.patches == 0 && d80.fmacs == 1);
|
||||
fail_read = 1; assert(system_config_8800d80(&d80) == -EIO);
|
||||
fail_read = 0; fail_write = 1; assert(system_config_8800d80(&d80) == -EIO);
|
||||
for (int state = RWNX_DRV_STATUS_CONNECTING; state <= RWNX_DRV_STATUS_DISCONNECTING; state++) {
|
||||
struct vif vif = {.drv_conn_state = state};
|
||||
assert(busy(&vif) == -EBUSY && vif.drv_conn_state == state);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory(prefix="aic-8820-test-") as temp:
|
||||
c_file, binary = Path(temp) / "loader.c", Path(temp) / "loader"
|
||||
c_file.write_text(prelude + header + config + normal_wrapper + normal +
|
||||
busy_wrapper + guard + checks)
|
||||
subprocess.run([os.environ.get("CC", "cc"), "-std=gnu11", "-o", str(binary),
|
||||
str(c_file)], check=True)
|
||||
subprocess.run([str(binary)], check=True)
|
||||
print("PASS: per-device/reprobe loader selection, MCU1 cache fix, write errors, busy connect state")
|
||||
|
||||
Reference in new issue
Block a user