diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_main.c b/drivers/aic8800/aic8800_fdrv/rwnx_main.c index 1548fe4..8b7a984 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_main.c +++ b/drivers/aic8800/aic8800_fdrv/rwnx_main.c @@ -3207,13 +3207,11 @@ static int rwnx_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, (int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTING) { AICWFDBG(LOGERROR, "%s driver is disconnecting or connecting ,return it \r\n", __func__); /* - * local fix: returning 0 here leaves cfg80211 waiting for a connect + * Returning 0 here leaves cfg80211 waiting for a connect * result that never comes, so every later connect fails with -EALREADY * (NetworkManager then loops asking for the password forever). - * Report the failure instead so cfg80211 drops its pending request. + * Reject the new request without changing an in-flight operation's state. */ - rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, - (int)RWNX_DRV_STATUS_DISCONNECTED); return -EBUSY; } #endif diff --git a/drivers/aic8800/aic_load_fw/aic_compat_8800d80.c b/drivers/aic8800/aic_load_fw/aic_compat_8800d80.c index fb94f59..e080032 100644 --- a/drivers/aic8800/aic_load_fw/aic_compat_8800d80.c +++ b/drivers/aic8800/aic_load_fw/aic_compat_8800d80.c @@ -18,7 +18,6 @@ void rwnx_release_firmware_common(u32** buffer); extern int testmode; extern u8 chip_id; extern u8 chip_mcu_id; -extern u8 aic8800d80_m80_variant; typedef u32 (*array2_tbl_t)[2]; @@ -248,6 +247,7 @@ int system_config_8800d80(struct aic_usb_dev *usb_dev){ } chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16); product_id = rd_mem_addr_cfm.memdata & 0xFFFF; + usb_dev->d80_m80_variant = product_id == 0x8820; printk("chip_id=%x, chip_mcu_id = %d, product_id=0x%04x\n", chip_id, chip_mcu_id, product_id); printk("AIC8800D80 legacy: using Radxa SDK V3 loader profile\n"); @@ -272,7 +272,7 @@ int system_config_8800d80(struct aic_usb_dev *usb_dev){ * #118: without this the firmware boots and answers configuration * requests, but never confirms MM_START_REQ, so the MAC never starts. */ - if (product_id == 0x8820) { + if (usb_dev->d80_m80_variant) { ret = rwnx_send_dbg_mem_write_req(usb_dev, 0x40500010, 0x2); if (ret) { printk("0x8820: 40500010 write fail: %d\n", ret); @@ -283,7 +283,6 @@ int system_config_8800d80(struct aic_usb_dev *usb_dev){ printk("0x8820: 40506024 mask write fail: %d\n", ret); return ret; } - aic8800d80_m80_variant = 1; printk("AIC8800D80 0x8820: applied vendor syscfg writes, patch layer will be skipped\n"); } #if 1 @@ -389,7 +388,7 @@ int aicfw_download_fw_8800d80(struct aic_usb_dev *usb_dev) if(testmode == FW_NORMAL_MODE){ if (chip_id != CHIP_REV_U01){ - if (!aic8800d80_m80_variant) { + if (!usb_dev->d80_m80_variant) { if(rwnx_plat_bin_fw_upload_android(usb_dev, patch_info.addr_adid, FW_ADID_BASE_NAME_8800D80_U02)) { return -1; } @@ -422,7 +421,7 @@ int aicfw_download_fw_8800d80(struct aic_usb_dev *usb_dev) if (rwnx_send_dbg_mem_write_req(usb_dev, 0x40500048, 0x1e0000)) return -1; #endif - if (!aic8800d80_m80_variant && aicwf_patch_config_8800d80(usb_dev)) { + if (!usb_dev->d80_m80_variant && aicwf_patch_config_8800d80(usb_dev)) { return -1; } if (rwnx_send_dbg_start_app_req(usb_dev, RAM_FMAC_FW_ADDR_8800D80_U02, HOST_START_APP_AUTO)) { diff --git a/drivers/aic8800/aic_load_fw/aicwf_usb.c b/drivers/aic8800/aic_load_fw/aicwf_usb.c index 6655261..3f550f6 100644 --- a/drivers/aic8800/aic_load_fw/aicwf_usb.c +++ b/drivers/aic8800/aic_load_fw/aicwf_usb.c @@ -39,7 +39,6 @@ extern uint32_t gpio_dft_lvl;//0:defalut pull down, 1:default pull up u8 chip_id = 0; u8 chip_sub_id = 0; u8 chip_mcu_id = 0; -u8 aic8800d80_m80_variant = 0; /* product_id == 0x8820 (upstream issue #79) */ int fw_loaded = 0; void aicwf_usb_tx_flowctrl(struct aic_usb_dev *usb_dev, bool state) diff --git a/drivers/aic8800/aic_load_fw/aicwf_usb.h b/drivers/aic8800/aic_load_fw/aicwf_usb.h index 736fdcf..c29813d 100644 --- a/drivers/aic8800/aic_load_fw/aicwf_usb.h +++ b/drivers/aic8800/aic_load_fw/aicwf_usb.h @@ -223,6 +223,7 @@ struct aic_usb_dev { #endif u16 chipid; + bool d80_m80_variant; /* D80 product 0x8820: FMAC-only loader sequence */ bool tbusy; bool app_cmp; u32 fw_version_uint; diff --git a/tests/issue119-8820/README.md b/tests/issue119-8820/README.md new file mode 100644 index 0000000..0195cb6 --- /dev/null +++ b/tests/issue119-8820/README.md @@ -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. + diff --git a/tests/issue119-8820/test_loader.py b/tests/issue119-8820/test_loader.py new file mode 100644 index 0000000..bd3ba90 --- /dev/null +++ b/tests/issue119-8820/test_loader.py @@ -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 +#include +#include +#include +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") +