summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2026-10-10 09:25:49 +0200
committerJohannes Berg <johannes.berg@intel.com>2026-10-10 09:26:06 +0200
commitfbfa27d9b06f025c5f2cfa79d0e01e6400eeff2a (patch)
treebdcde0641f516cf83cbc49ff30c6bf58c5669c23 /drivers
parent07b3eb8b9048976b585d29f1a1df9a435423c473 (diff)
parent7e6689cbbf9e6040e4ce38656a126459aa994f68 (diff)
downloadlinux-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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/morsemicro/mm81x/command.c5
-rw-r--r--drivers/net/wireless/morsemicro/mm81x/core.h16
-rw-r--r--drivers/net/wireless/morsemicro/mm81x/mac.c297
-rw-r--r--drivers/net/wireless/morsemicro/mm81x/mmrc.c34
-rw-r--r--drivers/net/wireless/morsemicro/mm81x/yaps_hw.c14
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);