diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 60c5864bb8e3..fd695aa28db1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3960,6 +3960,16 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, u32 num_packets, u32 rate, u32 intvl, gfp_t gfp); +/** + * cfg80211_roaming_status - notify userspace about changes in the driver + * ability to support roaming + * @dev: network device + * @enabled: indicates whether roaming is supported at the current time + * @gfp: allocation flags + */ +void cfg80211_roaming_status(struct net_device *dev, + bool enabled, gfp_t gfp); + /** * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying * @dev: network device diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4381530d4e55..f2b965fd4871 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3254,6 +3254,17 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, */ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); +/** + * ieee80211_roaming_status - report if roaming support by the driver changed + * + * Some drivers have limitations on roaming in certain conditions (e.g. multi + * role) and need to report this back to userspace. + * + * @vif: interface + * @enabled: is roaming supported + */ +void ieee80211_roaming_status(struct ieee80211_vif *vif, bool enabled); + /** * ieee80211_beacon_get_tim - beacon generation function * @hw: pointer obtained from ieee80211_alloc_hw(). diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9a2a7bb116df..bf7424a97f18 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -659,6 +659,11 @@ * On reception of this notification, userspace may decide to stop earlier * currently running scan with (@NL80211_CMD_SCAN_CANCEL). * + * @NL80211_CMD_ROAMING_SUPPORT: A notify event used to alert userspace + * regarding changes in roaming support by the driver. If roaming is + * disabled (marked by the presence of @NL80211_ATTR_ROAMING_DISABLED flag) + * userspace should disable background scans and roaming attempts. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -825,6 +830,8 @@ enum nl80211_commands { NL80211_CMD_IM_SCAN_RESULT, + NL80211_CMD_ROAMING_SUPPORT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1478,6 +1485,8 @@ enum nl80211_commands { * each short interval scheduled scan cycle in msecs. * @NL80211_ATTR_SCHED_SCAN_NUM_SHORT_INTERVALS: number of short * sched scan intervals before switching to the long interval + * @NL80211_ATTR_ROAMING_DISABLED: indicates that the driver can't do roaming + * currently. * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -1787,6 +1796,8 @@ enum nl80211_attrs { NL80211_ATTR_SCHED_SCAN_SHORT_INTERVAL, NL80211_ATTR_SCHED_SCAN_NUM_SHORT_INTERVALS, + NL80211_ATTR_ROAMING_DISABLED, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 43439203f4e4..d214d92a55c7 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -672,6 +672,14 @@ void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) } EXPORT_SYMBOL(ieee80211_report_low_ack); + +void ieee80211_roaming_status(struct ieee80211_vif *vif, bool enabled) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + cfg80211_roaming_status(sdata->dev, enabled, GFP_KERNEL); +} +EXPORT_SYMBOL(ieee80211_roaming_status); + void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b806e8647151..107c45c3d056 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -371,6 +371,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_SCAN_NUM_PROBE] = { .type = NLA_U8 }, [NL80211_ATTR_SCHED_SCAN_SHORT_INTERVAL] = { .type = NLA_U32 }, [NL80211_ATTR_SCHED_SCAN_NUM_SHORT_INTERVALS] = { .type = NLA_U8 }, + [NL80211_ATTR_ROAMING_DISABLED] = { .type = NLA_FLAG }, [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, @@ -10560,6 +10561,50 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, } EXPORT_SYMBOL(cfg80211_probe_status); + +void cfg80211_roaming_status(struct net_device *dev, bool enabled, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAMING_SUPPORT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex)) + goto nla_put_failure; + + if (!enabled) + if (nla_put_flag(msg, NL80211_ATTR_ROAMING_DISABLED)) + goto nla_put_failure; + + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_roaming_status); + + void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, int freq, int sig_dbm)