From a9683a456079131a0381ed75fb7a4f5514527b3c Mon Sep 17 00:00:00 2001 From: Shen Mintao Date: Wed, 21 Oct 2026 10:33:37 +0800 Subject: [PATCH] fix(legacy-mcu1): backport Linux 7.3 cfg80211 support Backport main commit 2c4ab4b421d6a780842deeef700f23ba9ecf7578 to legacy-mcu1, preserving the branch's MCU1 firmware and loader profile. Adapt the probe status link ID, probe_peer callback and input cookie APIs; retain cfg80211-assigned cookies through asynchronous completion paths. Add an isolated native-kernel build check and record the regression results. The original branch reproduces all six reported errors on 7.3-rc3. The fix builds all three modules and passes MODPOST on upstream 7.3 final and Ubuntu 7.3-rc3, 7.2 and 6.17 headers. The final-release module_layout CRCs match the fully built test kernel. Hardware operation has not been tested. Refs #111 Co-authored-by: MastaG --- drivers/aic8800/aic8800_fdrv/rwnx_defs.h | 16 +++ drivers/aic8800/aic8800_fdrv/rwnx_main.c | 151 ++++++++++++++++++--- drivers/aic8800/aic8800_fdrv/rwnx_main.h | 5 + drivers/aic8800/aic8800_fdrv/rwnx_msg_rx.c | 12 +- drivers/aic8800/aic8800_fdrv/rwnx_tdls.c | 4 +- drivers/aic8800/aic8800_fdrv/rwnx_tx.c | 39 +++++- drivers/aic8800/aic8800_fdrv/rwnx_tx.h | 15 +- tests/kernel-build/.gitattributes | 1 + tests/kernel-build/README.md | 69 ++++++++++ tests/kernel-build/build.sh | 35 +++++ 10 files changed, 313 insertions(+), 34 deletions(-) create mode 100644 tests/kernel-build/.gitattributes create mode 100644 tests/kernel-build/README.md create mode 100644 tests/kernel-build/build.sh diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_defs.h b/drivers/aic8800/aic8800_fdrv/rwnx_defs.h index bd811e2..3363bcc 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_defs.h +++ b/drivers/aic8800/aic8800_fdrv/rwnx_defs.h @@ -335,6 +335,12 @@ struct apm_probe_sta { u8 sta_mac_addr[6]; u8 vif_idx; u64 probe_id; + /* + * 7.3+ only: the cookie cfg80211_ops::probe_peer() was called with, + * echoed back via cfg80211_probe_status() once the probe completes. + * See rwnx_cfg80211_probe_client(). + */ + u64 cookie; struct work_struct apmprobestaWork; struct workqueue_struct *apmprobesta_wq; }; @@ -675,6 +681,16 @@ struct rwnx_roc_elem { bool mgmt_roc; /* Indicate if we have switch on the RoC channel */ bool on_chan; + /* + * The cookie reported to cfg80211 for this RoC, via + * cfg80211_ready_on_channel() / cfg80211_remain_on_channel_expired(). + * On 7.3+, for a RoC started directly by cfg80211_ops::remain_on_channel(), + * this is the cookie cfg80211 pre-assigned; on every older kernel, and + * always for the purely-internal RoC started from inside mgmt_tx, it is + * rwnx_hw->roc_cookie_cnt at RoC-start time. See + * rwnx_cfg80211_remain_on_channel_(). + */ + u64 cookie; }; /* Structure containing channel survey information received from MAC */ diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_main.c b/drivers/aic8800/aic8800_fdrv/rwnx_main.c index 8e2207f..4421b9e 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_main.c +++ b/drivers/aic8800/aic8800_fdrv/rwnx_main.c @@ -3945,7 +3945,21 @@ void apm_probe_sta_work_process(struct work_struct *work) spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock); printk("sta %pM found = %d\n", mac, found); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) + /* + * 7.3 added an MLO link_id parameter between cookie and acked. This + * driver has no MLO support, and -1 is the documented value for + * "non-MLO" (see the cfg80211_probe_status() kerneldoc). The cookie + * source also changes here: sta_probe.cookie is the value cfg80211 + * pre-assigned via probe_peer(), which now must be echoed back + * instead of the driver's own probe_id counter -- see + * rwnx_cfg80211_probe_client(). + */ + if(found) + cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.cookie, -1, 1, 0, false, GFP_ATOMIC); + else + cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.cookie, -1, 0, 0, false, GFP_ATOMIC); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) if(found) cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 1, 0, false, GFP_ATOMIC); else @@ -4495,11 +4509,20 @@ int rwnx_cfg80211_set_monitor_channel_(struct wiphy *wiphy, } /** - * @probe_client: probe an associated client, must return a cookie that it - * later passes to cfg80211_probe_status(). + * @probe_peer: probe an associated client. Before 7.3 the driver returns a + * cookie of its own and passes the same value to cfg80211_probe_status() + * later; from 7.3 the cookie is assigned by cfg80211, passed in, and must + * be echoed back unchanged. Renamed from @probe_client upstream; kept as + * rwnx_cfg80211_probe_client() here since only the cfg80211_ops field + * name changed, not this driver's own naming. */ +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) +int rwnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 cookie) +#else int rwnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie) +#endif { //struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy); struct rwnx_vif *vif = netdev_priv(dev); @@ -4523,9 +4546,21 @@ int rwnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev, memcpy(vif->sta_probe.sta_mac_addr, peer, 6); - queue_work(vif->sta_probe.apmprobesta_wq, &vif->sta_probe.apmprobestaWork); +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) + /* + * 7.3 made this an input parameter: cfg80211 pre-assigns the cookie + * before calling us instead of us inventing one and returning it. Save + * it before queueing the work: the worker may run on another CPU as + * soon as it is queued, and it echoes this value back via + * cfg80211_probe_status() once the probe completes. + */ + vif->sta_probe.cookie = cookie; +#else *cookie = vif->sta_probe.probe_id; +#endif + + queue_work(vif->sta_probe.apmprobesta_wq, &vif->sta_probe.apmprobestaWork); return 0; } @@ -4726,6 +4761,7 @@ rwnx_cfg80211_remain_on_channel_(struct wiphy *wiphy, struct wireless_dev *wdev = &rwnx_vif->wdev; #endif struct rwnx_roc_elem *roc_elem; + u64 roc_cookie; struct mm_add_if_cfm add_if_cfm; struct mm_remain_on_channel_cfm roc_cfm; int error; @@ -4817,6 +4853,34 @@ rwnx_cfg80211_remain_on_channel_(struct wiphy *wiphy, /* Initialize the OFFCHAN TX queue to allow off-channel transmissions */ rwnx_txq_offchan_init(rwnx_vif); + /* + * Decide the cookie before the element is published and the request + * sent: the RX path can deliver the channel-switch indication and + * call cfg80211_ready_on_channel() with roc_elem->cookie before this + * thread resumes, so it must already be the final value by then. + * Taking one snapshot here also keeps that value and the one reported + * back to the caller below identical: rwnx_send_roc() sleeps, and the + * RoC expiry handler increments roc_cookie_cnt, so re-reading the + * counter afterwards could return a different value than the one + * already reported to cfg80211. + */ +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) + /* + * mgmt_roc_flag is false only for the direct cfg80211 + * .remain_on_channel call path (see rwnx_cfg80211_remain_on_channel() + * below), where on 7.3+ *cookie already holds the value cfg80211 + * pre-assigned and must be honored rather than overwritten. The + * purely-internal RoC this function also serves (started from + * inside mgmt_tx, mgmt_roc_flag true) never goes through that + * cfg80211 op, so there is no pre-assigned value to honor there -- + * keep inventing one, exactly as on every older kernel. + */ + roc_cookie = mgmt_roc_flag ? (u64)(rwnx_hw->roc_cookie_cnt) : *cookie; +#else + roc_cookie = (u64)(rwnx_hw->roc_cookie_cnt); +#endif + roc_elem->cookie = roc_cookie; + /* Forward the information to the FMAC */ rwnx_hw->roc_elem = roc_elem; error = rwnx_send_roc(rwnx_hw, rwnx_vif, chan, duration, &roc_cfm); @@ -4825,7 +4889,12 @@ rwnx_cfg80211_remain_on_channel_(struct wiphy *wiphy, if (error == 0) { /* Set the cookie value */ - *cookie = (u64)(rwnx_hw->roc_cookie_cnt); +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) + if (mgmt_roc_flag) + *cookie = roc_cookie; +#else + *cookie = roc_cookie; +#endif if(roc_cfm.status) { // failed to roc rwnx_hw->roc_elem = NULL; @@ -4855,18 +4924,42 @@ rwnx_cfg80211_remain_on_channel(struct wiphy *wiphy, #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) enum nl80211_channel_type channel_type, #endif - unsigned int duration, u64 *cookie -#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 2, 0) + unsigned int duration, +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) + /* + * 7.3 made this cookie an input parameter: cfg80211 + * pre-assigns it before calling us instead of us + * inventing one and returning it. See + * rwnx_cfg80211_remain_on_channel_(). rx_addr is + * unused for the reason given in the 7.2 arm below: + * it is always NULL for this driver. + */ + u64 cookie, const u8 *rx_addr +#elif AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 2, 0) /* * 7.2 added an optional receive address filter for the off-channel * period. cfg80211 refuses a non-NULL one unless the driver sets * NL80211_EXT_FEATURE_ROC_ADDR_FILTER, which this one does not, so it * is always NULL here. mac80211 ignores it in the same way. */ - , const u8 *rx_addr + u64 *cookie, const u8 *rx_addr +#else + u64 *cookie #endif ) { +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) + /* + * 7.3 passes the cookie by value, while the shared helper still takes a + * pointer because its internal caller invents one. Only the argument + * differs, so pick it here and leave the call itself alone. + */ + u64 local_cookie = cookie; + u64 *cookie_ptr = &local_cookie; +#else + u64 *cookie_ptr = cookie; +#endif + return rwnx_cfg80211_remain_on_channel_(wiphy, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) wdev, @@ -4877,7 +4970,7 @@ rwnx_cfg80211_remain_on_channel(struct wiphy *wiphy, #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) channel_type, #endif - duration, cookie, false); + duration, cookie_ptr, false); } /** @@ -5051,7 +5144,17 @@ struct ieee80211_channel *rwnx_cfg80211_get_channel(struct wiphy *wiphy) /** * @mgmt_tx: Transmit a management frame. */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) +#if (AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0)) +static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, + /* + * 7.3 made this cookie an input parameter: cfg80211 + * pre-assigns it before calling us instead of us + * inventing one and returning it. See + * rwnx_start_mgmt_xmit(). + */ + u64 cookie) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie) @@ -5066,7 +5169,7 @@ static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, bool dont_wait_for_ack, #endif u64 *cookie) -#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ +#endif /* AICWF_CFG80211_VERSION_CODE >= 7.3 / LINUX_VERSION_CODE >= 3.14 */ { struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) @@ -5153,7 +5256,8 @@ static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, return -EINVAL; } } else { - u64 cookie; + /* The internal RoC's own cookie; deliberately not the mgmt_tx one. */ + u64 roc_cookie; int error; AICWFDBG(LOGINFO, "mgmt rx remain on chan\n"); @@ -5161,13 +5265,13 @@ static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* Start a ROC procedure for 30ms */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) error = rwnx_cfg80211_remain_on_channel_(wiphy, wdev, channel, - 30, &cookie, true); + 30, &roc_cookie, true); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) error = rwnx_cfg80211_remain_on_channel_(wiphy, wdev, channel, NL80211_CHAN_NO_HT, - 30, &cookie, true); + 30, &roc_cookie, true); #else error = rwnx_cfg80211_remain_on_channel_(wiphy, dev, channel, NL80211_CHAN_NO_HT, - 30, &cookie, true); + 30, &roc_cookie, true); #endif if (error) { @@ -5184,11 +5288,16 @@ static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, #endif send_frame: - #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) - return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, params, offchan, cookie); + #if (AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0)) + { + u64 local_cookie = cookie; + return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, params, offchan, &local_cookie, true); + } + #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) + return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, params, offchan, cookie, false); #else - return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, channel, offchan, wait, buf, len, no_cck, dont_wait_for_ack, cookie); - #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ + return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, channel, offchan, wait, buf, len, no_cck, dont_wait_for_ack, cookie, false); + #endif /* AICWF_CFG80211_VERSION_CODE >= 7.3 / LINUX_VERSION_CODE >= 3.14 */ } /** @@ -6553,7 +6662,11 @@ static struct cfg80211_ops rwnx_cfg80211_ops = { typeof(&rwnx_cfg80211_set_monitor_channel_with_dev)), rwnx_cfg80211_set_monitor_channel_with_dev, rwnx_cfg80211_set_monitor_channel), +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) + .probe_peer = rwnx_cfg80211_probe_client, +#else .probe_client = rwnx_cfg80211_probe_client, +#endif // .mgmt_frame_register = rwnx_cfg80211_mgmt_frame_register, .set_wiphy_params = rwnx_cfg80211_set_wiphy_params, .set_txq_params = rwnx_cfg80211_set_txq_params, diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_main.h b/drivers/aic8800/aic8800_fdrv/rwnx_main.h index bdf33dc..1788740 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_main.h +++ b/drivers/aic8800/aic8800_fdrv/rwnx_main.h @@ -93,8 +93,13 @@ void apm_staloss_work_process(struct work_struct *work); void apm_probe_sta_work_process(struct work_struct *work); int rwnx_cfg80211_set_monitor_channel_(struct wiphy *wiphy, struct cfg80211_chan_def *chandef); +#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(7, 3, 0) +int rwnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 cookie); +#else int rwnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie); +#endif void rwnx_cfg80211_mgmt_frame_register(struct wiphy *wiphy, #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) struct net_device *dev, diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_msg_rx.c b/drivers/aic8800/aic8800_fdrv/rwnx_msg_rx.c index 386c28f..4aa237f 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_msg_rx.c +++ b/drivers/aic8800/aic8800_fdrv/rwnx_msg_rx.c @@ -123,13 +123,13 @@ static inline int rwnx_rx_chan_switch_ind(struct rwnx_hw *rwnx_hw, if (!roc_elem->mgmt_roc) { /* Inform the host that we have switch on the indicated off-channel */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) - cfg80211_ready_on_channel(roc_elem->wdev->netdev, (u64)(rwnx_hw->roc_cookie_cnt), + cfg80211_ready_on_channel(roc_elem->wdev->netdev, roc_elem->cookie, roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC); #elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) - cfg80211_ready_on_channel(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt), + cfg80211_ready_on_channel(roc_elem->wdev, roc_elem->cookie, roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC); #else - cfg80211_ready_on_channel(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt), + cfg80211_ready_on_channel(roc_elem->wdev, roc_elem->cookie, roc_elem->chan, roc_elem->duration, GFP_ATOMIC); #endif } @@ -245,13 +245,13 @@ static inline int rwnx_rx_remain_on_channel_exp_ind(struct rwnx_hw *rwnx_hw, /* Inform the host that off-channel period has expired */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) - cfg80211_remain_on_channel_expired(roc_elem->wdev->netdev, (u64)(rwnx_hw->roc_cookie_cnt), + cfg80211_remain_on_channel_expired(roc_elem->wdev->netdev, roc_elem->cookie, roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC); #elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) - cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt), + cfg80211_remain_on_channel_expired(roc_elem->wdev, roc_elem->cookie, roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC); #else - cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt), + cfg80211_remain_on_channel_expired(roc_elem->wdev, roc_elem->cookie, roc_elem->chan, GFP_ATOMIC); #endif } diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_tdls.c b/drivers/aic8800/aic8800_fdrv/rwnx_tdls.c index b9ed49a..cf4cf12 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_tdls.c +++ b/drivers/aic8800/aic8800_fdrv/rwnx_tdls.c @@ -775,9 +775,9 @@ rwnx_tdls_send_mgmt_packet_data(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_v params.len = skb->len; params.buf = skb->data; - ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, ¶ms, false, &cookie); + ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, ¶ms, false, &cookie, false); #else - ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, NULL, false, 0, skb->data, skb->len, false, false, &cookie); + ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, NULL, false, 0, skb->data, skb->len, false, false, &cookie, false); #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ return ret; diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_tx.c b/drivers/aic8800/aic8800_fdrv/rwnx_tx.c index 2cec672..9030899 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_tx.c +++ b/drivers/aic8800/aic8800_fdrv/rwnx_tx.c @@ -1325,6 +1325,7 @@ int intf_tx(struct rwnx_hw *priv,struct msg_buf *msg) sw_txhdr->rwnx_sta = sta; sw_txhdr->rwnx_vif = rwnx_vif; sw_txhdr->skb = skb; + sw_txhdr->cookie = (unsigned long)skb; sw_txhdr->headroom = headroom; sw_txhdr->map_len = skb->len - offsetof(struct rwnx_txhdr, hw_hdr); @@ -1541,6 +1542,7 @@ netdev_tx_t rwnx_start_xmit(struct sk_buff *skb, struct net_device *dev) sw_txhdr->rwnx_sta = sta; sw_txhdr->rwnx_vif = rwnx_vif; sw_txhdr->skb = skb; + sw_txhdr->cookie = (unsigned long)skb; sw_txhdr->headroom = headroom; sw_txhdr->map_len = skb->len - offsetof(struct rwnx_txhdr, hw_hdr); @@ -1630,7 +1632,12 @@ free: * @params: Mgmt frame parameters * @offchan: Indicate whether the frame must be send via the offchan TXQ. * (is is redundant with params->offchan ?) - * @cookie: updated with a unique value to identify the frame with upper layer + * @cookie: on return, the value cfg80211_mgmt_tx_status() will report for + * this frame: invented here (the skb pointer) unless + * @use_given_cookie is set, in which case *@cookie is kept. + * @use_given_cookie: *@cookie already holds the cookie cfg80211 assigned + * (7.3+, the direct .mgmt_tx path only); honour it instead of + * inventing one. Every other caller passes false. * */ @@ -1638,7 +1645,7 @@ free: #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, struct cfg80211_mgmt_tx_params *params, bool offchan, - u64 *cookie) + u64 *cookie, bool use_given_cookie) #else int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, struct ieee80211_channel *channel, bool offchan, @@ -1649,7 +1656,7 @@ int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) bool dont_wait_for_ack, #endif - u64 *cookie) + u64 *cookie, bool use_given_cookie) #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ { struct rwnx_hw *rwnx_hw = vif->rwnx_hw; @@ -1703,7 +1710,18 @@ int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, return -ENOMEM; } - *cookie = (unsigned long)skb; + /* + * use_given_cookie is true only for the direct cfg80211 .mgmt_tx call + * path on 7.3+, where *cookie already holds the value cfg80211 + * pre-assigned and must be echoed back via cfg80211_mgmt_tx_status() + * later -- see rwnx_cfg80211_mgmt_tx(). TDLS's internal reuse of this + * function (use_given_cookie == false) never goes through that cfg80211 + * op, so it still gets an invented, locally-unique cookie exactly as + * before this fix, and on every kernel older than 7.3 every caller + * passes use_given_cookie == false, also leaving this unchanged. + */ + if (!use_given_cookie) + *cookie = (unsigned long)skb; /* * Move skb->data pointer in order to reserve room for rwnx_txhdr @@ -1773,6 +1791,7 @@ int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, sw_txhdr->rwnx_sta = sta; sw_txhdr->rwnx_vif = vif; sw_txhdr->skb = skb; + sw_txhdr->cookie = *cookie; sw_txhdr->headroom = headroom; sw_txhdr->map_len = skb->len - offsetof(struct rwnx_txhdr, hw_hdr); #ifdef CONFIG_RWNX_AMSDUS_TX @@ -1924,6 +1943,7 @@ void rwnx_probersp_work(struct work_struct *work) sw_txhdr->rwnx_sta = sta; sw_txhdr->rwnx_vif = rwnx_vif; sw_txhdr->skb = skb; + sw_txhdr->cookie = (unsigned long)skb; sw_txhdr->headroom = headroom; sw_txhdr->map_len = skb->len - offsetof(struct rwnx_txhdr, hw_hdr); #ifdef CONFIG_RWNX_AMSDUS_TX @@ -2278,6 +2298,15 @@ netdev_tx_t rwnx_start_monitor_if_xmit(struct sk_buff *skb, struct net_device *d sw_txhdr->rwnx_sta = sta; sw_txhdr->rwnx_vif = vif; sw_txhdr->skb = skb_mgmt; + /* + * Injected frames are flagged TXU_CNTRL_MGMT below and complete through + * the same path as management frames, which reports sw_txhdr->cookie to + * cfg80211_mgmt_tx_status(). There is no cfg80211-assigned cookie for an + * injected frame, so this is the skb-derived value that path reported + * before the field existed. Not gated on 7.3: this path runs on every + * kernel with CONFIG_RWNX_MON_XMIT. + */ + sw_txhdr->cookie = (unsigned long)skb_mgmt; sw_txhdr->headroom = headroom; sw_txhdr->map_len = skb_mgmt->len - offsetof(struct rwnx_txhdr, hw_hdr); sw_txhdr->raw_frame = 1; @@ -2389,7 +2418,7 @@ int rwnx_txdatacfm(void *pthis, void *host_id) #endif /* Confirm transmission to CFG80211 */ cfg80211_mgmt_tx_status(&sw_txhdr->rwnx_vif->wdev, - (unsigned long)skb, + sw_txhdr->cookie, (skb->data + sw_txhdr->headroom), sw_txhdr->frame_len, rwnx_txst.acknowledged, diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_tx.h b/drivers/aic8800/aic8800_fdrv/rwnx_tx.h index 532ffab..dff6839 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_tx.h +++ b/drivers/aic8800/aic8800_fdrv/rwnx_tx.h @@ -130,6 +130,17 @@ struct rwnx_sw_txhdr { #endif u32 need_cfm; struct sk_buff *skb; + /* + * The cookie cfg80211_mgmt_tx_status() reports for this frame. sw_txhdr + * comes from a kmem_cache and is never zeroed, so every allocation site + * sets this: (unsigned long)skb, which is what the completion path + * reported before the field existed, except in rwnx_start_mgmt_xmit(), + * which stores the cfg80211-assigned cookie it was handed on 7.3+ (see + * use_given_cookie there). Only frames flagged TXU_CNTRL_MGMT are ever + * reported, but the field is initialised unconditionally so that filter + * is not what keeps it safe. + */ + u64 cookie; size_t map_len; dma_addr_t dma_addr; @@ -158,7 +169,7 @@ netdev_tx_t rwnx_start_xmit(struct sk_buff *skb, struct net_device *dev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, struct cfg80211_mgmt_tx_params *params, bool offchan, - u64 *cookie); + u64 *cookie, bool use_given_cookie); #else int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, struct ieee80211_channel *channel, bool offchan, @@ -169,7 +180,7 @@ int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) bool dont_wait_for_ack, #endif - u64 *cookie); + u64 *cookie, bool use_given_cookie); #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ #ifdef CONFIG_RWNX_MON_XMIT netdev_tx_t rwnx_start_monitor_if_xmit(struct sk_buff *skb, diff --git a/tests/kernel-build/.gitattributes b/tests/kernel-build/.gitattributes new file mode 100644 index 0000000..dfdb8b7 --- /dev/null +++ b/tests/kernel-build/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/tests/kernel-build/README.md b/tests/kernel-build/README.md new file mode 100644 index 0000000..d7e8ca8 --- /dev/null +++ b/tests/kernel-build/README.md @@ -0,0 +1,69 @@ +# Native kernel build regression + +Build the current driver against real, prepared kernel headers and symbol +versions. The script cleans a temporary source copy, builds all three modules +and leaves the results for inspection. It does not install or load them. + +```sh +bash tests/kernel-build/build.sh /path/to/kernel/build CC=gcc-14 +``` + +Set `JOBS` to choose parallelism and `TMPDIR` to an existing directory if +build outputs need to survive system temporary-directory cleanup. + +Use the kernel's matching compiler and configuration. For a custom kernel, +pass the exact output directory used to build it, including `O=...` when +applicable. `make modules_prepare` alone does not generate `Module.symvers`; +see the [external module build documentation](https://docs.kernel.org/kbuild/modules.html). + +For OpenWrt wireless backports, use the separate +[backports build check](../openwrt-backports/README.md), which supplies the +staged wireless headers, symbol versions and `CFG80211_VERSION` together. + +## Issue #111: Linux 7.3 on legacy-mcu1 + +The `legacy-mcu1` branch was missing `main`'s Linux 7.3 compatibility commit +[`2c4ab4b`](https://github.com/shenmintao/aic8800d80/commit/2c4ab4b421d6a780842deeef700f23ba9ecf7578). +The backport adapts `cfg80211_probe_status()`'s link ID, the renamed +`probe_peer` callback, and the input cookies for probe, management TX and +remain-on-channel operations. Completion notifications retain the cookie +assigned by cfg80211; internal TX/RoC callers keep their driver-assigned +cookies. The existing MCU1 firmware and loader profile are unchanged. + +The original branch at `e074b7e` reproduces all six errors from the +[issue's build log](https://github.com/shenmintao/aic8800d80/issues/111#issuecomment-6294321166) +against Linux 7.3-rc3 headers: two probe-status argument errors, the renamed +probe callback and its resulting initializer error, and incompatible +management TX and remain-on-channel callback types. + +## Verified on 2026-10-21 + +| Kernel/build headers | Fixed legacy-mcu1 result | +| --- | --- | +| Upstream Linux 7.3.0 final, x86_64 test configuration | All three modules built; MODPOST passed | +| Ubuntu 7.3.0-070300rc3-generic headers | All three modules built; MODPOST passed | +| Ubuntu 7.2.0-070200-generic headers | All three modules built; MODPOST passed | +| Ubuntu 6.17.0-14-generic headers | All three modules built; MODPOST passed | + +The final-release check uses the upstream `linux-7.3.tar.xz` source, checked +against kernel.org's SHA-256 manifest. The test kernel enables modules, +symbol versioning, cfg80211, USB, Bluetooth and kprobes. Both `vmlinux` and +the in-tree modules were built with GCC 14.2.0 to generate matching headers +and `Module.symvers` before running this script. No symbol-version or +MODPOST checks were bypassed. All three modules report `7.3.0` vermagic and +the same `module_layout` CRC as that kernel's `Module.symvers`. + +The Ubuntu header checks also use GCC 14.2.0. The 7.2 and 7.3-rc3 headers +were generated with GCC 15 and produce compiler/attribute warnings; BTF +generation is skipped where a matching `vmlinux` is unavailable. The rc3 +`cfg80211.h` is byte-identical to the +[upstream 7.3 final header](https://github.com/torvalds/linux/blob/v7.3/include/net/cfg80211.h). + +These checks cover compilation and symbol resolution. They do not test the +reporter's custom `7.3.0-lxpup64` configuration, module loading, firmware +startup, Wi-Fi traffic or Bluetooth operation on hardware. + +After a successful build, install only the newly built modules against the +same kernel. Chain build and installation with `&&` so a failed build cannot +lead to an attempt to install stale modules. MCU1 hardware should continue +using `legacy-mcu1`. diff --git a/tests/kernel-build/build.sh b/tests/kernel-build/build.sh new file mode 100644 index 0000000..c0a9bc3 --- /dev/null +++ b/tests/kernel-build/build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Build a source copy against a prepared native kernel; never install or load it. +set -euo pipefail + +if [ "$#" -lt 1 ]; then + echo "Usage: $0 KERNEL_BUILD_DIR [make arguments...]" >&2 + exit 2 +fi + +kernel_dir=$(realpath "$1") +shift + +for required in "$kernel_dir/Makefile" "$kernel_dir/Module.symvers" \ + "$kernel_dir/include/generated/autoconf.h"; do + if [ ! -s "$required" ]; then + echo "Missing build input: $required" >&2 + exit 2 + fi +done + +repo_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +build_dir=$(mktemp -d "${TMPDIR:-/tmp}/aic8800-kernel-build.XXXXXX") +cp -a "$repo_dir/drivers/aic8800/." "$build_dir/" +echo "Build directory: $build_dir" + +# Discard copied build outputs so stale modules cannot satisfy the check. +make -C "$kernel_dir" M="$build_dir" "$@" clean +make -C "$kernel_dir" M="$build_dir" -j"${JOBS:-2}" "$@" modules + +for module in aic_load_fw/aic_load_fw.ko aic8800_fdrv/aic8800_fdrv.ko \ + aic_zlp_quirk/aic_zlp_quirk.ko; do + test -s "$build_dir/$module" +done + +echo "All three modules built successfully in $build_dir"