From c01fc9ada926aaad907989ca2eba40c2a2a73afe Mon Sep 17 00:00:00 2001 From: Sunil Dutt Date: Wed, 9 Oct 2013 20:45:21 +0530 Subject: cfg80211: pass station supported channel and oper class info The information of the peer's supported channels and supported operating classes are required for the driver to perform TDLS off channel operations. This commit enhances the function nl80211_(new)set_station to pass this information of the peer to the driver. Signed-off-by: Sunil Dutt [return errors for malformed tuples] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 45f6bf59110..5db5fe24eff 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -744,6 +744,10 @@ enum station_parameters_apply_mask { * @capability: station capability * @ext_capab: extended capabilities of the station * @ext_capab_len: number of extended capabilities + * @supported_channels: supported channels in IEEE 802.11 format + * @supported_channels_len: number of supported channels + * @supported_oper_classes: supported oper classes in IEEE 802.11 format + * @supported_oper_classes_len: number of supported operating classes */ struct station_parameters { const u8 *supported_rates; @@ -763,6 +767,10 @@ struct station_parameters { u16 capability; const u8 *ext_capab; u8 ext_capab_len; + const u8 *supported_channels; + u8 supported_channels_len; + const u8 *supported_oper_classes; + u8 supported_oper_classes_len; }; /** -- cgit v1.2.3-70-g09d2 From 5336fa88e8ac6b666a3db9902a4797d94d86a702 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 7 Oct 2013 18:41:05 +0200 Subject: nl80211/cfg80211: enable DFS for IBSS mode To use DFS in IBSS mode, userspace is required to react to radar events. It can inform nl80211 that it is capable of doing so by adding a NL80211_ATTR_HANDLE_DFS attribute when joining the IBSS. This attribute is supplied to let the kernelspace know that the userspace application can and will handle radar events, e.g. by intiating channel switches to a valid channel. DFS channels may only be used if this attribute is supplied and the driver supports it. Driver support will be checked even if a channel without DFS will be initially joined, as a DFS channel may be chosen later. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer [fix attribute name in commit message] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ++++++ include/uapi/linux/nl80211.h | 9 +++++++++ net/wireless/chan.c | 3 ++- net/wireless/ibss.c | 24 ++++++++++++++++++++---- net/wireless/nl80211.c | 8 ++++++-- net/wireless/util.c | 14 ++++++++++---- 6 files changed, 53 insertions(+), 11 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5db5fe24eff..b1acf36e5f4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1664,6 +1664,9 @@ struct cfg80211_disassoc_request { * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is * required to assume that the port is unauthorized until authorized by * user space. Otherwise, port is marked authorized by default. + * @userspace_handles_dfs: whether user space controls DFS operation, i.e. + * changes the channel when a radar is detected. This is required + * to operate on DFS channels. * @basic_rates: bitmap of basic rates to use when creating the IBSS * @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask @@ -1681,6 +1684,7 @@ struct cfg80211_ibss_params { bool channel_fixed; bool privacy; bool control_port; + bool userspace_handles_dfs; int mcast_rate[IEEE80211_NUM_BANDS]; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; @@ -3061,6 +3065,7 @@ struct cfg80211_cached_keys; * @conn: (private) cfg80211 software SME connection state machine data * @connect_keys: (private) keys to set after connection is established * @ibss_fixed: (private) IBSS is using fixed BSSID + * @ibss_dfs_possible: (private) IBSS may change to a DFS channel * @event_list: (private) list for internal event processing * @event_lock: (private) lock for event list */ @@ -3099,6 +3104,7 @@ struct wireless_dev { struct ieee80211_channel *channel; bool ibss_fixed; + bool ibss_dfs_possible; bool ps; int ps_timeout; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index f2aef2a7a57..f752e9821e7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1501,6 +1501,13 @@ enum nl80211_commands { * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported * supported operating classes. * + * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space + * controls DFS operation in IBSS mode. If the flag is included in + * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS + * channels and reports radar events to userspace. Userspace is required + * to react to radar events, e.g. initiate a channel switch or leave the + * IBSS network. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1815,6 +1822,8 @@ enum nl80211_attrs { NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + NL80211_ATTR_HANDLE_DFS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 16f3c3a7b2c..9b8cc877eb1 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -504,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, case NL80211_IFTYPE_ADHOC: if (wdev->current_bss) { *chan = wdev->current_bss->pub.channel; - *chanmode = wdev->ibss_fixed + *chanmode = (wdev->ibss_fixed && + !wdev->ibss_dfs_possible) ? CHAN_MODE_SHARED : CHAN_MODE_EXCLUSIVE; return; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 39bff7d3676..fa7461b6ba3 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *check_chan; + u8 radar_detect_width = 0; int err; ASSERT_WDEV_LOCK(wdev); @@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, wdev->connect_keys = connkeys; wdev->ibss_fixed = params->channel_fixed; + wdev->ibss_dfs_possible = params->userspace_handles_dfs; #ifdef CONFIG_CFG80211_WEXT wdev->wext.ibss.chandef = params->chandef; #endif + check_chan = params->chandef.chan; + if (params->userspace_handles_dfs) { + /* use channel NULL to check for radar even if the current + * channel is not a radar channel - it might decide to change + * to DFS channel later. + */ + radar_detect_width = BIT(params->chandef.width); + check_chan = NULL; + } + + err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, + check_chan, + (params->channel_fixed && + !radar_detect_width) + ? CHAN_MODE_SHARED + : CHAN_MODE_EXCLUSIVE, + radar_detect_width); - err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan, - params->channel_fixed - ? CHAN_MODE_SHARED - : CHAN_MODE_EXCLUSIVE); if (err) { wdev->connect_keys = NULL; return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 460638ac2d7..7502d33a3a7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -356,6 +356,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 }, [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY }, [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, + [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -5768,9 +5769,9 @@ skip_beacons: if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) return -EINVAL; - /* DFS channels are only supported for AP/P2P GO ... for now. */ if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP || - dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) { + dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO || + dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) { err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); if (err < 0) { @@ -6602,6 +6603,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.control_port = nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]); + ibss.userspace_handles_dfs = + nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]); + err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) kfree(connkeys); diff --git a/net/wireless/util.c b/net/wireless/util.c index 3c8be6104ba..935dea9485d 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1249,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chmode; int num_different_channels = 0; int total = 1; - bool radar_required; + bool radar_required = false; int i, j; ASSERT_RTNL(); @@ -1264,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_WDS: - radar_required = !!(chan && - (chan->flags & IEEE80211_CHAN_RADAR)); + /* if the interface could potentially choose a DFS channel, + * then mark DFS as required. + */ + if (!chan) { + if (chanmode != CHAN_MODE_UNDEFINED && radar_detect) + radar_required = true; + break; + } + radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_MONITOR: - radar_required = false; break; case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_UNSPECIFIED: -- cgit v1.2.3-70-g09d2 From 919be62b97e43e48c97a35ff82add1719d56553e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 14 Oct 2013 10:05:16 +0200 Subject: mac80211: add missing IEEE80211_HW_SUPPORTS_HT_CCK_RATES docs Document the IEEE80211_HW_SUPPORTS_HT_CCK_RATES flag. Reported-by: Randy Dunlap Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f386c480e13..ecba8a10acf 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1503,6 +1503,10 @@ struct ieee80211_tx_control { * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames * only, to allow getting TBTT of a DTIM beacon. * + * @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates + * and can cope with CCK rates in an aggregation session (e.g. by not + * using aggregation for such frames.) + * * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA) * for a single active channel while using channel contexts. When support * is not enabled the default action is to disconnect when getting the -- cgit v1.2.3-70-g09d2 From 034c6d6e675f84ef5e67445150522b2517d963c9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 14 Oct 2013 17:42:06 -0700 Subject: cfg80211: export reg_initiator_name() Drivers can now use this to parse the regulatory request and be more verbose when needed. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 +++++++++ net/wireless/reg.c | 26 ++++++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b1acf36e5f4..3eae46cb1ac 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3497,6 +3497,15 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, u32 center_freq); +/** + * reg_initiator_name - map regulatory request initiator enum to name + * @initiator: the regulatory request initiator + * + * You can use this to map the regulatory request initiator enum to a + * proper string representation. + */ +const char *reg_initiator_name(enum nl80211_reg_initiator initiator); + /* * callbacks for asynchronous cfg80211 methods, notification * functions and BSS handling helpers diff --git a/net/wireless/reg.c b/net/wireless/reg.c index edb2ba4e2a1..7da67fd0b41 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -768,24 +768,25 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, } EXPORT_SYMBOL(freq_reg_info); -#ifdef CONFIG_CFG80211_REG_DEBUG -static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) +const char *reg_initiator_name(enum nl80211_reg_initiator initiator) { switch (initiator) { case NL80211_REGDOM_SET_BY_CORE: - return "Set by core"; + return "core"; case NL80211_REGDOM_SET_BY_USER: - return "Set by user"; + return "user"; case NL80211_REGDOM_SET_BY_DRIVER: - return "Set by driver"; + return "driver"; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - return "Set by country IE"; + return "country IE"; default: WARN_ON(1); - return "Set by bug"; + return "bug"; } } +EXPORT_SYMBOL(reg_initiator_name); +#ifdef CONFIG_CFG80211_REG_DEBUG static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, const struct ieee80211_reg_rule *reg_rule) { @@ -986,14 +987,17 @@ static bool ignore_reg_update(struct wiphy *wiphy, struct regulatory_request *lr = get_last_request(); if (!lr) { - REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", + REG_DBG_PRINT("Ignoring regulatory request set by %s " + "since last_request is not set\n", reg_initiator_name(initiator)); return true; } if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n", + REG_DBG_PRINT("Ignoring regulatory request set by %s " + "since the driver uses its own custom " + "regulatory domain\n", reg_initiator_name(initiator)); return true; } @@ -1005,7 +1009,9 @@ static bool ignore_reg_update(struct wiphy *wiphy, if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && !is_world_regdom(lr->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", + REG_DBG_PRINT("Ignoring regulatory request set by %s " + "since the driver requires its own regulatory " + "domain to be set first\n", reg_initiator_name(initiator)); return true; } -- cgit v1.2.3-70-g09d2 From 06be6b149f7e406bcf16098567f5a6c9f042bced Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Oct 2013 18:01:00 +0200 Subject: mac80211: add ieee80211_tx_prepare_skb() helper function This can be used by a driver to prepare skbs for transmission, which were obtained via functions such as ieee80211_probereq_get or ieee80211_nullfunc_get. This is useful for drivers that want to send those frames directly, but need rate control information to be prepared first. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/net/mac80211.h | 14 ++++++++++++++ net/mac80211/tx.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ecba8a10acf..7ceed99a05b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4571,4 +4571,18 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, struct cfg80211_wowlan_wakeup *wakeup, gfp_t gfp); +/** + * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @vif: virtual interface + * @skb: frame to be sent from within the driver + * @band: the band to transmit on + * @sta: optional pointer to get the station to send the frame to + * + * Note: must be called under RCU lock + */ +bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct sk_buff *skb, + int band, struct ieee80211_sta **sta); + #endif /* MAC80211_H */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 4fcbf634b54..acd9b61fbc0 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1366,6 +1366,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } +bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct sk_buff *skb, + int band, struct ieee80211_sta **sta) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_data tx; + + if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) + return false; + + info->band = band; + info->control.vif = vif; + info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)]; + + if (invoke_tx_handlers(&tx)) + return false; + + if (sta) { + if (tx.sta) + *sta = &tx.sta->sta; + else + *sta = NULL; + } + + return true; +} +EXPORT_SYMBOL(ieee80211_tx_prepare_skb); + /* * Returns false if the frame couldn't be transmitted but was queued instead. */ -- cgit v1.2.3-70-g09d2