mirror of
https://github.com/shenmintao/aic8800d80.git
synced 2026-11-10 16:49:17 +00:00
Merge pull request #119 from kaijia323/fix/0x8820-syscfg-and-connect-ealready
fix(legacy-mcu1): support product 0x8820 and reject busy connect requests
This commit is contained in:
4 files changed
+200
-2
No files matched your search
@@ -3206,7 +3206,12 @@ static int rwnx_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|||||||
if((int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING||
|
if((int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING||
|
||||||
(int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTING) {
|
(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__);
|
AICWFDBG(LOGERROR, "%s driver is disconnecting or connecting ,return it \r\n", __func__);
|
||||||
return 0;
|
/*
|
||||||
|
* Returning 0 would accept this request without submitting it to
|
||||||
|
* the firmware or providing its own connect result.
|
||||||
|
* Reject the new request without changing an in-flight operation's state.
|
||||||
|
*/
|
||||||
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,7 @@ int rwnx_plat_userconfig_load_8800d80(struct aic_usb_dev *usb_dev){
|
|||||||
int system_config_8800d80(struct aic_usb_dev *usb_dev){
|
int system_config_8800d80(struct aic_usb_dev *usb_dev){
|
||||||
int syscfg_num;
|
int syscfg_num;
|
||||||
int ret, cnt;
|
int ret, cnt;
|
||||||
|
u32 product_id;
|
||||||
const u32 mem_addr = 0x40500000;
|
const u32 mem_addr = 0x40500000;
|
||||||
const u32 cache_mem_addr = 0x40100020;
|
const u32 cache_mem_addr = 0x40100020;
|
||||||
struct dbg_mem_read_cfm rd_mem_addr_cfm;
|
struct dbg_mem_read_cfm rd_mem_addr_cfm;
|
||||||
@@ -245,7 +246,9 @@ int system_config_8800d80(struct aic_usb_dev *usb_dev){
|
|||||||
chip_mcu_id = 1;
|
chip_mcu_id = 1;
|
||||||
}
|
}
|
||||||
chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16);
|
chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16);
|
||||||
printk("chip_id=%x, chip_mcu_id = %d\n", chip_id, chip_mcu_id);
|
product_id = rd_mem_addr_cfm.memdata & 0xFFFF;
|
||||||
|
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");
|
printk("AIC8800D80 legacy: using Radxa SDK V3 loader profile\n");
|
||||||
if (chip_mcu_id == 1) {
|
if (chip_mcu_id == 1) {
|
||||||
ret = rwnx_send_dbg_mem_read_req(usb_dev, cache_mem_addr, &rd_mem_addr_cfm);
|
ret = rwnx_send_dbg_mem_read_req(usb_dev, cache_mem_addr, &rd_mem_addr_cfm);
|
||||||
@@ -261,6 +264,25 @@ int system_config_8800d80(struct aic_usb_dev *usb_dev){
|
|||||||
}
|
}
|
||||||
printk("AIC8800D80 MCU1: enabled Bluetooth cache fix\n");
|
printk("AIC8800D80 MCU1: enabled Bluetooth cache fix\n");
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Product 0x8820 needs these extra system-configuration writes for
|
||||||
|
* MM_START to complete (#118/#119). Keep the normal patch uploads:
|
||||||
|
* skipping them causes Bluetooth HCI Reset to time out on the
|
||||||
|
* reported MCU1 unit.
|
||||||
|
*/
|
||||||
|
if (product_id == 0x8820) {
|
||||||
|
ret = rwnx_send_dbg_mem_write_req(usb_dev, 0x40500010, 0x2);
|
||||||
|
if (ret) {
|
||||||
|
printk("0x8820: 40500010 write fail: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = rwnx_send_dbg_mem_mask_write_req(usb_dev, 0x40506024, 0xff, 0xdf);
|
||||||
|
if (ret) {
|
||||||
|
printk("0x8820: 40506024 mask write fail: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
printk("AIC8800D80 0x8820: applied vendor syscfg writes\n");
|
||||||
|
}
|
||||||
#if 1
|
#if 1
|
||||||
syscfg_num = sizeof(syscfg_tbl_8800d80) / sizeof(u32) / 2;
|
syscfg_num = sizeof(syscfg_tbl_8800d80) / sizeof(u32) / 2;
|
||||||
for (cnt = 0; cnt < syscfg_num; cnt++) {
|
for (cnt = 0; cnt < syscfg_num; cnt++) {
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# 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 both product `0x8820` and regular D80 devices upload the complete
|
||||||
|
adid/patch/extended-patch/patch-table/patch-config layer before starting FMAC.
|
||||||
|
Alternating and reprobed devices receive the extra syscfg writes only when
|
||||||
|
their current product readback is `0x8820`. The checks also retain the MCU1
|
||||||
|
cache fix, propagate read/cache/syscfg write errors, and reject a new connect
|
||||||
|
without overwriting an operation already in progress.
|
||||||
|
|
||||||
|
The [hardware follow-up in PR #119](https://github.com/shenmintao/aic8800d80/pull/119#issuecomment-6468608942)
|
||||||
|
corrects the initial FMAC-only hypothesis from #118. On one UGREEN
|
||||||
|
`a69c:5723`, chip revision 7, MCU1, product `0x8820` unit running Ubuntu 26.04
|
||||||
|
and kernel `7.0.0-14-generic`, the two syscfg writes alone resolve the
|
||||||
|
`MM_START` timeout. Skipping the patch layer causes Bluetooth HCI Reset
|
||||||
|
(`0x0c03`) to time out; restoring all patch uploads, including patch config,
|
||||||
|
preserves Wi-Fi and restores controller initialization and LE discovery.
|
||||||
|
The firmware remains the branch's matched V3 set, including its 327,037-byte
|
||||||
|
FMAC.
|
||||||
|
|
||||||
|
The contributor tested the restored-patch variant, rather than this exact
|
||||||
|
follow-up commit. The cold-start sequence was replayed through USB
|
||||||
|
re-enumeration; physical unplug/replug, reboot of that variant, Bluetooth
|
||||||
|
pairing/audio, and other units remain unverified. These mocked checks do not
|
||||||
|
establish hardware compatibility or support for every device in issue #79.
|
||||||
|
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#!/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 {
|
||||||
|
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, fail_mask;
|
||||||
|
static u32 fail_write_addr;
|
||||||
|
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 && (!fail_write_addr || addr == fail_write_addr)) 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);
|
||||||
|
if (fail_mask) return -EIO;
|
||||||
|
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};
|
||||||
|
/* Both products need the full patch layer; only 0x8820 gets extra syscfg. */
|
||||||
|
assert(system_config_8800d80(&m80) == 0);
|
||||||
|
assert(system_config_8800d80(&d80) == 0);
|
||||||
|
assert(load_normal(&m80) == 0 && m80.patches == 5 && 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));
|
||||||
|
/* Re-read the product on reprobe in both directions. */
|
||||||
|
m80.id = 0x00078800; m80.patches = m80.fmacs = m80.starts = 0;
|
||||||
|
assert(system_config_8800d80(&m80) == 0);
|
||||||
|
assert(load_normal(&m80) == 0 && m80.patches == 5 && m80.fmacs == 1 && m80.starts == 1);
|
||||||
|
assert(m80.syscfg_writes == 1 && m80.syscfg_masks == 1);
|
||||||
|
d80.id = 0x00078820; d80.patches = d80.fmacs = d80.starts = 0;
|
||||||
|
assert(system_config_8800d80(&d80) == 0);
|
||||||
|
assert(load_normal(&d80) == 0 && d80.patches == 5 && d80.fmacs == 1 && d80.starts == 1);
|
||||||
|
assert(d80.syscfg_writes == 1 && d80.syscfg_masks == 1);
|
||||||
|
fail_read = 1; assert(system_config_8800d80(&d80) == -EIO);
|
||||||
|
fail_read = 0; fail_write = 1; assert(system_config_8800d80(&d80) == -EIO);
|
||||||
|
fail_write_addr = 0x40500010;
|
||||||
|
assert(system_config_8800d80(&d80) == -EIO);
|
||||||
|
fail_write = 0; fail_mask = 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: full patch uploads for both products, per-probe syscfg, MCU1 cache fix, write errors, busy connect state")
|
||||||
|
|
||||||
Reference in new issue
Block a user