From 483e1ebc813280dc566210ec5c48738060355f62 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:10 +1000 Subject: [PATCH 01/11] wifi: mm81x: move beacon work and init below the tx op Move mm81x_mac_beacon_work() and mm81x_mac_beacon_init() to below mm81x_mac_ops_tx() to prepare for future multicast handling such that we avoid a forward declaration. No functional change. Link: https://patch.msgid.link/20260827054917.255584-2-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/mac.c | 84 ++++++++++----------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 08ca116a68b4..4d0131246710 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -445,39 +445,6 @@ static void mm81x_beacon_h_fill_tx_info(struct mm81x *mors, cpu_to_le32(MM81X_TX_CONF_FLAGS_IMMEDIATE_REPORT); } -static void mm81x_mac_beacon_work(struct work_struct *work) -{ - struct mm81x_vif *mors_vif = - from_work(mors_vif, work, u.ap.beacon_work); - struct mm81x *mors = mm81x_vif_to_mors(mors_vif); - struct mm81x_skbq *mq; - struct sk_buff *beacon; - struct ieee80211_vif *vif = mm81x_vif_to_ieee80211_vif(mors_vif); - struct mm81x_skb_tx_info tx_info = { 0 }; - int num_bcn_vifs = atomic_read(&mors->num_bcn_vifs); - - mq = mm81x_hif_get_tx_beacon_queue(mors); - if (!mq) { - dev_err(mors->dev, "no matching beacon Q found"); - return; - } - - if (mm81x_skbq_count(mq) >= num_bcn_vifs) { - dev_err(mors->dev, - "previous beacon not consumed, dropping req [id:%d]", - mors_vif->id); - return; - } - - beacon = ieee80211_beacon_get(mors->hw, vif, false); - if (!beacon) - return; - - mm81x_beacon_h_fill_tx_info(mors, &tx_info, mors_vif, - cfg80211_chandef_s1g_pri_width(&mors->chandef)); - mm81x_skbq_skb_tx(mq, &beacon, &tx_info, MM81X_SKB_CHAN_BEACON); -} - void mm81x_mac_beacon_irq_handle(struct mm81x *mors, u32 status) { int vif_id; @@ -497,15 +464,6 @@ void mm81x_mac_beacon_irq_handle(struct mm81x *mors, u32 status) } } -static void mm81x_mac_beacon_init(struct mm81x_vif *mors_vif) -{ - struct mm81x *mors = mm81x_vif_to_mors(mors_vif); - - INIT_WORK(&mors_vif->u.ap.beacon_work, mm81x_mac_beacon_work); - mm81x_mac_beacon_irq_enable(mors_vif, true); - atomic_inc(&mors->num_bcn_vifs); -} - static struct hw_scan_tlv_hdr mm81x_hw_scan_h_pack_tlv_hdr(u16 tag, u16 len) { struct hw_scan_tlv_hdr hdr = { .tag = cpu_to_le16(tag), @@ -1329,6 +1287,48 @@ static void mm81x_mac_ops_tx(struct ieee80211_hw *hw, MM81X_SKB_CHAN_DATA); } +static void mm81x_mac_beacon_work(struct work_struct *work) +{ + struct mm81x_vif *mors_vif = + from_work(mors_vif, work, u.ap.beacon_work); + struct mm81x *mors = mm81x_vif_to_mors(mors_vif); + struct mm81x_skbq *mq; + struct sk_buff *beacon; + struct ieee80211_vif *vif = mm81x_vif_to_ieee80211_vif(mors_vif); + struct mm81x_skb_tx_info tx_info = { 0 }; + int num_bcn_vifs = atomic_read(&mors->num_bcn_vifs); + + mq = mm81x_hif_get_tx_beacon_queue(mors); + if (!mq) { + dev_err(mors->dev, "no matching beacon Q found"); + return; + } + + if (mm81x_skbq_count(mq) >= num_bcn_vifs) { + dev_err(mors->dev, + "previous beacon not consumed, dropping req [id:%d]", + mors_vif->id); + return; + } + + beacon = ieee80211_beacon_get(mors->hw, vif, false); + if (!beacon) + return; + + mm81x_beacon_h_fill_tx_info(mors, &tx_info, mors_vif, + cfg80211_chandef_s1g_pri_width(&mors->chandef)); + mm81x_skbq_skb_tx(mq, &beacon, &tx_info, MM81X_SKB_CHAN_BEACON); +} + +static void mm81x_mac_beacon_init(struct mm81x_vif *mors_vif) +{ + struct mm81x *mors = mm81x_vif_to_mors(mors_vif); + + INIT_WORK(&mors_vif->u.ap.beacon_work, mm81x_mac_beacon_work); + mm81x_mac_beacon_irq_enable(mors_vif, true); + atomic_inc(&mors->num_bcn_vifs); +} + static void mm81x_mac_ops_stop(struct ieee80211_hw *hw, bool suspend) { struct mm81x *mors = hw->priv; From ceebe279826fd781c4e90cc9a9c15c94c8b88765 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:11 +1000 Subject: [PATCH 02/11] wifi: mm81x: release buffered broadcast frames after DTIM Since we set the HOST_BROADCAST_PS_BUFFERING, mac80211 handles the buffering of broadcast frames. Make sure after each beacon we call ieee80211_get_buffered_bc() to release buffered broadcast frames after each DTIM beacon. Link: https://patch.msgid.link/20260827054917.255584-3-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/mac.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 4d0131246710..0bbedcdd0b3d 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -66,6 +66,9 @@ /* HW restart delay time before terminating hardware IF work items */ #define MM81X_HW_RESTART_DELAY_MS 20 +/* Maximum number of multicast frames to release after a DTIM beacon */ +#define MM81X_MAX_MC_FRAMES_AFTER_DTIM 4 + /* clang-format off */ /* mm81x chips do not support 16MHz */ @@ -1287,6 +1290,20 @@ static void mm81x_mac_ops_tx(struct ieee80211_hw *hw, MM81X_SKB_CHAN_DATA); } +static void mm81x_mac_send_buffered_bc(struct mm81x *mors, + struct ieee80211_vif *vif) +{ + int count = MM81X_MAX_MC_FRAMES_AFTER_DTIM; + struct ieee80211_tx_control control = { 0 }; + struct sk_buff *bc_frame; + + while (count-- && + (bc_frame = ieee80211_get_buffered_bc(mors->hw, vif))) { + IEEE80211_SKB_CB(bc_frame)->control.vif = vif; + mm81x_mac_ops_tx(mors->hw, &control, bc_frame); + } +} + static void mm81x_mac_beacon_work(struct work_struct *work) { struct mm81x_vif *mors_vif = @@ -1318,6 +1335,9 @@ static void mm81x_mac_beacon_work(struct work_struct *work) mm81x_beacon_h_fill_tx_info(mors, &tx_info, mors_vif, cfg80211_chandef_s1g_pri_width(&mors->chandef)); mm81x_skbq_skb_tx(mq, &beacon, &tx_info, MM81X_SKB_CHAN_BEACON); + + if (!test_bit(MM81X_STATE_DATA_QS_STOPPED, &mors->state_flags)) + mm81x_mac_send_buffered_bc(mors, vif); } static void mm81x_mac_beacon_init(struct mm81x_vif *mors_vif) From 2d59a60c748362fc9be330334ecd23b0e38372f2 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:12 +1000 Subject: [PATCH 03/11] wifi: mm81x: do not drop PS filtered frames in AP mode When the firmware reports a frame as filtered because the station has entered power save, the AP-mode path dropped the frame and returned "handled". In AP mode mac80211 owns the per-station power-save re-buffering, so the frame must instead be reported with IEEE80211_TX_STAT_TX_FILTERED and left for mac80211 to re-queue once the station wakes. Return false for AP interfaces so the tx-status path flags the frame as filtered rather than silently dropping it. Assisted-by: Claude:claude-opus-5 Link: https://patch.msgid.link/20260827054917.255584-4-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/skbq.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/skbq.c b/drivers/net/wireless/morsemicro/mm81x/skbq.c index 25655bd56d14..3bf15e7c38d8 100644 --- a/drivers/net/wireless/morsemicro/mm81x/skbq.c +++ b/drivers/net/wireless/morsemicro/mm81x/skbq.c @@ -231,10 +231,9 @@ static bool mm81x_tx_h_is_ps_filtered(struct mm81x_skbq *mq, WARN_ON_ONCE(!(le32_to_cpu(tx_sts->flags) & MM81X_TX_STATUS_FLAGS_PS_FILTERED)); - if (vif->type == NL80211_IFTYPE_AP) { - __mm81x_skbq_drop_pending_skb(mq, skb); - return true; - } + /* mac80211 buffers frames in AP mode */ + if (vif->type == NL80211_IFTYPE_AP) + return false; if (vif->type == NL80211_IFTYPE_STATION) { mm81x_skbq_insert_pending(mq, skb, tx_sts->pkt_id); From fa17ef70279f8d3ba2a4ba448d0863cf474e0cd1 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:13 +1000 Subject: [PATCH 04/11] wifi: mm81x: implement .tx_frames_pending() mac op Implement .tx_frames_pending() to assist mac80211 when deciding whether we should enter powersave such that we don't enter when there are pending tx frames. Link: https://patch.msgid.link/20260827054917.255584-5-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/mac.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 0bbedcdd0b3d..5bae7c6251df 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -2035,6 +2035,13 @@ static void mm81x_mac_ops_flush(struct ieee80211_hw *hw, mm81x_mac_wait_queues(mors); } +static bool mm81x_mac_ops_tx_frames_pending(struct ieee80211_hw *hw) +{ + struct mm81x *mors = hw->priv; + + return mm81x_mac_has_tx_pending(mors); +} + static int mm81x_mac_ops_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value) { @@ -2260,6 +2267,7 @@ static const struct ieee80211_ops mm81x_ops = { .configure_filter = mm81x_mac_ops_configure_filter, .sta_state = mm81x_mac_ops_sta_state, .flush = mm81x_mac_ops_flush, + .tx_frames_pending = mm81x_mac_ops_tx_frames_pending, .set_frag_threshold = mm81x_mac_set_frag_threshold, .set_rts_threshold = mm81x_mac_ops_set_rts_threshold, .sta_statistics = mm81x_mac_ops_sta_statistics, From c9c88ce192a448ce70ac9beb1c859e7923d71cbe Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:14 +1000 Subject: [PATCH 05/11] wifi: mm81x: factor out tx width selection The tx mac op is getting busy, lets factor out tx width selection to prepare for some future tx width selection fixes. No functional changes. Link: https://patch.msgid.link/20260827054917.255584-6-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/mac.c | 54 ++++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 5bae7c6251df..1444db98351b 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -1243,38 +1243,44 @@ static void mm81x_tx_h_fill_info(struct mm81x *mors, } } +static int mm81x_tx_h_get_bw(struct mm81x *mors, struct ieee80211_sta *sta, + struct sk_buff *skb, bool is_mgmt) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct mm81x_sta *mors_sta = NULL; + int tx_bw_mhz; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + return 1; + + if (is_mgmt) + return cfg80211_chandef_s1g_pri_width(&mors->chandef); + + if (sta) + mors_sta = (struct mm81x_sta *)sta->drv_priv; + + tx_bw_mhz = min(mm81x_tx_h_get_max_bw(mors), + cfg80211_chandef_get_width(&mors->chandef)); + if (mors_sta && mors_sta->max_bw_mhz) + tx_bw_mhz = min(tx_bw_mhz, mors_sta->max_bw_mhz); + + return tx_bw_mhz; +} + static void mm81x_mac_ops_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { - struct mm81x *mors = hw->priv; - struct mm81x_skbq *mq = NULL; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_vif *vif = info->control.vif; - struct mm81x_skb_tx_info tx_info = { 0 }; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); bool is_mgmt = ieee80211_is_mgmt(hdr->frame_control); - int tx_bw_mhz = cfg80211_chandef_get_width(&mors->chandef); struct ieee80211_sta *sta = control->sta; - int max_tx_bw = 0, sta_max_bw_mhz = 0; + struct mm81x_skb_tx_info tx_info = { 0 }; + struct mm81x *mors = hw->priv; + struct mm81x_skbq *mq; - if (sta) { - struct mm81x_sta *mors_sta = (struct mm81x_sta *)sta->drv_priv; - - sta_max_bw_mhz = mors_sta->max_bw_mhz; - } - - max_tx_bw = mm81x_tx_h_get_max_bw(mors); - tx_bw_mhz = min(max_tx_bw, tx_bw_mhz); - - if (is_mgmt) - tx_bw_mhz = cfg80211_chandef_s1g_pri_width(&mors->chandef); - if (sta_max_bw_mhz) - tx_bw_mhz = min(tx_bw_mhz, sta_max_bw_mhz); - if (ieee80211_is_probe_resp(hdr->frame_control)) - tx_bw_mhz = 1; - - mm81x_tx_h_fill_info(mors, &tx_info, skb, vif, tx_bw_mhz, sta); + mm81x_tx_h_fill_info(mors, &tx_info, skb, info->control.vif, + mm81x_tx_h_get_bw(mors, sta, skb, is_mgmt), sta); if (mm81x_tx_h_ps_filtered_for_sta(mors, skb, sta)) return; From 1595123dff32ebf6d459c36cebe6e860d4d8a6d5 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:15 +1000 Subject: [PATCH 06/11] wifi: mm81x: tx EAPOLs at primary width Right now port control frames such as EAPOLs are sent at the operating width which, within noisier environments, can lead to increased association times and at higher attenuation potentially prevent association from occurring altogether. Make sure we send EAPOLs and any other port control frames at the primary width. Link: https://patch.msgid.link/20260827054917.255584-7-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 1444db98351b..fa3b0cfccffe 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -1247,13 +1247,14 @@ static int mm81x_tx_h_get_bw(struct mm81x *mors, struct ieee80211_sta *sta, struct sk_buff *skb, bool is_mgmt) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct mm81x_sta *mors_sta = NULL; int tx_bw_mhz; if (ieee80211_is_probe_resp(hdr->frame_control)) return 1; - if (is_mgmt) + if (is_mgmt || info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) return cfg80211_chandef_s1g_pri_width(&mors->chandef); if (sta) From 3208462a40d0d844de1ff3f7b0b16d78e2d55f18 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:16 +1000 Subject: [PATCH 07/11] wifi: mm81x: clamp tx width to stas max supported width max_bw_mhz is not populated with the stas maximum supported width during association. This means data frames are sent at the operating width, regardless of whether the sta can support the operating width. For example a STA that supports up to 4MHz receiving frames from an AP running on an 8MHz operating channel will be sent frames at 8MHz which it cannot receive. Make sure we initialise the stas maximum support bandwidth and while we are here rename it to max_rx_bw_mhz to indicate its the maximum bandwidth the sta can receive frames from. Note: This should be conveyed via ieee80211_link_sta::bandwidth, but the ieee80211_sta_rx_bandwidth enum does not support S1G widths as of now and will require some non-trivial work to support in the future. Link: https://patch.msgid.link/20260827054917.255584-8-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/core.h | 7 ++++++- drivers/net/wireless/morsemicro/mm81x/mac.c | 11 +++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/core.h b/drivers/net/wireless/morsemicro/mm81x/core.h index 2fd4b4786e77..75e998e8e324 100644 --- a/drivers/net/wireless/morsemicro/mm81x/core.h +++ b/drivers/net/wireless/morsemicro/mm81x/core.h @@ -192,7 +192,12 @@ struct mm81x_sta { bool tid_tx[IEEE80211_NUM_TIDS]; bool tid_start_tx[IEEE80211_NUM_TIDS]; u8 tid_params[IEEE80211_NUM_TIDS]; - int max_bw_mhz; + + /* + * We really should be using link_sta::bandwidth but + * that requires non-trivial work. + */ + int max_rx_bw_mhz; struct mm81x_rc_sta rc; struct mmrc_rate last_sta_tx_rate; s16 avg_rssi; diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index fa3b0cfccffe..d77df6e9af85 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -325,7 +325,7 @@ static int mm81x_mac_ops_start(struct ieee80211_hw *hw) return 0; } -static int mm81x_tx_h_get_max_bw(struct mm81x *mors) +static int mm81x_tx_h_get_max_tx_bw(struct mm81x *mors) { return MM81X_FW_SUPP(&mors->fw_caps, 8MHZ) ? 8 : MM81X_FW_SUPP(&mors->fw_caps, 4MHZ) ? 4 : @@ -1260,10 +1260,10 @@ static int mm81x_tx_h_get_bw(struct mm81x *mors, struct ieee80211_sta *sta, if (sta) mors_sta = (struct mm81x_sta *)sta->drv_priv; - tx_bw_mhz = min(mm81x_tx_h_get_max_bw(mors), + tx_bw_mhz = min(mm81x_tx_h_get_max_tx_bw(mors), cfg80211_chandef_get_width(&mors->chandef)); - if (mors_sta && mors_sta->max_bw_mhz) - tx_bw_mhz = min(tx_bw_mhz, mors_sta->max_bw_mhz); + if (mors_sta && mors_sta->max_rx_bw_mhz) + tx_bw_mhz = min(tx_bw_mhz, mors_sta->max_rx_bw_mhz); return tx_bw_mhz; } @@ -1718,6 +1718,9 @@ static int mm81x_mac_ops_sta_state(struct ieee80211_hw *hw, mors_vif->u.ap.num_stas++; else if (vif->type == NL80211_IFTYPE_STATION) mors_vif->u.sta.is_assoc = true; + + mors_sta->max_rx_bw_mhz = + S1G_SUPP_CH_WIDTH_MAX(sta->deflink.s1g_cap.cap); } if (new_state < old_state && new_state == IEEE80211_STA_NONE) { From 0f5a5d9290ca1c766dfbdf534ee43f4c0b26253a Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Thu, 27 Aug 2026 15:49:17 +1000 Subject: [PATCH 08/11] wifi: mm81x: tx multicast frames from AP at primary width If an AP has stations that are operating on a sub-primary (i.e using the 4MHz primary of the 8MHz operating) multicast frames sent on the operating width will be lost. Make sure we tx them on the primary width to ensure all stations are capabile of receiving them. Link: https://patch.msgid.link/20260827054917.255584-9-lachlan.hodges@morsemicro.com Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/mac.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index d77df6e9af85..4485748518b4 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -1257,6 +1257,15 @@ static int mm81x_tx_h_get_bw(struct mm81x *mors, struct ieee80211_sta *sta, if (is_mgmt || info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) return cfg80211_chandef_s1g_pri_width(&mors->chandef); + /* + * In AP mode group addressed frames go out at the primary width so + * that every associated STA can receive them, including any that + * cannot support the operating width. + */ + if (info->control.vif->type == NL80211_IFTYPE_AP && + is_multicast_ether_addr(ieee80211_get_DA(hdr))) + return cfg80211_chandef_s1g_pri_width(&mors->chandef); + if (sta) mors_sta = (struct mm81x_sta *)sta->drv_priv; From 37f018809909dfc99646b1b1f79c2b3968e7890a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 20 Aug 2026 11:30:22 +0300 Subject: [PATCH 09/11] wifi: mm81x: fix type bugs handling sdio_readl/writel() The sdio_readl() and sdio_writel() functions, instead of returning kernel error codes, instead stores the error codes in a parameter. These caller functions pass should pass an int pointer to store the error code but instead pass a signed long. This will not work on big endian systems. On little endian systems passing a ssize_t means negative error codes are converted to positive values near UINT_MAX. This doesn't cause a problem at runtime because in mm81x_sdio_reg32_write() the error codes are discarded and we always return -EIO. In mm81x_sdio_reg32_read() the high bits are truncated away so the positive value is re-converted back to negative and the code works as intended. Either way, passing an int is the correct thing and is a cleanup. Fixes: b1906cea00b0 ("wifi: mm81x: add mm81x Wi-Fi HaLow driver") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/aoa7Hj1W7ETOrOzO@stanley.mountain Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/sdio.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/sdio.c b/drivers/net/wireless/morsemicro/mm81x/sdio.c index 96fce187dd35..65277399bf18 100644 --- a/drivers/net/wireless/morsemicro/mm81x/sdio.c +++ b/drivers/net/wireless/morsemicro/mm81x/sdio.c @@ -381,7 +381,7 @@ static int mm81x_sdio_dm_read(struct mm81x *mors, u32 address, u8 *data, static int mm81x_sdio_reg32_write(struct mm81x *mors, u32 address, u32 val) { - ssize_t ret = 0; + int ret = 0; u32 original_address = address; struct mm81x_sdio *sdio = (struct mm81x_sdio *)mors->drv_priv; struct sdio_func *func1 = sdio->func->card->sdio_func[0]; @@ -391,7 +391,7 @@ static int mm81x_sdio_reg32_write(struct mm81x *mors, u32 address, u32 val) address &= 0x0000FFFF; sdio_writel(func1, (__force u32)cpu_to_le32(val), - (__force u32)cpu_to_le32(address), (int *)&ret); + (__force u32)cpu_to_le32(address), &ret); if (ret) goto error; @@ -411,7 +411,7 @@ error: static int mm81x_sdio_reg32_read(struct mm81x *mors, u32 address, u32 *val) { u32 value; - ssize_t ret = 0; + int ret = 0; struct mm81x_sdio *sdio = (struct mm81x_sdio *)mors->drv_priv; struct sdio_func *func1 = sdio->func->card->sdio_func[0]; @@ -419,8 +419,7 @@ static int mm81x_sdio_reg32_read(struct mm81x *mors, u32 address, u32 *val) MM81X_CONFIG_ACCESS_4BYTE); address &= 0x0000FFFF; - value = sdio_readl(func1, (__force u32)cpu_to_le32(address), - (int *)&ret); + value = sdio_readl(func1, (__force u32)cpu_to_le32(address), &ret); if (ret) return ret; From 87230a514d2ca128c3fcd96126bb18162459640f Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Wed, 12 Aug 2026 14:07:37 +0800 Subject: [PATCH 10/11] wifi: mm81x: free the firmware scratch buffer on parse failures mm81x_fw_load_fw() allocates fw_buf before it inspects the image, but releases it only on the success path. The two early returns taken for a malformed image leave one firmware-sized allocation attached to the device, and mm81x_fw_flash() retries the load three times. Release it on those paths too, the way mm81x_fw_load_bcf() already does. Fixes: b1906cea00b0 ("wifi: mm81x: add mm81x Wi-Fi HaLow driver") Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260812060740.1275280-2-lilinmao@kylinos.cn Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/fw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/fw.c b/drivers/net/wireless/morsemicro/mm81x/fw.c index d6d2ad086c32..d407b57f4c9c 100644 --- a/drivers/net/wireless/morsemicro/mm81x/fw.c +++ b/drivers/net/wireless/morsemicro/mm81x/fw.c @@ -117,13 +117,15 @@ static int mm81x_fw_load_fw(struct mm81x *mors, const struct firmware *fw) if (mm81x_fw_get_header(fw->data, &ehdr)) { dev_err(mors->dev, "Wrong file format"); - return -EINVAL; + ret = -EINVAL; + goto out_free; } if (mm81x_fw_get_section_header(fw->data, &ehdr, &sh_strtab, ehdr.e_shstrndx)) { dev_err(mors->dev, "Invalid firmware. Missing string table"); - return -ENOENT; + ret = -ENOENT; + goto out_free; } sh_strs = (const char *)fw->data + sh_strtab.sh_offset; @@ -179,6 +181,7 @@ static int mm81x_fw_load_fw(struct mm81x *mors, const struct firmware *fw) if (ehdr.e_entry) ret = mm81x_fw_set_boot_addr(mors, ehdr.e_entry); +out_free: devm_kfree(mors->dev, fw_buf); return ret; } From dc1936211335eca00acca53d058dc94fbab02028 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Wed, 12 Aug 2026 14:07:38 +0800 Subject: [PATCH 11/11] wifi: mm81x: do not discard a failed firmware segment write When mm81x_dm_write() fails while downloading a PT_LOAD segment, mm81x_fw_load_fw() sets ret to -EIO and breaks out of the loop. The boot address write at the end of the function then overwrites ret, so a partially downloaded image is reported as loaded and the chip is triggered on it instead of the load being retried. Leave the function as soon as a segment fails. Fixes: b1906cea00b0 ("wifi: mm81x: add mm81x Wi-Fi HaLow driver") Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260812060740.1275280-3-lilinmao@kylinos.cn Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/fw.c b/drivers/net/wireless/morsemicro/mm81x/fw.c index d407b57f4c9c..a2cb97cefc4e 100644 --- a/drivers/net/wireless/morsemicro/mm81x/fw.c +++ b/drivers/net/wireless/morsemicro/mm81x/fw.c @@ -162,7 +162,7 @@ static int mm81x_fw_load_fw(struct mm81x *mors, const struct firmware *fw) mm81x_release_bus(mors); if (status) { ret = -EIO; - break; + goto out_free; } } }