From ae33bd817a10f39174453b754e9b548132acae4a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Jul 2012 16:25:02 +0200 Subject: nl80211: allow enabling WoWLAN without triggers It may be desirable to use WoWLAN without triggers to keep the connection alive to the AP while suspended. Allow this use by enabling WoWLAN without triggers if no triggers were requested. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6472c7f928dc..079fc49e3975 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6466,8 +6466,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; - struct cfg80211_wowlan no_triggers = {}; struct cfg80211_wowlan new_triggers = {}; + struct cfg80211_wowlan *ntrig; struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; int err, i; bool prev_enabled = rdev->wowlan; @@ -6475,8 +6475,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) return -EOPNOTSUPP; - if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) - goto no_triggers; + if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = NULL; + goto set_wakeup; + } err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), @@ -6587,22 +6590,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) } } - if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { - struct cfg80211_wowlan *ntrig; - ntrig = kmemdup(&new_triggers, sizeof(new_triggers), - GFP_KERNEL); - if (!ntrig) { - err = -ENOMEM; - goto error; - } - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = ntrig; - } else { - no_triggers: - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = NULL; + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); + if (!ntrig) { + err = -ENOMEM; + goto error; } + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = ntrig; + set_wakeup: if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan) rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan); -- cgit v1.2.3 From 5b7ccaf3fc7446e42b83a77fd7aa7ad92850acdd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Jul 2012 19:45:08 +0200 Subject: cfg80211/mac80211: re-add get_channel operation This essentially reverts commit 2e165b818456 but introduces the get_channel operation with a new wireless_dev argument so that you can retrieve the channel per interface. This is necessary as even though we can track all interface channels (except monitor) we can't track the channel type used. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 +++++++++ net/mac80211/cfg.c | 11 +++++++++++ net/wireless/nl80211.c | 16 +++++++++++----- net/wireless/wext-compat.c | 9 +++++++-- 4 files changed, 38 insertions(+), 7 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5a67165f3b19..8115d68eb603 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1612,6 +1612,10 @@ struct cfg80211_gtk_rekey_data { * @get_et_strings: Ethtool API to get a set of strings to describe stats * and perhaps other supported types of ethtool data-sets. * See @ethtool_ops.get_strings + * + * @get_channel: Get the current operating channel for the virtual interface. + * For monitor interfaces, it should return %NULL unless there's a single + * current monitoring channel. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1821,6 +1825,11 @@ struct cfg80211_ops { u32 sset, u8 *data); void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled); + + struct ieee80211_channel * + (*get_channel)(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_channel_type *type); }; /* diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e95f24eef870..10dd9631e4da 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2982,6 +2982,16 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, return 0; } +static struct ieee80211_channel * +ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, + enum nl80211_channel_type *type) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + *type = local->_oper_channel_type; + return local->oper_channel; +} + #ifdef CONFIG_PM static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled) { @@ -3062,4 +3072,5 @@ struct cfg80211_ops mac80211_config_ops = { .get_et_sset_count = ieee80211_get_et_sset_count, .get_et_stats = ieee80211_get_et_stats, .get_et_strings = ieee80211_get_et_strings, + .get_channel = ieee80211_cfg_get_channel, }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 079fc49e3975..6b001e445718 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1759,11 +1759,17 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, (cfg80211_rdev_list_generation << 2))) goto nla_put_failure; - if (rdev->monitor_channel) { - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, - rdev->monitor_channel->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - rdev->monitor_channel_type)) + if (rdev->ops->get_channel) { + struct ieee80211_channel *chan; + enum nl80211_channel_type channel_type; + + chan = rdev->ops->get_channel(&rdev->wiphy, wdev, + &channel_type); + if (chan && + (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + chan->center_freq) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + channel_type))) goto nla_put_failure; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 7df42f541873..494379eb464f 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -827,6 +827,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan; + enum nl80211_channel_type channel_type; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -834,10 +836,13 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); case NL80211_IFTYPE_MONITOR: - if (!rdev->monitor_channel) + if (!rdev->ops->get_channel) return -EINVAL; - freq->m = rdev->monitor_channel->center_freq; + chan = rdev->ops->get_channel(wdev->wiphy, wdev, &channel_type); + if (!chan) + return -EINVAL; + freq->m = chan->center_freq; freq->e = 6; return 0; default: -- cgit v1.2.3 From 00f5335079689cd65a9430b5df8974dc35c7914b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 17 Jul 2012 11:53:12 +0200 Subject: nl80211: add wdev ID as u64 as it should In one of my previous patches I erroneously used nla_put_u32 for the wdev_id, fix that to use nla_put_u64. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/wireless/nl80211.c') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6b001e445718..be8750f91d78 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8101,7 +8101,7 @@ static void nl80211_send_remain_on_chan_event( if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) || - nla_put_u32(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) -- cgit v1.2.3 From 84f10708f73254878246772cead70a2eb6a123f2 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 12 Jul 2012 16:17:33 -0700 Subject: cfg80211: support TX error rate CQM Let the user configure serveral TX error conection quality monitoring parameters: % error rate, survey interval, and # of attempted packets. On exceeding the TX failure rate over the given interval, the driver will send a CQM notify event with the actual TX failure rate and packets attempted. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 16 ++++++++++ include/net/cfg80211.h | 21 ++++++++++++ net/wireless/mlme.c | 13 ++++++++ net/wireless/nl80211.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 5 +++ 5 files changed, 140 insertions(+) (limited to 'net/wireless/nl80211.c') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e791487ead37..d6cfacc3ce4d 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1550,6 +1550,8 @@ enum nl80211_attrs { /* default RSSI threshold for scan results if none specified. */ #define NL80211_SCAN_RSSI_THOLD_OFF -300 +#define NL80211_CQM_TXE_MAX_INTVL 1800 + /** * enum nl80211_iftype - (virtual) interface types * @@ -2589,6 +2591,17 @@ enum nl80211_ps_state { * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many * consecutive packets were not acknowledged by the peer + * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures + * during the given %NL80211_ATTR_CQM_TXE_INTVL before an + * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and + * %NL80211_ATTR_CQM_TXE_PKTS is generated. + * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given + * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is + * checked. + * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic + * interval in which %NL80211_ATTR_CQM_TXE_PKTS and + * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an + * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -2598,6 +2611,9 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, NL80211_ATTR_CQM_PKT_LOSS_EVENT, + NL80211_ATTR_CQM_TXE_RATE, + NL80211_ATTR_CQM_TXE_PKTS, + NL80211_ATTR_CQM_TXE_INTVL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0245208c2978..493fa0c79005 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1573,6 +1573,8 @@ struct cfg80211_gtk_rekey_data { * @set_power_mgmt: Configure WLAN power management. A timeout value of -1 * allows the driver to adjust the dynamic ps timeout value. * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. + * @set_cqm_txe_config: Configure connection quality monitor TX error + * thresholds. * @sched_scan_start: Tell the driver to start a scheduled scan. * @sched_scan_stop: Tell the driver to stop an ongoing scheduled * scan. The driver_initiated flag specifies whether the driver @@ -1783,6 +1785,10 @@ struct cfg80211_ops { struct net_device *dev, s32 rssi_thold, u32 rssi_hyst); + int (*set_cqm_txe_config)(struct wiphy *wiphy, + struct net_device *dev, + u32 rate, u32 pkts, u32 intvl); + void (*mgmt_frame_register)(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg); @@ -3395,6 +3401,21 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, void cfg80211_cqm_pktloss_notify(struct net_device *dev, const u8 *peer, u32 num_packets, gfp_t gfp); +/** + * cfg80211_cqm_txe_notify - TX error rate event + * @dev: network device + * @peer: peer's MAC address + * @num_packets: how many packets were lost + * @rate: % of packets which failed transmission + * @intvl: interval (in s) over which the TX failure threshold was breached. + * @gfp: context flags + * + * Notify userspace when configured % TX failures over number of packets in a + * given interval is exceeded. + */ +void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, + u32 num_packets, u32 rate, u32 intvl, gfp_t gfp); + /** * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying * @dev: network device diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index abe9f82d5a82..1cdb1d5e6b0f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -919,6 +919,19 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, } EXPORT_SYMBOL(cfg80211_cqm_pktloss_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_dev(wiphy); + + nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets, + rate, intvl, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_txe_notify); + void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index be8750f91d78..9216e45e53a0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6267,8 +6267,35 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, }; +static int nl80211_set_cqm_txe(struct genl_info *info, + u32 rate, u32 pkts, u32 intvl) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev; + struct net_device *dev = info->user_ptr[1]; + + if ((rate < 0 || rate > 100) || + (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL)) + return -EINVAL; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_cqm_txe_config) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev, + rate, pkts, intvl); +} + static int nl80211_set_cqm_rssi(struct genl_info *info, s32 threshold, u32 hysteresis) { @@ -6316,6 +6343,14 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); err = nl80211_set_cqm_rssi(info, threshold, hysteresis); + } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] && + attrs[NL80211_ATTR_CQM_TXE_PKTS] && + attrs[NL80211_ATTR_CQM_TXE_INTVL]) { + u32 rate, pkts, intvl; + rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]); + pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]); + intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]); + err = nl80211_set_cqm_txe(info, rate, pkts, intvl); } else err = -EINVAL; @@ -8495,6 +8530,56 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, u32 rate, u32 intvl, gfp_t gfp) +{ + 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, netdev->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(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 89ce99675e61..9f2616fffb40 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -110,6 +110,11 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, u32 num_packets, gfp_t gfp); +void +nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, u32 rate, u32 intvl, gfp_t gfp); + void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); -- cgit v1.2.3 From 57b5ce072e7361218a8e2ea1d62960cbb71d9cff Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 12 Jul 2012 11:49:18 -0700 Subject: cfg80211: add cellular base station regulatory hint support Cellular base stations can provide hints to cfg80211 about where they think we are. This can be done for example on a cell phone. To enable these hints we simply allow them through as user regulatory hints but we allow userspace to clasify the hint as either coming directly from the user or coming from a cellular base station. This option is only available when you enable CONFIG_CFG80211_CERTIFICATION_ONUS. The base station hints themselves will not be processed by the core unless at least one device on the system supports this feature. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 32 ++++++++++++++ include/net/regulatory.h | 5 +++ net/wireless/core.c | 1 + net/wireless/nl80211.c | 23 +++++++++- net/wireless/reg.c | 113 ++++++++++++++++++++++++++++++++++++++++++++--- net/wireless/reg.h | 5 ++- 6 files changed, 171 insertions(+), 8 deletions(-) (limited to 'net/wireless/nl80211.c') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index d6cfacc3ce4d..2f3878806403 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1245,6 +1245,12 @@ enum nl80211_commands { * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds * or 0 to disable background scan. * + * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from + * userspace. If unset it is assumed the hint comes directly from + * a user. If set code could specify exactly what type of source + * was used to provide the hint. For the different types of + * allowed user regulatory hints see nl80211_user_reg_hint_type. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1498,6 +1504,8 @@ enum nl80211_attrs { NL80211_ATTR_WDEV, + NL80211_ATTR_USER_REG_HINT_TYPE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2060,6 +2068,26 @@ enum nl80211_dfs_regions { NL80211_DFS_JP = 3, }; +/** + * enum nl80211_user_reg_hint_type - type of user regulatory hint + * + * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always + * assumed if the attribute is not set. + * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular + * base station. Device drivers that have been tested to work + * properly to support this type of hint can enable these hints + * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature + * capability on the struct wiphy. The wireless core will + * ignore all cell base station hints until at least one device + * present has been registered with the wireless core that + * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a + * supported feature. + */ +enum nl80211_user_reg_hint_type { + NL80211_USER_REG_HINT_USER = 0, + NL80211_USER_REG_HINT_CELL_BASE = 1, +}; + /** * enum nl80211_survey_info - survey information * @@ -2963,11 +2991,15 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up * the connected inactive stations in AP mode. + * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested + * to work properly to suppport receiving regulatory hints from + * cellular base stations. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, }; /** diff --git a/include/net/regulatory.h b/include/net/regulatory.h index a5f79933e211..7dcaa2794fde 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -52,6 +52,10 @@ enum environment_cap { * DFS master operation on a known DFS region (NL80211_DFS_*), * dfs_region represents that region. Drivers can use this and the * @alpha2 to adjust their device's DFS parameters as required. + * @user_reg_hint_type: if the @initiator was of type + * %NL80211_REGDOM_SET_BY_USER, this classifies the type + * of hint passed. This could be any of the %NL80211_USER_REG_HINT_* + * types. * @intersect: indicates whether the wireless core should intersect * the requested regulatory domain with the presently set regulatory * domain. @@ -70,6 +74,7 @@ enum environment_cap { struct regulatory_request { int wiphy_idx; enum nl80211_reg_initiator initiator; + enum nl80211_user_reg_hint_type user_reg_hint_type; char alpha2[2]; u8 dfs_region; bool intersect; diff --git a/net/wireless/core.c b/net/wireless/core.c index 71b684b5a675..c0307b05986c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -542,6 +542,7 @@ int wiphy_register(struct wiphy *wiphy) } /* set up regulatory info */ + wiphy_regulatory_register(wiphy); regulatory_update(wiphy, NL80211_REGDOM_SET_BY_CORE); list_add_rcu(&rdev->list, &cfg80211_rdev_list); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9216e45e53a0..50b1a0e84f1a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -354,6 +354,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, + [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -3582,6 +3583,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) { int r; char *data = NULL; + enum nl80211_user_reg_hint_type user_reg_hint_type; /* * You should only get this when cfg80211 hasn't yet initialized @@ -3601,7 +3603,21 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); - r = regulatory_hint_user(data); + if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) + user_reg_hint_type = + nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); + else + user_reg_hint_type = NL80211_USER_REG_HINT_USER; + + switch (user_reg_hint_type) { + case NL80211_USER_REG_HINT_USER: + case NL80211_USER_REG_HINT_CELL_BASE: + break; + default: + return -EINVAL; + } + + r = regulatory_hint_user(data, user_reg_hint_type); return r; } @@ -3971,6 +3987,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) cfg80211_regdomain->dfs_region))) goto nla_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; + nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); if (!nl_reg_rules) goto nla_put_failure; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ad6f9029c564..83583a9c15d9 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -97,9 +97,16 @@ const struct ieee80211_regdomain *cfg80211_regdomain; * - cfg80211_world_regdom * - cfg80211_regdom * - last_request + * - reg_num_devs_support_basehint */ static DEFINE_MUTEX(reg_mutex); +/* + * Number of devices that registered to the core + * that support cellular base station regulatory hints + */ +static int reg_num_devs_support_basehint; + static inline void assert_reg_lock(void) { lockdep_assert_held(®_mutex); @@ -911,6 +918,59 @@ static void handle_band(struct wiphy *wiphy, handle_channel(wiphy, initiator, band, i); } +static bool reg_request_cell_base(struct regulatory_request *request) +{ + if (request->initiator != NL80211_REGDOM_SET_BY_USER) + return false; + if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) + return false; + return true; +} + +bool reg_last_request_cell_base(void) +{ + assert_cfg80211_lock(); + + mutex_lock(®_mutex); + return reg_request_cell_base(last_request); + mutex_unlock(®_mutex); +} + +#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS + +/* Core specific check */ +static int reg_ignore_cell_hint(struct regulatory_request *pending_request) +{ + if (!reg_num_devs_support_basehint) + return -EOPNOTSUPP; + + if (reg_request_cell_base(last_request)) { + if (!regdom_changes(pending_request->alpha2)) + return -EALREADY; + return 0; + } + return 0; +} + +/* Device specific check */ +static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) +{ + if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) + return true; + return false; +} +#else +static int reg_ignore_cell_hint(struct regulatory_request *pending_request) +{ + return -EOPNOTSUPP; +} +static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) +{ + return true; +} +#endif + + static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { @@ -944,6 +1004,9 @@ static bool ignore_reg_update(struct wiphy *wiphy, return true; } + if (reg_request_cell_base(last_request)) + return reg_dev_ignore_cell_hint(wiphy); + return false; } @@ -1307,6 +1370,13 @@ static int ignore_request(struct wiphy *wiphy, return 0; case NL80211_REGDOM_SET_BY_COUNTRY_IE: + if (reg_request_cell_base(last_request)) { + /* Trust a Cell base station over the AP's country IE */ + if (regdom_changes(pending_request->alpha2)) + return -EOPNOTSUPP; + return -EALREADY; + } + last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (unlikely(!is_an_alpha2(pending_request->alpha2))) @@ -1351,6 +1421,12 @@ static int ignore_request(struct wiphy *wiphy, return REG_INTERSECT; case NL80211_REGDOM_SET_BY_USER: + if (reg_request_cell_base(pending_request)) + return reg_ignore_cell_hint(pending_request); + + if (reg_request_cell_base(last_request)) + return -EOPNOTSUPP; + if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_INTERSECT; /* @@ -1640,7 +1716,8 @@ static int regulatory_hint_core(const char *alpha2) } /* User hints */ -int regulatory_hint_user(const char *alpha2) +int regulatory_hint_user(const char *alpha2, + enum nl80211_user_reg_hint_type user_reg_hint_type) { struct regulatory_request *request; @@ -1654,6 +1731,7 @@ int regulatory_hint_user(const char *alpha2) request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_USER; + request->user_reg_hint_type = user_reg_hint_type; queue_regulatory_request(request); @@ -1906,7 +1984,7 @@ static void restore_regulatory_settings(bool reset_user) * settings, user regulatory settings takes precedence. */ if (is_an_alpha2(alpha2)) - regulatory_hint_user(user_alpha2); + regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); if (list_empty(&tmp_reg_req_list)) return; @@ -2081,9 +2159,16 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) else { if (is_unknown_alpha2(rd->alpha2)) pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); - else - pr_info("Regulatory domain changed to country: %c%c\n", - rd->alpha2[0], rd->alpha2[1]); + else { + if (reg_request_cell_base(last_request)) + pr_info("Regulatory domain changed " + "to country: %c%c by Cell Station\n", + rd->alpha2[0], rd->alpha2[1]); + else + pr_info("Regulatory domain changed " + "to country: %c%c\n", + rd->alpha2[0], rd->alpha2[1]); + } } print_dfs_region(rd->dfs_region); print_rd_rules(rd); @@ -2293,6 +2378,18 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) } #endif /* CONFIG_HOTPLUG */ +void wiphy_regulatory_register(struct wiphy *wiphy) +{ + assert_cfg80211_lock(); + + mutex_lock(®_mutex); + + if (!reg_dev_ignore_cell_hint(wiphy)) + reg_num_devs_support_basehint++; + + mutex_unlock(®_mutex); +} + /* Caller must hold cfg80211_mutex */ void reg_device_remove(struct wiphy *wiphy) { @@ -2302,6 +2399,9 @@ void reg_device_remove(struct wiphy *wiphy) mutex_lock(®_mutex); + if (!reg_dev_ignore_cell_hint(wiphy)) + reg_num_devs_support_basehint--; + kfree(wiphy->regd); if (last_request) @@ -2367,7 +2467,8 @@ int __init regulatory_init(void) * as a user hint. */ if (!is_world_regdom(ieee80211_regdom)) - regulatory_hint_user(ieee80211_regdom); + regulatory_hint_user(ieee80211_regdom, + NL80211_USER_REG_HINT_USER); return 0; } diff --git a/net/wireless/reg.h b/net/wireless/reg.h index e2aaaf525a22..519492fdda3c 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -22,9 +22,11 @@ bool is_world_regdom(const char *alpha2); bool reg_is_valid_request(const char *alpha2); bool reg_supported_dfs_region(u8 dfs_region); -int regulatory_hint_user(const char *alpha2); +int regulatory_hint_user(const char *alpha2, + enum nl80211_user_reg_hint_type user_reg_hint_type); int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env); +void wiphy_regulatory_register(struct wiphy *wiphy); void reg_device_remove(struct wiphy *wiphy); int __init regulatory_init(void); @@ -33,6 +35,7 @@ void regulatory_exit(void); int set_regdom(const struct ieee80211_regdomain *rd); void regulatory_update(struct wiphy *wiphy, enum nl80211_reg_initiator setby); +bool reg_last_request_cell_base(void); /** * regulatory_hint_found_beacon - hints a beacon was found on a channel -- cgit v1.2.3