summaryrefslogtreecommitdiff
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c539
1 files changed, 527 insertions, 12 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 85f139016da2..70ca74a75f22 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -854,6 +854,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_MLO_RECONF_REM_LINKS] = { .type = NLA_U16 },
[NL80211_ATTR_EPCS] = { .type = NLA_FLAG },
[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 },
+ [NL80211_ATTR_WIPHY_RADIO_INDEX] = { .type = NLA_U8 },
};
/* policy for the key attributes */
@@ -2446,6 +2447,7 @@ fail:
static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx)
{
const struct wiphy_radio *r = &wiphy->radio[idx];
+ const struct wiphy_radio_cfg *rcfg = &wiphy->radio_cfg[idx];
struct nlattr *radio, *freq;
int i;
@@ -2456,6 +2458,11 @@ static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx)
if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
goto nla_put_failure;
+ if (rcfg->rts_threshold &&
+ nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_RTS_THRESHOLD,
+ rcfg->rts_threshold))
+ goto nla_put_failure;
+
if (r->antenna_mask &&
nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK,
r->antenna_mask))
@@ -2639,7 +2646,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
u32 tx_ant = 0, rx_ant = 0;
int res;
- res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);
+ res = rdev_get_antenna(rdev, -1, &tx_ant, &rx_ant);
if (!res) {
if (nla_put_u32(msg,
NL80211_ATTR_WIPHY_ANTENNA_TX,
@@ -3608,6 +3615,33 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
return __nl80211_set_channel(rdev, netdev, info, link_id);
}
+static int nl80211_set_wiphy_radio(struct genl_info *info,
+ struct cfg80211_registered_device *rdev,
+ int radio_idx)
+{
+ u32 rts_threshold = 0, old_rts, changed = 0;
+ int result;
+
+ if (!rdev->ops->set_wiphy_params)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
+ rts_threshold = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
+ changed |= WIPHY_PARAM_RTS_THRESHOLD;
+ }
+
+ old_rts = rdev->wiphy.radio_cfg[radio_idx].rts_threshold;
+
+ rdev->wiphy.radio_cfg[radio_idx].rts_threshold = rts_threshold;
+
+ result = rdev_set_wiphy_params(rdev, radio_idx, changed);
+ if (result)
+ rdev->wiphy.radio_cfg[radio_idx].rts_threshold = old_rts;
+
+ return 0;
+}
+
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = NULL;
@@ -3620,6 +3654,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
u32 frag_threshold = 0, rts_threshold = 0;
u8 coverage_class = 0;
u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
+ int radio_idx = -1;
rtnl_lock();
/*
@@ -3670,6 +3705,19 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (result)
return result;
+ if (info->attrs[NL80211_ATTR_WIPHY_RADIO_INDEX]) {
+ /* Radio idx is not expected for non-multi radio wiphy */
+ if (rdev->wiphy.n_radio <= 0)
+ return -EINVAL;
+
+ radio_idx = nla_get_u8(
+ info->attrs[NL80211_ATTR_WIPHY_RADIO_INDEX]);
+ if (radio_idx >= rdev->wiphy.n_radio)
+ return -EINVAL;
+
+ return nl80211_set_wiphy_radio(info, rdev, radio_idx);
+ }
+
if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
struct ieee80211_txq_params txq_params;
struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
@@ -3759,7 +3807,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
mbm = nla_get_u32(info->attrs[idx]);
}
- result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
+ result = rdev_set_tx_power(rdev, txp_wdev, radio_idx, type,
+ mbm);
if (result)
return result;
}
@@ -3785,7 +3834,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
- result = rdev_set_antenna(rdev, tx_ant, rx_ant);
+ result = rdev_set_antenna(rdev, radio_idx, tx_ant, rx_ant);
if (result)
return result;
}
@@ -3879,16 +3928,30 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (changed) {
u8 old_retry_short, old_retry_long;
u32 old_frag_threshold, old_rts_threshold;
- u8 old_coverage_class;
+ u8 old_coverage_class, i;
u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum;
+ u32 *old_radio_rts_threshold = NULL;
if (!rdev->ops->set_wiphy_params)
return -EOPNOTSUPP;
+ if (rdev->wiphy.n_radio) {
+ old_radio_rts_threshold = kcalloc(rdev->wiphy.n_radio,
+ sizeof(u32),
+ GFP_KERNEL);
+ if (!old_radio_rts_threshold)
+ return -ENOMEM;
+ }
+
old_retry_short = rdev->wiphy.retry_short;
old_retry_long = rdev->wiphy.retry_long;
old_frag_threshold = rdev->wiphy.frag_threshold;
old_rts_threshold = rdev->wiphy.rts_threshold;
+ if (old_radio_rts_threshold) {
+ for (i = 0 ; i < rdev->wiphy.n_radio; i++)
+ old_radio_rts_threshold[i] =
+ rdev->wiphy.radio_cfg[i].rts_threshold;
+ }
old_coverage_class = rdev->wiphy.coverage_class;
old_txq_limit = rdev->wiphy.txq_limit;
old_txq_memory_limit = rdev->wiphy.txq_memory_limit;
@@ -3900,8 +3963,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.retry_long = retry_long;
if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
rdev->wiphy.frag_threshold = frag_threshold;
- if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+ if ((changed & WIPHY_PARAM_RTS_THRESHOLD) &&
+ old_radio_rts_threshold) {
rdev->wiphy.rts_threshold = rts_threshold;
+ for (i = 0 ; i < rdev->wiphy.n_radio; i++)
+ rdev->wiphy.radio_cfg[i].rts_threshold =
+ rdev->wiphy.rts_threshold;
+ }
if (changed & WIPHY_PARAM_COVERAGE_CLASS)
rdev->wiphy.coverage_class = coverage_class;
if (changed & WIPHY_PARAM_TXQ_LIMIT)
@@ -3911,18 +3979,26 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (changed & WIPHY_PARAM_TXQ_QUANTUM)
rdev->wiphy.txq_quantum = txq_quantum;
- result = rdev_set_wiphy_params(rdev, changed);
+ result = rdev_set_wiphy_params(rdev, radio_idx, changed);
if (result) {
rdev->wiphy.retry_short = old_retry_short;
rdev->wiphy.retry_long = old_retry_long;
rdev->wiphy.frag_threshold = old_frag_threshold;
rdev->wiphy.rts_threshold = old_rts_threshold;
+ if (old_radio_rts_threshold) {
+ for (i = 0 ; i < rdev->wiphy.n_radio; i++)
+ rdev->wiphy.radio_cfg[i].rts_threshold =
+ old_radio_rts_threshold[i];
+ }
rdev->wiphy.coverage_class = old_coverage_class;
rdev->wiphy.txq_limit = old_txq_limit;
rdev->wiphy.txq_memory_limit = old_txq_memory_limit;
rdev->wiphy.txq_quantum = old_txq_quantum;
- return result;
}
+
+ if (old_rts_threshold)
+ kfree(old_radio_rts_threshold);
+ return result;
}
return 0;
@@ -4012,7 +4088,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
if (rdev->ops->get_tx_power && !wdev->valid_links) {
int dbm, ret;
- ret = rdev_get_tx_power(rdev, wdev, 0, &dbm);
+ ret = rdev_get_tx_power(rdev, wdev, -1, 0, &dbm);
if (ret == 0 &&
nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
DBM_TO_MBM(dbm)))
@@ -4084,7 +4160,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
if (rdev->ops->get_tx_power) {
int dbm, ret;
- ret = rdev_get_tx_power(rdev, wdev, link_id, &dbm);
+ ret = rdev_get_tx_power(rdev, wdev, -1, link_id, &dbm);
if (ret == 0 &&
nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
DBM_TO_MBM(dbm)))
@@ -6728,6 +6804,185 @@ static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
return true;
}
+static int nl80211_fill_link_station(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev,
+ struct link_station_info *link_sinfo)
+{
+ struct nlattr *bss_param, *link_sinfoattr;
+
+#define PUT_LINK_SINFO(attr, memb, type) do { \
+ BUILD_BUG_ON(sizeof(type) == sizeof(u64)); \
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) && \
+ nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr, \
+ link_sinfo->memb)) \
+ goto nla_put_failure; \
+ } while (0)
+#define PUT_LINK_SINFO_U64(attr, memb) do { \
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) && \
+ nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr, \
+ link_sinfo->memb, NL80211_STA_INFO_PAD)) \
+ goto nla_put_failure; \
+ } while (0)
+
+ link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
+ if (!link_sinfoattr)
+ goto nla_put_failure;
+
+ PUT_LINK_SINFO(INACTIVE_TIME, inactive_time, u32);
+
+ if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
+ BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) &&
+ nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
+ (u32)link_sinfo->rx_bytes))
+ goto nla_put_failure;
+
+ if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
+ BIT_ULL(NL80211_STA_INFO_TX_BYTES64)) &&
+ nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
+ (u32)link_sinfo->tx_bytes))
+ goto nla_put_failure;
+
+ PUT_LINK_SINFO_U64(RX_BYTES64, rx_bytes);
+ PUT_LINK_SINFO_U64(TX_BYTES64, tx_bytes);
+ PUT_LINK_SINFO_U64(RX_DURATION, rx_duration);
+ PUT_LINK_SINFO_U64(TX_DURATION, tx_duration);
+
+ if (wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+ PUT_LINK_SINFO(AIRTIME_WEIGHT, airtime_weight, u16);
+
+ switch (rdev->wiphy.signal_type) {
+ case CFG80211_SIGNAL_TYPE_MBM:
+ PUT_LINK_SINFO(SIGNAL, signal, u8);
+ PUT_LINK_SINFO(SIGNAL_AVG, signal_avg, u8);
+ break;
+ default:
+ break;
+ }
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) {
+ if (!nl80211_put_signal(msg, link_sinfo->chains,
+ link_sinfo->chain_signal,
+ NL80211_STA_INFO_CHAIN_SIGNAL))
+ goto nla_put_failure;
+ }
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
+ if (!nl80211_put_signal(msg, link_sinfo->chains,
+ link_sinfo->chain_signal_avg,
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
+ goto nla_put_failure;
+ }
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) {
+ if (!nl80211_put_sta_rate(msg, &link_sinfo->txrate,
+ NL80211_STA_INFO_TX_BITRATE))
+ goto nla_put_failure;
+ }
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) {
+ if (!nl80211_put_sta_rate(msg, &link_sinfo->rxrate,
+ NL80211_STA_INFO_RX_BITRATE))
+ goto nla_put_failure;
+ }
+
+ PUT_LINK_SINFO(RX_PACKETS, rx_packets, u32);
+ PUT_LINK_SINFO(TX_PACKETS, tx_packets, u32);
+ PUT_LINK_SINFO(TX_RETRIES, tx_retries, u32);
+ PUT_LINK_SINFO(TX_FAILED, tx_failed, u32);
+ PUT_LINK_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32);
+ PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32);
+
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
+ bss_param = nla_nest_start_noflag(msg,
+ NL80211_STA_INFO_BSS_PARAM);
+ if (!bss_param)
+ goto nla_put_failure;
+
+ if (((link_sinfo->bss_param.flags &
+ BSS_PARAM_FLAGS_CTS_PROT) &&
+ nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
+ ((link_sinfo->bss_param.flags &
+ BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
+ nla_put_flag(msg,
+ NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
+ ((link_sinfo->bss_param.flags &
+ BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
+ nla_put_flag(msg,
+ NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
+ nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+ link_sinfo->bss_param.dtim_period) ||
+ nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+ link_sinfo->bss_param.beacon_interval))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, bss_param);
+ }
+
+ PUT_LINK_SINFO_U64(RX_DROP_MISC, rx_dropped_misc);
+ PUT_LINK_SINFO_U64(BEACON_RX, rx_beacon);
+ PUT_LINK_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
+ PUT_LINK_SINFO(RX_MPDUS, rx_mpdu_count, u32);
+ PUT_LINK_SINFO(FCS_ERROR_COUNT, fcs_err_count, u32);
+ if (wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT)) {
+ PUT_LINK_SINFO(ACK_SIGNAL, ack_signal, u8);
+ PUT_LINK_SINFO(ACK_SIGNAL_AVG, avg_ack_signal, s8);
+ }
+
+#undef PUT_LINK_SINFO
+#undef PUT_LINK_SINFO_U64
+
+ if (link_sinfo->pertid) {
+ struct nlattr *tidsattr;
+ int tid;
+
+ tidsattr = nla_nest_start_noflag(msg,
+ NL80211_STA_INFO_TID_STATS);
+ if (!tidsattr)
+ goto nla_put_failure;
+
+ for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
+ struct cfg80211_tid_stats *tidstats;
+ struct nlattr *tidattr;
+
+ tidstats = &link_sinfo->pertid[tid];
+
+ if (!tidstats->filled)
+ continue;
+
+ tidattr = nla_nest_start_noflag(msg, tid + 1);
+ if (!tidattr)
+ goto nla_put_failure;
+
+#define PUT_TIDVAL_U64(attr, memb) do { \
+ if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) && \
+ nla_put_u64_64bit(msg, NL80211_TID_STATS_ ## attr, \
+ tidstats->memb, NL80211_TID_STATS_PAD)) \
+ goto nla_put_failure; \
+ } while (0)
+
+ PUT_TIDVAL_U64(RX_MSDU, rx_msdu);
+ PUT_TIDVAL_U64(TX_MSDU, tx_msdu);
+ PUT_TIDVAL_U64(TX_MSDU_RETRIES, tx_msdu_retries);
+ PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed);
+
+#undef PUT_TIDVAL_U64
+ if ((tidstats->filled &
+ BIT(NL80211_TID_STATS_TXQ_STATS)) &&
+ !nl80211_put_txq_stats(msg, &tidstats->txq_stats,
+ NL80211_TID_STATS_TXQ_STATS))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, tidattr);
+ }
+
+ nla_nest_end(msg, tidsattr);
+ }
+
+ nla_nest_end(msg, link_sinfoattr);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
u32 seq, int flags,
struct cfg80211_registered_device *rdev,
@@ -6736,6 +6991,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
{
void *hdr;
struct nlattr *sinfoattr, *bss_param;
+ struct link_station_info *link_sinfo;
+ struct nlattr *links, *link;
+ int link_id;
hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
if (!hdr) {
@@ -6950,6 +7208,40 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
goto nla_put_failure;
}
+ if (sinfo->valid_links) {
+ links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+ if (!links)
+ goto nla_put_failure;
+
+ for_each_valid_link(sinfo, link_id) {
+ link_sinfo = sinfo->links[link_id];
+
+ if (WARN_ON_ONCE(!link_sinfo))
+ continue;
+
+ if (!is_valid_ether_addr(link_sinfo->addr))
+ continue;
+
+ link = nla_nest_start(msg, link_id + 1);
+ if (!link)
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ link_id))
+ goto nla_put_failure;
+
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ link_sinfo->addr))
+ goto nla_put_failure;
+
+ if (nl80211_fill_link_station(msg, rdev, link_sinfo))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, link);
+ }
+ nla_nest_end(msg, links);
+ }
+
cfg80211_sinfo_release_content(sinfo);
genlmsg_end(msg, hdr);
return 0;
@@ -6960,6 +7252,194 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
return -EMSGSIZE;
}
+static void cfg80211_sta_set_mld_sinfo(struct station_info *sinfo)
+{
+ struct link_station_info *link_sinfo;
+ int link_id, init = 0;
+ u32 link_inactive_time;
+
+ sinfo->signal = -99;
+
+ for_each_valid_link(sinfo, link_id) {
+ link_sinfo = sinfo->links[link_id];
+ if (!link_sinfo)
+ continue;
+
+ if ((link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_TX_PACKETS))) {
+ sinfo->tx_packets += link_sinfo->tx_packets;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
+ }
+
+ if ((link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_RX_PACKETS))) {
+ sinfo->rx_packets += link_sinfo->rx_packets;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
+ }
+
+ if (link_sinfo->filled &
+ (BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
+ BIT_ULL(NL80211_STA_INFO_TX_BYTES64))) {
+ sinfo->tx_bytes += link_sinfo->tx_bytes;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
+ }
+
+ if (link_sinfo->filled &
+ (BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
+ BIT_ULL(NL80211_STA_INFO_TX_BYTES64))) {
+ sinfo->rx_bytes += link_sinfo->rx_bytes;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
+ }
+
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_TX_RETRIES)) {
+ sinfo->tx_retries += link_sinfo->tx_retries;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+ }
+
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_FAILED)) {
+ sinfo->tx_failed += link_sinfo->tx_failed;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+ }
+
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC)) {
+ sinfo->rx_dropped_misc += link_sinfo->rx_dropped_misc;
+ sinfo->filled |=
+ BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC);
+ }
+
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_BEACON_LOSS)) {
+ sinfo->beacon_loss_count +=
+ link_sinfo->beacon_loss_count;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_LOSS);
+ }
+
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT)) {
+ sinfo->expected_throughput +=
+ link_sinfo->expected_throughput;
+ sinfo->filled |=
+ BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT);
+ }
+
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_MPDUS)) {
+ sinfo->rx_mpdu_count += link_sinfo->rx_mpdu_count;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_MPDUS);
+ }
+
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_FCS_ERROR_COUNT)) {
+ sinfo->fcs_err_count += link_sinfo->fcs_err_count;
+ sinfo->filled |=
+ BIT_ULL(NL80211_STA_INFO_FCS_ERROR_COUNT);
+ }
+
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_BEACON_RX)) {
+ sinfo->rx_beacon += link_sinfo->rx_beacon;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX);
+ }
+
+ /* Update MLO signal, signal_avg as best among links */
+ if ((link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_SIGNAL)) &&
+ link_sinfo->signal > sinfo->signal) {
+ sinfo->signal = link_sinfo->signal;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
+ }
+
+ if ((link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG)) &&
+ link_sinfo->signal_avg > sinfo->signal_avg) {
+ sinfo->signal_avg = link_sinfo->signal_avg;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
+ }
+
+ /* Update MLO inactive_time, bss_param based on least
+ * value for corresponding field of link.
+ */
+ if ((link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME)) &&
+ (!init ||
+ link_inactive_time > link_sinfo->inactive_time)) {
+ link_inactive_time = link_sinfo->inactive_time;
+ sinfo->inactive_time = link_sinfo->inactive_time;
+ sinfo->filled |= NL80211_STA_INFO_INACTIVE_TIME;
+ }
+
+ if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM) &&
+ (!init ||
+ sinfo->bss_param.dtim_period >
+ link_sinfo->bss_param.dtim_period)) {
+ sinfo->bss_param.dtim_period =
+ link_sinfo->bss_param.dtim_period;
+ sinfo->filled |= NL80211_STA_BSS_PARAM_DTIM_PERIOD;
+ sinfo->bss_param.beacon_interval =
+ link_sinfo->bss_param.beacon_interval;
+ sinfo->filled |= NL80211_STA_BSS_PARAM_BEACON_INTERVAL;
+ }
+
+ /* Update MLO rates as per last updated link rate */
+ if ((link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) &&
+ (!init ||
+ link_inactive_time > link_sinfo->inactive_time)) {
+ sinfo->txrate = link_sinfo->txrate;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+ }
+ if ((link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) &&
+ (!init ||
+ link_inactive_time > link_sinfo->inactive_time)) {
+ sinfo->rxrate = link_sinfo->rxrate;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+ }
+
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_TX_DURATION) &&
+ (!init ||
+ link_inactive_time > link_sinfo->inactive_time)) {
+ sinfo->tx_duration += link_sinfo->tx_duration;
+ sinfo->filled |=
+ BIT_ULL(NL80211_STA_INFO_TX_DURATION);
+ }
+ if (link_sinfo->filled &
+ BIT_ULL(NL80211_STA_INFO_RX_DURATION) &&
+ (!init ||
+ link_inactive_time > link_sinfo->inactive_time)) {
+ sinfo->rx_duration += link_sinfo->rx_duration;
+ sinfo->filled |=
+ BIT_ULL(NL80211_STA_INFO_RX_DURATION);
+ }
+ init++;
+
+ /* pertid stats accumulate for rx/tx fields */
+ if (sinfo->pertid) {
+ sinfo->pertid->rx_msdu +=
+ link_sinfo->pertid->rx_msdu;
+ sinfo->pertid->tx_msdu +=
+ link_sinfo->pertid->tx_msdu;
+ sinfo->pertid->tx_msdu_retries +=
+ link_sinfo->pertid->tx_msdu_retries;
+ sinfo->pertid->tx_msdu_failed +=
+ link_sinfo->pertid->tx_msdu_failed;
+
+ sinfo->pertid->filled |=
+ BIT(NL80211_TID_STATS_RX_MSDU) |
+ BIT(NL80211_TID_STATS_TX_MSDU) |
+ BIT(NL80211_TID_STATS_TX_MSDU_RETRIES) |
+ BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
+ }
+ }
+
+ /* Reset sinfo->filled bits to exclude fields which don't make
+ * much sense at the MLO level.
+ */
+ sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
+ sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
+}
+
static int nl80211_dump_station(struct sk_buff *skb,
struct netlink_callback *cb)
{
@@ -6968,7 +7448,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
struct wireless_dev *wdev;
u8 mac_addr[ETH_ALEN];
int sta_idx = cb->args[2];
- int err;
+ int err, i;
err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL);
if (err)
@@ -6988,6 +7468,16 @@ static int nl80211_dump_station(struct sk_buff *skb,
while (1) {
memset(&sinfo, 0, sizeof(sinfo));
+
+ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+ sinfo.links[i] =
+ kzalloc(sizeof(*sinfo.links[0]), GFP_KERNEL);
+ if (!sinfo.links[i]) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+ }
+
err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
mac_addr, &sinfo);
if (err == -ENOENT)
@@ -6995,6 +7485,9 @@ static int nl80211_dump_station(struct sk_buff *skb,
if (err)
goto out_err;
+ if (sinfo.valid_links)
+ cfg80211_sta_set_mld_sinfo(&sinfo);
+
if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -7009,6 +7502,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
cb->args[2] = sta_idx;
err = skb->len;
out_err:
+ cfg80211_sinfo_release_content(&sinfo);
wiphy_unlock(&rdev->wiphy);
return err;
@@ -7021,7 +7515,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
struct station_info sinfo;
struct sk_buff *msg;
u8 *mac_addr = NULL;
- int err;
+ int err, i;
memset(&sinfo, 0, sizeof(sinfo));
@@ -7033,9 +7527,19 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->get_station)
return -EOPNOTSUPP;
+ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+ sinfo.links[i] = kzalloc(sizeof(*sinfo.links[0]), GFP_KERNEL);
+ if (!sinfo.links[i]) {
+ cfg80211_sinfo_release_content(&sinfo);
+ return -ENOMEM;
+ }
+ }
+
err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
- if (err)
+ if (err) {
+ cfg80211_sinfo_release_content(&sinfo);
return err;
+ }
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg) {
@@ -7043,6 +7547,9 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
return -ENOMEM;
}
+ if (sinfo.valid_links)
+ cfg80211_sta_set_mld_sinfo(&sinfo);
+
if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
info->snd_portid, info->snd_seq, 0,
rdev, dev, mac_addr, &sinfo) < 0) {
@@ -7349,6 +7856,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,
}
}
+ if (info->attrs[NL80211_ATTR_S1G_CAPABILITY])
+ params->link_sta_params.s1g_capa =
+ nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY]);
+
err = nl80211_parse_sta_channel_info(info, params);
if (err)
return err;
@@ -7675,6 +8186,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.link_sta_params.he_6ghz_capa =
nla_data(info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_S1G_CAPABILITY])
+ params.link_sta_params.s1g_capa =
+ nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY]);
+
if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
params.link_sta_params.opmode_notif_used = true;
params.link_sta_params.opmode_notif =