};
static const struct genl_multicast_group nl80211_mcgrps[] = {
- [NL80211_MCGRP_CONFIG] = { .name = "config", },
- [NL80211_MCGRP_SCAN] = { .name = "scan", },
- [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
- [NL80211_MCGRP_MLME] = { .name = "mlme", },
- [NL80211_MCGRP_VENDOR] = { .name = "vendor", },
+ [NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },
+ [NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN },
+ [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
+ [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
+ [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
#ifdef CONFIG_NL80211_TESTMODE
- [NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
+ [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
#endif
};
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
+ [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
rdev->wiphy.max_num_csa_counters))
goto nla_put_failure;
+ if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
+
+ if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
+ sizeof(rdev->wiphy.ext_features),
+ rdev->wiphy.ext_features))
+ goto nla_put_failure;
+
/* done */
state->split_start = 0;
break;
static int nl80211_send_chandef(struct sk_buff *msg,
const struct cfg80211_chan_def *chandef)
{
- WARN_ON(!cfg80211_chandef_valid(chandef));
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return -EINVAL;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
chandef->chan->center_freq))
return err;
}
-static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
+ struct sk_buff *msg)
{
- const struct ieee80211_regdomain *regdom;
- struct sk_buff *msg;
- void *hdr = NULL;
struct nlattr *nl_reg_rules;
unsigned int i;
- if (!cfg80211_regdomain)
- return -EINVAL;
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOBUFS;
-
- hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
- NL80211_CMD_GET_REG);
- if (!hdr)
- goto put_failure;
-
- if (reg_last_request_cell_base() &&
- nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
- NL80211_USER_REG_HINT_CELL_BASE))
- goto nla_put_failure;
-
- rcu_read_lock();
- regdom = rcu_dereference(cfg80211_regdomain);
-
if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
(regdom->dfs_region &&
nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
- goto nla_put_failure_rcu;
+ goto nla_put_failure;
nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
if (!nl_reg_rules)
- goto nla_put_failure_rcu;
+ goto nla_put_failure;
for (i = 0; i < regdom->n_reg_rules; i++) {
struct nlattr *nl_reg_rule;
nl_reg_rule = nla_nest_start(msg, i);
if (!nl_reg_rule)
- goto nla_put_failure_rcu;
+ goto nla_put_failure;
max_bandwidth_khz = freq_range->max_bandwidth_khz;
if (!max_bandwidth_khz)
power_rule->max_eirp) ||
nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
reg_rule->dfs_cac_ms))
- goto nla_put_failure_rcu;
+ goto nla_put_failure;
nla_nest_end(msg, nl_reg_rule);
}
- rcu_read_unlock();
nla_nest_end(msg, nl_reg_rules);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
+{
+ const struct ieee80211_regdomain *regdom = NULL;
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy = NULL;
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOBUFS;
+
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+ NL80211_CMD_GET_REG);
+ if (!hdr)
+ goto put_failure;
+
+ if (info->attrs[NL80211_ATTR_WIPHY]) {
+ bool self_managed;
+
+ rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+ if (IS_ERR(rdev)) {
+ nlmsg_free(msg);
+ return PTR_ERR(rdev);
+ }
+
+ wiphy = &rdev->wiphy;
+ self_managed = wiphy->regulatory_flags &
+ REGULATORY_WIPHY_SELF_MANAGED;
+ regdom = get_wiphy_regdom(wiphy);
+
+ /* a self-managed-reg device must have a private regdom */
+ if (WARN_ON(!regdom && self_managed)) {
+ nlmsg_free(msg);
+ return -EINVAL;
+ }
+
+ if (regdom &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+ goto nla_put_failure;
+ }
+
+ if (!wiphy && reg_last_request_cell_base() &&
+ nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+ NL80211_USER_REG_HINT_CELL_BASE))
+ goto nla_put_failure;
+
+ rcu_read_lock();
+
+ if (!regdom)
+ regdom = rcu_dereference(cfg80211_regdomain);
+
+ if (nl80211_put_regdom(regdom, msg))
+ goto nla_put_failure_rcu;
+
+ rcu_read_unlock();
genlmsg_end(msg, hdr);
return genlmsg_reply(msg, info);
return -EMSGSIZE;
}
+static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
+ u32 seq, int flags, struct wiphy *wiphy,
+ const struct ieee80211_regdomain *regdom)
+{
+ void *hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
+ NL80211_CMD_GET_REG);
+
+ if (!hdr)
+ return -1;
+
+ genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
+ if (nl80211_put_regdom(regdom, msg))
+ goto nla_put_failure;
+
+ if (!wiphy && reg_last_request_cell_base() &&
+ nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+ NL80211_USER_REG_HINT_CELL_BASE))
+ goto nla_put_failure;
+
+ if (wiphy &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+ goto nla_put_failure;
+
+ if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_dump(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ const struct ieee80211_regdomain *regdom = NULL;
+ struct cfg80211_registered_device *rdev;
+ int err, reg_idx, start = cb->args[2];
+
+ rtnl_lock();
+
+ if (cfg80211_regdomain && start == 0) {
+ err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+ NLM_F_MULTI, NULL,
+ rtnl_dereference(cfg80211_regdomain));
+ if (err < 0)
+ goto out_err;
+ }
+
+ /* the global regdom is idx 0 */
+ reg_idx = 1;
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ regdom = get_wiphy_regdom(&rdev->wiphy);
+ if (!regdom)
+ continue;
+
+ if (++reg_idx <= start)
+ continue;
+
+ err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+ NLM_F_MULTI, &rdev->wiphy, regdom);
+ if (err < 0) {
+ reg_idx--;
+ break;
+ }
+ }
+
+ cb->args[2] = reg_idx;
+ err = skb->len;
+out_err:
+ rtnl_unlock();
+ return err;
+}
+
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
struct nlattr *nl_reg_rule;
- char *alpha2 = NULL;
- int rem_reg_rules = 0, r = 0;
+ char *alpha2;
+ int rem_reg_rules, r;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
- struct ieee80211_regdomain *rd = NULL;
+ struct ieee80211_regdomain *rd;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
}
/* there was no other matchset, so the RSSI one is alone */
- if (i == 0)
+ if (i == 0 && n_match_sets)
request->match_sets[0].rssi_thold = default_match_rssi;
request->min_rssi_thold = INT_MAX;
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_sched_scan_request *sched_scan_req;
int err;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
if (rdev->sched_scan_req)
return -EINPROGRESS;
- rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
- info->attrs);
- err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+ sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+ info->attrs);
+
+ err = PTR_ERR_OR_ZERO(sched_scan_req);
if (err)
goto out_err;
- err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+ err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
if (err)
goto out_free;
- rdev->sched_scan_req->dev = dev;
- rdev->sched_scan_req->wiphy = &rdev->wiphy;
+ sched_scan_req->dev = dev;
+ sched_scan_req->wiphy = &rdev->wiphy;
+
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+ sched_scan_req->owner_nlportid = info->snd_portid;
+
+ rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req);
nl80211_send_sched_scan(rdev, dev,
NL80211_CMD_START_SCHED_SCAN);
return 0;
out_free:
- kfree(rdev->sched_scan_req);
+ kfree(sched_scan_req);
out_err:
- rdev->sched_scan_req = NULL;
return err;
}
}
static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
- int flags, struct net_device *dev,
- struct survey_info *survey)
+ int flags, struct net_device *dev,
+ bool allow_radio_stats,
+ struct survey_info *survey)
{
void *hdr;
struct nlattr *infoattr;
+ /* skip radio stats if userspace didn't request them */
+ if (!survey->channel && !allow_radio_stats)
+ return 0;
+
hdr = nl80211hdr_put(msg, portid, seq, flags,
NL80211_CMD_NEW_SURVEY_RESULTS);
if (!hdr)
if (!infoattr)
goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
+ if (survey->channel &&
+ nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
survey->channel->center_freq))
goto nla_put_failure;
if ((survey->filled & SURVEY_INFO_IN_USE) &&
nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
goto nla_put_failure;
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
- survey->channel_time))
+ if ((survey->filled & SURVEY_INFO_TIME) &&
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME,
+ survey->time))
goto nla_put_failure;
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
- survey->channel_time_busy))
+ if ((survey->filled & SURVEY_INFO_TIME_BUSY) &&
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_BUSY,
+ survey->time_busy))
goto nla_put_failure;
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
- survey->channel_time_ext_busy))
+ if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) &&
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+ survey->time_ext_busy))
goto nla_put_failure;
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
- survey->channel_time_rx))
+ if ((survey->filled & SURVEY_INFO_TIME_RX) &&
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_RX,
+ survey->time_rx))
goto nla_put_failure;
- if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
- nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
- survey->channel_time_tx))
+ if ((survey->filled & SURVEY_INFO_TIME_TX) &&
+ nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_TX,
+ survey->time_tx))
goto nla_put_failure;
nla_nest_end(msg, infoattr);
return -EMSGSIZE;
}
-static int nl80211_dump_survey(struct sk_buff *skb,
- struct netlink_callback *cb)
+static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
{
struct survey_info survey;
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
int survey_idx = cb->args[2];
int res;
+ bool radio_stats;
res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
if (res)
return res;
+ /* prepare_wdev_dump parsed the attributes */
+ radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
+
if (!wdev->netdev) {
res = -EINVAL;
goto out_err;
}
while (1) {
- struct ieee80211_channel *chan;
-
res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
if (res == -ENOENT)
break;
if (res)
goto out_err;
- /* Survey without a channel doesn't make sense */
- if (!survey.channel) {
- res = -EINVAL;
- goto out;
- }
-
- chan = ieee80211_get_channel(&rdev->wiphy,
- survey.channel->center_freq);
- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+ /* don't send disabled channels, but do send non-channel data */
+ if (survey.channel &&
+ survey.channel->flags & IEEE80211_CHAN_DISABLED) {
survey_idx++;
continue;
}
if (nl80211_send_survey(skb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
- wdev->netdev, &survey) < 0)
+ wdev->netdev, radio_stats, &survey) < 0)
goto out;
survey_idx++;
}
},
{
.cmd = NL80211_CMD_GET_REG,
- .doit = nl80211_get_reg,
+ .doit = nl80211_get_reg_do,
+ .dumpit = nl80211_get_reg_dump,
.policy = nl80211_policy,
.internal_flags = NL80211_FLAG_NEED_RTNL,
/* can be retrieved by unprivileged users */
NL80211_MCGRP_SCAN, GFP_KERNEL);
}
-/*
- * This can happen on global regulatory changes or device specific settings
- * based on custom world regulatory domains.
- */
-void nl80211_send_reg_change_event(struct regulatory_request *request)
+static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
+ struct regulatory_request *request)
{
- struct sk_buff *msg;
- void *hdr;
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return;
-
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
- if (!hdr) {
- nlmsg_free(msg);
- return;
- }
-
/* Userspace can always count this one always being set */
if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
goto nla_put_failure;
goto nla_put_failure;
}
- if (request->wiphy_idx != WIPHY_IDX_INVALID &&
- nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+ if (request->wiphy_idx != WIPHY_IDX_INVALID) {
+ struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
+
+ if (wiphy &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+ goto nla_put_failure;
+
+ if (wiphy &&
+ wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
+ }
+
+ return true;
+
+nla_put_failure:
+ return false;
+}
+
+/*
+ * This can happen on global regulatory changes or device specific settings
+ * based on custom regulatory domains.
+ */
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+ struct regulatory_request *request)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nl80211_reg_change_event_fill(msg, request) == false)
goto nla_put_failure;
genlmsg_end(msg, hdr);
}
EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
-void cfg80211_cqm_rssi_notify(struct net_device *dev,
- enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp)
+static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
+ const char *mac, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
-
- trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ void **cb;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
- return;
+ return NULL;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- if (!hdr) {
+ cb = (void **)msg->cb;
+
+ cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+ if (!cb[0]) {
nlmsg_free(msg);
- return;
+ return NULL;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
goto nla_put_failure;
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
+ if (mac && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac))
goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
- rssi_event))
+ cb[1] = nla_nest_start(msg, NL80211_ATTR_CQM);
+ if (!cb[1])
goto nla_put_failure;
- nla_nest_end(msg, pinfoattr);
+ cb[2] = rdev;
- genlmsg_end(msg, hdr);
+ return msg;
+ nla_put_failure:
+ nlmsg_free(msg);
+ return NULL;
+}
+
+static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp)
+{
+ void **cb = (void **)msg->cb;
+ struct cfg80211_registered_device *rdev = cb[2];
+
+ nla_nest_end(msg, cb[1]);
+ genlmsg_end(msg, cb[0]);
+
+ memset(msg->cb, 0, sizeof(msg->cb));
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
NL80211_MCGRP_MLME, gfp);
+}
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
+ if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
+ rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
+ return;
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ rssi_event))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+
return;
nla_put_failure:
- genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets,
+ u32 rate, u32 intvl, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_flag(msg, NL80211_ATTR_CQM_BEACON_LOSS_EVENT))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
+
static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp)
}
EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
-void cfg80211_cqm_txe_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets,
- u32 rate, u32 intvl, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
-
- msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
- if (!msg)
- return;
-
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- 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) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
- goto nla_put_failure;
-
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
- goto nla_put_failure;
-
- nla_nest_end(msg, pinfoattr);
-
- genlmsg_end(msg, hdr);
-
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
- NL80211_MCGRP_MLME, gfp);
- return;
-
- nla_put_failure:
- genlmsg_cancel(msg, hdr);
- nlmsg_free(msg);
-}
-EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
-
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
const struct cfg80211_chan_def *chandef,
nlmsg_free(msg);
}
-void cfg80211_cqm_pktloss_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
-
- trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
- if (!msg)
- return;
-
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- 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) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
- goto nla_put_failure;
-
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
- goto nla_put_failure;
-
- nla_nest_end(msg, pinfoattr);
-
- genlmsg_end(msg, hdr);
-
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
- NL80211_MCGRP_MLME, gfp);
- return;
-
- nla_put_failure:
- genlmsg_cancel(msg, hdr);
- nlmsg_free(msg);
-}
-EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
-
void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
u64 cookie, bool acked, gfp_t gfp)
{
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
bool schedule_destroy_work = false;
+ bool schedule_scan_stop = false;
+ struct cfg80211_sched_scan_request *sched_scan_req =
+ rcu_dereference(rdev->sched_scan_req);
+
+ if (sched_scan_req && notify->portid &&
+ sched_scan_req->owner_nlportid == notify->portid)
+ schedule_scan_stop = true;
list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
cfg80211_mlme_unregister_socket(wdev, notify->portid);
spin_unlock(&rdev->destroy_list_lock);
schedule_work(&rdev->destroy_work);
}
+ } else if (schedule_scan_stop) {
+ sched_scan_req->owner_nlportid = 0;
+
+ if (rdev->ops->sched_scan_stop &&
+ rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+ schedule_work(&rdev->sched_scan_stop_wk);
}
}