diff options
| author | Johannes Berg <johannes.berg@intel.com> | 2026-10-10 09:25:49 +0200 |
|---|---|---|
| committer | Johannes Berg <johannes.berg@intel.com> | 2026-10-10 09:26:06 +0200 |
| commit | fbfa27d9b06f025c5f2cfa79d0e01e6400eeff2a (patch) | |
| tree | bdcde0641f516cf83cbc49ff30c6bf58c5669c23 | |
| parent | 07b3eb8b9048976b585d29f1a1df9a435423c473 (diff) | |
| parent | 7e6689cbbf9e6040e4ce38656a126459aa994f68 (diff) | |
| download | linux-next-fbfa27d9b06f025c5f2cfa79d0e01e6400eeff2a.tar.gz linux-next-fbfa27d9b06f025c5f2cfa79d0e01e6400eeff2a.zip | |
Merge tag 'mm81x-next-2026-10-09' of https://github.com/MorseMicroLabs/linux-wireless
Lachlan Hodges says:
====================
mm81x 7.4 PR #2
- monitor mode support
- rx improvements
- fix persistent NO_IR flag bug
- misc minor LLM fixes
====================
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
| -rw-r--r-- | drivers/net/wireless/morsemicro/mm81x/command.c | 5 | ||||
| -rw-r--r-- | drivers/net/wireless/morsemicro/mm81x/core.h | 16 | ||||
| -rw-r--r-- | drivers/net/wireless/morsemicro/mm81x/mac.c | 297 | ||||
| -rw-r--r-- | drivers/net/wireless/morsemicro/mm81x/mmrc.c | 34 | ||||
| -rw-r--r-- | drivers/net/wireless/morsemicro/mm81x/yaps_hw.c | 14 |
5 files changed, 273 insertions, 93 deletions
diff --git a/drivers/net/wireless/morsemicro/mm81x/command.c b/drivers/net/wireless/morsemicro/mm81x/command.c index afb7ee9bb236..56dd338321f0 100644 --- a/drivers/net/wireless/morsemicro/mm81x/command.c +++ b/drivers/net/wireless/morsemicro/mm81x/command.c @@ -262,6 +262,9 @@ int mm81x_cmd_add_if(struct mm81x *mors, u16 *vif_id, const u8 *addr, case NL80211_IFTYPE_AP: req.interface_type = cpu_to_le32(HOST_CMD_INTERFACE_TYPE_AP); break; + case NL80211_IFTYPE_MONITOR: + req.interface_type = cpu_to_le32(HOST_CMD_INTERFACE_TYPE_MON); + break; default: return -EOPNOTSUPP; } @@ -458,7 +461,7 @@ int mm81x_cmd_cfg_multicast_filter(struct mm81x *mors, struct mm81x_vif *mors_vif) { struct host_cmd_req_mcast_filter *req; - struct mcast_filter *filter = mors->mcast_filter; + struct mm81x_mcast_filter *filter = mors->mcast_filter; u16 filter_list_len = sizeof(filter->addr_list[0]) * filter->count; u16 alloc_len = filter_list_len + sizeof(*req); int ret = 0; diff --git a/drivers/net/wireless/morsemicro/mm81x/core.h b/drivers/net/wireless/morsemicro/mm81x/core.h index 75e998e8e324..d46323815304 100644 --- a/drivers/net/wireless/morsemicro/mm81x/core.h +++ b/drivers/net/wireless/morsemicro/mm81x/core.h @@ -225,7 +225,7 @@ struct mm81x_stale_tx_status { struct timer_list timer; }; -struct mcast_filter { +struct mm81x_mcast_filter { u8 count; /* * Integer representation of the last four bytes of a multicast MAC @@ -335,6 +335,18 @@ struct mm81x { struct ieee80211_vif __rcu *vifs[MM81X_MAX_IF]; + /* + * Groups A-MPDU subframes for monitor reporting. The firmware stamps + * every subframe of an A-MPDU with the same receive timestamp, so a + * change in timestamp starts a new reference. + */ + struct { + u64 timestamp; + u32 reference; + } rx_ampdu; + + bool monitor_en; + /* @mm81x_state_flags */ unsigned long state_flags; @@ -377,7 +389,7 @@ struct mm81x { wait_queue_head_t tx_empty_waitq; struct cfg80211_chan_def chandef; - struct mcast_filter *mcast_filter; + struct mm81x_mcast_filter *mcast_filter; atomic_t num_bcn_vifs; unsigned long beacon_irqs_enabled; u8 drv_priv[] __aligned(sizeof(void *)); diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 1a7382d27475..6f1feb24fe6a 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -7,6 +7,7 @@ #include <linux/jiffies.h> #include <linux/crc32.h> #include <net/mac80211.h> +#include <net/ieee80211_radiotap.h> #include <asm/div64.h> #include <linux/kernel.h> #include "hif.h" @@ -83,7 +84,7 @@ .max_power = 30, \ } -static struct ieee80211_channel mors_s1ghz_channels[] = { +static const struct ieee80211_channel mors_s1ghz_channels[] = { CHANS1G(1, 902, 500, IEEE80211_CHAN_S1G_NO_PRIMARY), CHANS1G(3, 903, 500, 0), CHANS1G(5, 904, 500, 0), @@ -114,11 +115,9 @@ static struct ieee80211_channel mors_s1ghz_channels[] = { /* clang-format on */ -static struct ieee80211_supported_band mors_band_s1ghz = { +static const struct ieee80211_supported_band mors_band_s1ghz = { .band = NL80211_BAND_S1GHZ, .s1g_cap.s1g = true, - .channels = mors_s1ghz_channels, - .n_channels = ARRAY_SIZE(mors_s1ghz_channels), .bitrates = NULL, .n_bitrates = 0, .s1g_cap.cap[4] = 0x80 /* STA type sensor only for AP & STA */ @@ -268,8 +267,10 @@ static void mm81x_mac_check_fw_disabled_chans(struct ieee80211_hw *hw) u32 i; struct mm81x *mors = hw->priv; struct host_cmd_resp_get_disabled_channels *resp; + struct ieee80211_supported_band *sband = + hw->wiphy->bands[NL80211_BAND_S1GHZ]; u32 resp_len = sizeof(struct host_cmd_disabled_channel_entry) * - ARRAY_SIZE(mors_s1ghz_channels) + + sband->n_channels + sizeof(*resp); resp = kzalloc(resp_len, GFP_KERNEL); @@ -282,8 +283,13 @@ static void mm81x_mac_check_fw_disabled_chans(struct ieee80211_hw *hw) if (ret) goto out; - for (i = 0; i < ARRAY_SIZE(mors_s1ghz_channels); i++) { - struct ieee80211_channel *ch = &mors_s1ghz_channels[i]; + if (le32_to_cpu(resp->n_channels) > sband->n_channels) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *ch = &sband->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; @@ -336,7 +342,8 @@ static int mm81x_tx_h_get_max_tx_bw(struct mm81x *mors) static void mm81x_mac_caps_init(struct mm81x *mors) { struct mm81x_fw_caps *fw_caps = &mors->fw_caps; - struct ieee80211_sta_s1g_cap *s1g = &mors_band_s1ghz.s1g_cap; + struct ieee80211_sta_s1g_cap *s1g = + &mors->hw->wiphy->bands[NL80211_BAND_S1GHZ]->s1g_cap; #define __FW_CAP_N(_n, _cap, _bit) \ do { \ @@ -1355,11 +1362,10 @@ static void mm81x_mac_beacon_work(struct work_struct *work) mm81x_mac_send_buffered_bc(mors, vif); } -static void mm81x_mac_beacon_init(struct mm81x_vif *mors_vif) +static void mm81x_mac_beacon_start(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); } @@ -1396,6 +1402,9 @@ static void mm81x_mac_ops_remove_interface(struct ieee80211_hw *hw, dev_err(mors->dev, "mm81x_cmd_rm_if failed %d", ret); RCU_INIT_POINTER(mors->vifs[mors_vif->id], NULL); + + if (vif->type == NL80211_IFTYPE_MONITOR) + __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, hw->flags); } static s32 mm81x_mac_get_max_txpower(struct mm81x *mors) @@ -1510,6 +1519,9 @@ static int mm81x_mac_ops_config(struct ieee80211_hw *hw, int radio_idx, return ret; } + if (changed & IEEE80211_CONF_CHANGE_MONITOR) + mors->monitor_en = conf->flags & IEEE80211_CONF_MONITOR; + if ((changed & IEEE80211_CONF_CHANGE_POWER) && !(changed & IEEE80211_CONF_CHANGE_CHANNEL) && !(conf->flags & IEEE80211_CONF_MONITOR)) { @@ -1582,7 +1594,9 @@ static void mm81x_mac_ops_bss_info_changed(struct ieee80211_hw *hw, mm81x_cmd_config_beacon_timer(mors, mors_vif, info->enable_beacon); - if (!info->enable_beacon) + if (info->enable_beacon) + mm81x_mac_beacon_start(mors_vif); + else mm81x_mac_beacon_finish(mors_vif); } @@ -1599,7 +1613,7 @@ static u64 mm81x_mac_ops_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) { struct mm81x *mors = hw->priv; - struct mcast_filter *filter; + struct mm81x_mcast_filter *filter; struct netdev_hw_addr *addr; u16 addr_count = netdev_hw_addr_list_count(mc_list); u16 len = sizeof(*filter) + addr_count * sizeof(filter->addr_list[0]); @@ -1626,17 +1640,38 @@ static u64 mm81x_mac_ops_prepare_multicast(struct ieee80211_hw *hw, return (u64)(unsigned long)filter; } +static void mm81x_mc_iface_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mm81x_vif *mors_vif = ieee80211_vif_to_mors_vif(vif); + struct mm81x *mors = data; + int ret; + + if (vif->type == NL80211_IFTYPE_MONITOR) + return; + + ret = mm81x_cmd_cfg_multicast_filter(mors, mors_vif); + if (ret) + dev_err(mors->dev, "Multicast filtering failed - rc=%d", ret); +} + +#define MM81X_MONITOR_FILTERS \ + (FIF_ALLMULTI | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_CONTROL | \ + FIF_FCSFAIL | \ + FIF_OTHER_BSS | \ + FIF_PSPOLL | \ + FIF_PROBE_REQ | \ + FIF_MCAST_ACTION) + static void mm81x_mac_ops_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { + struct mm81x_mcast_filter *cmd = (void *)(unsigned long)multicast; struct mm81x *mors = hw->priv; - struct mcast_filter *cmd = (void *)(unsigned long)multicast; - struct mm81x_vif *mors_vif = NULL; - struct ieee80211_vif *vif = NULL; - int vif_id = 0; - int ret = 0; if (!cmd) goto out; @@ -1644,25 +1679,15 @@ static void mm81x_mac_ops_configure_filter(struct ieee80211_hw *hw, kfree(mors->mcast_filter); mors->mcast_filter = cmd; - for (vif_id = 0; vif_id < ARRAY_SIZE(mors->vifs); vif_id++) { - vif = mm81x_rcu_dereference_vif_id(mors, vif_id, false); - if (!vif) - continue; - - mors_vif = ieee80211_vif_to_mors_vif(vif); - - ret = mm81x_cmd_cfg_multicast_filter(mors, mors_vif); - if (!ret) - continue; - - dev_err(mors->dev, "Multicast filtering failed - rc=%d", ret); - mors->mcast_filter = NULL; - kfree(cmd); - break; - } + ieee80211_iterate_active_interfaces(mors->hw, + IEEE80211_IFACE_ITER_NORMAL, + mm81x_mc_iface_iterator, mors); out: - *total_flags &= 0; + if (mors->monitor_en) + *total_flags &= MM81X_MONITOR_FILTERS; + else + *total_flags = 0; } static int mm81x_mac_ops_conf_tx(struct ieee80211_hw *hw, @@ -1900,14 +1925,95 @@ static u8 mm81x_rx_h_rc_bw_to_rx_bw(__le32 ratecode) } } +static u32 mm81x_rx_h_ampdu_reference(struct mm81x *mors, u64 timestamp) +{ + if (timestamp != mors->rx_ampdu.timestamp) { + mors->rx_ampdu.timestamp = timestamp; + mors->rx_ampdu.reference++; + } + + return mors->rx_ampdu.reference; +} + +static enum ieee80211_radiotap_s1g_ppdu_format +mm81x_rx_h_ppdu_format(__le32 ratecode) +{ + switch (mm81x_ratecode_preamble_get(ratecode)) { + case MM81X_RATE_PREAMBLE_S1G_1M: + return IEEE80211_RADIOTAP_S1G_PPDU_FORMAT_1M; + case MM81X_RATE_PREAMBLE_S1G_LONG: + return IEEE80211_RADIOTAP_S1G_PPDU_FORMAT_LONG; + default: + return IEEE80211_RADIOTAP_S1G_PPDU_FORMAT_SHORT; + } +} + +#define S1G_RADIOTAP_TLV_LEN \ + (sizeof(struct ieee80211_radiotap_tlv) + \ + ALIGN(sizeof(struct ieee80211_radiotap_s1g), 4)) + +static void mm81x_rx_h_add_rt_s1g_tlv(struct sk_buff *skb, + struct mm81x_skb_rx_status *hdr_rx_status, + struct ieee80211_rx_status *rx_status) +{ + __le32 ratecode = hdr_rx_status->mm81x_ratecode; + u32 flags = le32_to_cpu(hdr_rx_status->flags); + u8 color = hdr_rx_status->bss_color; + struct ieee80211_radiotap_tlv *tlv; + struct ieee80211_radiotap_s1g s1g; + + if (skb_headroom(skb) < S1G_RADIOTAP_TLV_LEN) + return; + + s1g.known = cpu_to_le16(IEEE80211_RADIOTAP_S1G_KNOWN_PPDU_FORMAT | + IEEE80211_RADIOTAP_S1G_KNOWN_RESPONSE_IND | + IEEE80211_RADIOTAP_S1G_KNOWN_GI | + IEEE80211_RADIOTAP_S1G_KNOWN_BW | + IEEE80211_RADIOTAP_S1G_KNOWN_UPLINK_IND | + IEEE80211_RADIOTAP_S1G_KNOWN_NSS | + IEEE80211_RADIOTAP_S1G_KNOWN_MCS | + IEEE80211_RADIOTAP_S1G_KNOWN_COLOR); + + s1g.data1 = le16_encode_bits(mm81x_rx_h_ppdu_format(ratecode), + IEEE80211_RADIOTAP_S1G_DATA1_PPDU_FORMAT) | + le16_encode_bits(MM81X_RX_STATUS_FLAGS_RI_GET(flags), + IEEE80211_RADIOTAP_S1G_DATA1_RESPONSE_IND) | + le16_encode_bits(mm81x_ratecode_sgi_get(ratecode), + IEEE80211_RADIOTAP_S1G_DATA1_GI) | + le16_encode_bits(mm81x_ratecode_nss_index_get(ratecode), + IEEE80211_RADIOTAP_S1G_DATA1_NSS) | + le16_encode_bits(mm81x_ratecode_bw_index_get(ratecode), + IEEE80211_RADIOTAP_S1G_DATA1_BW) | + le16_encode_bits(mm81x_ratecode_mcs_index_get(ratecode), + IEEE80211_RADIOTAP_S1G_DATA1_MCS); + + s1g.data2 = le16_encode_bits(color, IEEE80211_RADIOTAP_S1G_DATA2_COLOR) | + le16_encode_bits(MM81X_RX_STATUS_FLAGS_UPL_IND_GET(flags), + IEEE80211_RADIOTAP_S1G_DATA2_UPLINK_IND) | + le16_encode_bits((u8)rx_status->signal, + IEEE80211_RADIOTAP_S1G_DATA2_RSSI); + + tlv = skb_push(skb, S1G_RADIOTAP_TLV_LEN); + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_S1G); + tlv->len = cpu_to_le16(sizeof(s1g)); + + memcpy(tlv->data, &s1g, sizeof(s1g)); + memset(tlv->data + sizeof(s1g), 0, + S1G_RADIOTAP_TLV_LEN - sizeof(*tlv) - sizeof(s1g)); + + skb_set_mac_header(skb, S1G_RADIOTAP_TLV_LEN); + rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; +} + static void mm81x_rx_h_fill_status(struct mm81x *mors, struct mm81x_skb_rx_status *hdr_rx_status, struct ieee80211_rx_status *rx_status, struct sk_buff *skb) { - u32 flags = le32_to_cpu(hdr_rx_status->flags); + u64 timestamp = le64_to_cpu(hdr_rx_status->rx_timestamp_us); u16 freq_100khz = le16_to_cpu(hdr_rx_status->freq_100khz); __le32 ratecode = hdr_rx_status->mm81x_ratecode; + u32 flags = le32_to_cpu(hdr_rx_status->flags); rx_status->signal = le16_to_cpu(hdr_rx_status->rssi); rx_status->encoding = RX_ENC_S1G; @@ -1915,33 +2021,60 @@ static void mm81x_rx_h_fill_status(struct mm81x *mors, rx_status->freq = KHZ100_TO_MHZ(freq_100khz); rx_status->freq_offset = (freq_100khz % 10) ? 1 : 0; rx_status->nss = NSS_IDX_TO_NSS(mm81x_ratecode_nss_index_get(ratecode)); + rx_status->rate_idx = mm81x_ratecode_mcs_index_get(ratecode); + rx_status->bw = mm81x_rx_h_rc_bw_to_rx_bw(ratecode); + rx_status->mactime = timestamp; + rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64; if (flags & MM81X_RX_STATUS_FLAGS_DECRYPTED) rx_status->flag |= RX_FLAG_DECRYPTED; - rx_status->rate_idx = mm81x_ratecode_mcs_index_get(ratecode); - rx_status->bw = mm81x_rx_h_rc_bw_to_rx_bw(ratecode); - if (mm81x_ratecode_sgi_get(ratecode)) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + if (mors->monitor_en) { + if (flags & MM81X_RX_STATUS_FLAGS_CRC_ERROR) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (flags & MM81X_RX_STATUS_FLAGS_AMPDU) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS | + RX_FLAG_AMPDU_EOF_BIT_KNOWN; + rx_status->ampdu_reference = + mm81x_rx_h_ampdu_reference(mors, timestamp); + + if (flags & MM81X_RX_STATUS_FLAGS_EOF) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + if (flags & MM81X_RX_STATUS_FLAGS_NDP) { + rx_status->flag |= RX_FLAG_NO_PSDU; + + /* + * S1G PSDUs are not standardised in radiotap yet, so + * for now report as sounding PSDUs so that acks etc. + * can be passed through. + */ + rx_status->zero_length_psdu_type = + IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING; + + /* + * Null Data Packets contain no data, therefore no + * mcs encoding. The STF/LTF are usually BPSK, therefore + * the NDP mcs rate can always be considered as 0. + */ + rx_status->rate_idx = 0; + rx_status->nss = 1; + } + } } -static void mm81x_rx_h_update_sta(struct ieee80211_vif *vif, - struct ieee80211_hdr *hdr, +static void mm81x_rx_h_update_sta(struct ieee80211_sta *sta, struct ieee80211_rx_status *rx_status) { - struct ieee80211_sta *sta; - struct mm81x_sta *msta; - u8 *lookup = ieee80211_is_s1g_beacon(hdr->frame_control) ? hdr->addr1 : - hdr->addr2; + struct mm81x_sta *msta = (void *)sta->drv_priv; lockdep_assert_in_rcu_read_lock(); - sta = ieee80211_find_sta(vif, lookup); - if (!sta) - return; - - msta = (void *)sta->drv_priv; if (msta->avg_rssi) { msta->avg_rssi = CALC_AVG_RSSI(msta->avg_rssi, rx_status->signal); @@ -1969,9 +2102,11 @@ void mm81x_mac_rx_skb(struct mm81x *mors, struct sk_buff *skb, struct mm81x_skb_rx_status *hdr_rx_status) { struct ieee80211_vif *vif; + struct ieee80211_sta *sta = NULL; struct ieee80211_hw *hw = mors->hw; struct ieee80211_rx_status rx_status; struct ieee80211_hdr *hdr = (void *)skb->data; + u8 *lookup; memset(&rx_status, 0, sizeof(rx_status)); @@ -1980,19 +2115,28 @@ void mm81x_mac_rx_skb(struct mm81x *mors, struct sk_buff *skb, return; } + lookup = ieee80211_is_s1g_beacon(hdr->frame_control) ? hdr->addr1 : + hdr->addr2; + mm81x_rx_h_fill_status(mors, hdr_rx_status, &rx_status, skb); scoped_guard(rcu) { vif = mm81x_rx_h_skb_get_vif(mors, skb, hdr_rx_status); - if (!vif) - goto rx; + if (vif) { + sta = ieee80211_find_sta(vif, lookup); + if (sta) + mm81x_rx_h_update_sta(sta, &rx_status); + } - mm81x_rx_h_update_sta(vif, hdr, &rx_status); - } + if (mors->monitor_en) + mm81x_rx_h_add_rt_s1g_tlv(skb, hdr_rx_status, &rx_status); + + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); -rx: - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_ni(hw, skb); + local_bh_disable(); + ieee80211_rx_napi(hw, sta ? &sta->deflink : NULL, skb, NULL); + local_bh_enable(); + } } static void mm81x_mac_flush_queues(struct mm81x *mors) @@ -2259,9 +2403,6 @@ static int mm81x_mac_ops_add_interface(struct ieee80211_hw *hw, rcu_assign_pointer(mors->vifs[mors_vif->id], vif); - if (vif->type == NL80211_IFTYPE_AP) - mm81x_mac_beacon_init(mors_vif); - ret = mm81x_cmd_get_capabilities(mors, mors_vif->id, &mors->fw_caps); if (ret) { dev_err(mors->dev, @@ -2270,6 +2411,12 @@ static int mm81x_mac_ops_add_interface(struct ieee80211_hw *hw, return ret; } + if (vif->type == NL80211_IFTYPE_MONITOR) + ieee80211_hw_set(hw, RX_INCLUDES_FCS); + + if (vif->type == NL80211_IFTYPE_AP) + INIT_WORK(&mors_vif->u.ap.beacon_work, mm81x_mac_beacon_work); + ieee80211_wake_queues(mors->hw); return ret; } @@ -2330,7 +2477,6 @@ static void mm81x_mac_config_hw(struct mm81x *mors) for (i = 0; i < NUM_NL80211_BANDS; i++) hw->wiphy->bands[i] = NULL; - hw->wiphy->bands[NL80211_BAND_S1GHZ] = &mors_band_s1ghz; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION); hw->wiphy->reg_notifier = mm81x_reg_notifier; @@ -2343,6 +2489,9 @@ static void mm81x_mac_config_hw(struct mm81x *mors) hw->sta_data_size = sizeof(struct mm81x_sta); hw->extra_tx_headroom = sizeof(struct mm81x_skb_hdr) + mm81x_bus_get_alignment(mors); + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US | + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU; mors->wiphy = hw->wiphy; @@ -2357,6 +2506,7 @@ static void mm81x_mac_config_hw(struct mm81x *mors) ieee80211_hw_set(hw, PS_NULLFUNC_STACK); ieee80211_hw_set(hw, SUPPORTS_TX_FRAG); ieee80211_hw_set(hw, SUPPORTS_NDP_BLOCKACK); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); SET_IEEE80211_PERM_ADDR(hw, mors->macaddr); @@ -2404,6 +2554,28 @@ static void mm81x_mac_stale_tx_status_timer_init(struct mm81x *mors) timer_setup(&mors->stale_status.timer, mm81x_stale_tx_status_timer, 0); } +static int mm81x_mac_set_sband(struct mm81x *mors) +{ + struct ieee80211_supported_band *sband; + + sband = devm_kmemdup(mors->dev, &mors_band_s1ghz, sizeof(*sband), + GFP_KERNEL); + if (!sband) + return -ENOMEM; + + sband->channels = devm_kmemdup_array(mors->dev, mors_s1ghz_channels, + ARRAY_SIZE(mors_s1ghz_channels), + sizeof(*mors_s1ghz_channels), + GFP_KERNEL); + if (!sband->channels) + return -ENOMEM; + + sband->n_channels = ARRAY_SIZE(mors_s1ghz_channels); + mors->hw->wiphy->bands[NL80211_BAND_S1GHZ] = sband; + + return 0; +} + int mm81x_mac_register(struct mm81x *mors) { int ret; @@ -2418,6 +2590,11 @@ int mm81x_mac_register(struct mm81x *mors) return ret; mm81x_mac_config_hw(mors); + + ret = mm81x_mac_set_sband(mors); + if (ret) + return ret; + mm81x_mac_hw_scan_init(mors); mm81x_mac_stale_tx_status_timer_init(mors); diff --git a/drivers/net/wireless/morsemicro/mm81x/mmrc.c b/drivers/net/wireless/morsemicro/mm81x/mmrc.c index fe7e4f501d6c..6bedcdbcb61f 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mmrc.c +++ b/drivers/net/wireless/morsemicro/mm81x/mmrc.c @@ -2,6 +2,8 @@ /* * Copyright (c) 2017-2026 Morse Micro */ +#include <linux/bitops.h> + #include "mmrc.h" /* @@ -199,34 +201,12 @@ static const u32 sym_table[10] = { 24, 36, 48, 72, 96, 144, 192, 216, 256, 288 }; /* - * Calculate which bit is the nth bit set in an integer based flag. - */ -static u8 nth_bit(u16 in, u16 index) -{ - u32 i; - u8 count = 0; - - for (i = 0; count != index + 1; i++) { - if (((1u << i) & in) != 0) - count++; - } - - return i - 1; -} - -/* * Calculate the input bit's index among all the set bits in an integer * based flag. */ static u16 bit_index(u16 in, u32 bit_pos) { - u16 i; - u16 index = 0; - - for (i = 0; i != bit_pos + 1; i++) { - if (((1u << i) & in) != 0) - index++; - } + u16 index = hweight16(in & GENMASK(bit_pos, 0)); if (index == 0) { /* Could not match bit pos to caps */ @@ -291,19 +271,19 @@ static struct mmrc_rate get_rate_row(struct mmrc_table *tb, u16 index) u16 mcs_index = index / rows; u16 mcs_modulo = index % rows; - mcs = nth_bit(tb->caps.rates, mcs_index); + mcs = fns(tb->caps.rates, mcs_index); /* Find our spatial stream */ rows = rows / streams; - streams = nth_bit(tb->caps.spatial_streams, mcs_modulo / rows); + streams = fns(tb->caps.spatial_streams, mcs_modulo / rows); /* Find our bandwidth */ ss_index = index % rows; rows = rows / bw; - bw = nth_bit(tb->caps.bandwidth, ss_index / rows); + bw = fns(tb->caps.bandwidth, ss_index / rows); /* Find our guard */ - guard = nth_bit(tb->caps.guard, index % guard); + guard = fns(tb->caps.guard, index % guard); /* Add range checks to keep scan-build happy */ if (bw >= MMRC_BW_MAX) diff --git a/drivers/net/wireless/morsemicro/mm81x/yaps_hw.c b/drivers/net/wireless/morsemicro/mm81x/yaps_hw.c index 3d641d23c35b..1d90137470c2 100644 --- a/drivers/net/wireless/morsemicro/mm81x/yaps_hw.c +++ b/drivers/net/wireless/morsemicro/mm81x/yaps_hw.c @@ -401,16 +401,17 @@ exit: static bool mm81x_read_pkts_h_is_valid_delim(u32 delim) { u8 calc_crc = mm81x_yaps_hw_crc(delim); - int pkt_size = YAPS_DELIM_GET_PHANDLE_SIZE(delim); + int pkt_size = YAPS_DELIM_GET_PKT_SIZE(delim); int padding = YAPS_DELIM_GET_PADDING(delim); if (calc_crc != YAPS_DELIM_GET_CRC(delim)) return false; - if (pkt_size == 0) + if (pkt_size <= 0) return false; - if ((pkt_size + padding) > YAPS_MAX_PKT_SIZE_BYTES) + if ((pkt_size + YAPS_RESERVED_PAGE_SIZE + padding) > + YAPS_MAX_PKT_SIZE_BYTES) return false; /* Pkt length + padding should not require more padding */ @@ -455,6 +456,10 @@ static int mm81x_yaps_hw_read_pkts(struct mm81x_yaps *yaps, return 0; if (bytes_remaining < 0) return bytes_remaining; + if (!IS_ALIGNED(bytes_remaining, sizeof(u32))) { + dev_warn(yaps->mors->dev, "yaps truncated delimiter"); + return -EIO; + } if (bytes_remaining > YAPS_HW_WINDOW_SIZE_BYTES) { bytes_remaining = YAPS_HW_WINDOW_SIZE_BYTES; @@ -486,6 +491,9 @@ static int mm81x_yaps_hw_read_pkts(struct mm81x_yaps *yaps, int total_len; int pkt_size; + if (bytes_remaining < (int)sizeof(delim)) + break; + delim = le32_to_cpu(*((__le32 *)read_ptr)); read_ptr += sizeof(delim); bytes_remaining -= sizeof(delim); |
