From 789fd03331aa1ec45cb58168e2d82525c97c7351 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 4 Oct 2013 18:07:24 -0700 Subject: cfg80211: rename regulatory_hint_11d() to regulatory_hint_country_ie() It is incorrect to refer to this as 11d as 802.11d was just a proposed amendment, 802.11d was merged to the standard so use proper terminology. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ++-- net/wireless/reg.h | 4 ++-- net/wireless/sme.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d62cb1e91475..8fbe664fdcf8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1699,8 +1699,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) } EXPORT_SYMBOL(regulatory_hint); -void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, - const u8 *country_ie, u8 country_ie_len) +void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band, + const u8 *country_ie, u8 country_ie_len) { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index af2d5f8a5d82..9677e3c13da9 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -58,7 +58,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, gfp_t gfp); /** - * regulatory_hint_11d - hints a country IE as a regulatory domain + * regulatory_hint_country_ie - hints a country IE as a regulatory domain * @wiphy: the wireless device giving the hint (used only for reporting * conflicts) * @band: the band on which the country IE was received on. This determines @@ -78,7 +78,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, * not observed. For this reason if a triplet is seen with channel * information for a band the BSS is not present in it will be ignored. */ -void regulatory_hint_11d(struct wiphy *wiphy, +void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band, const u8 *country_ie, u8 country_ie_len); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 20e86a95dc4e..65f800890d70 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -682,8 +682,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ - regulatory_hint_11d(wdev->wiphy, bss->channel->band, - country_ie + 2, country_ie[1]); + regulatory_hint_country_ie(wdev->wiphy, bss->channel->band, + country_ie + 2, country_ie[1]); kfree(country_ie); } -- cgit v1.2.3 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 ++++++++ include/uapi/linux/nl80211.h | 9 +++++++++ net/wireless/nl80211.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 45f6bf591104..5db5fe24eff6 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; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 8c0417c222c6..f2aef2a7a570 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1496,6 +1496,11 @@ enum nl80211_commands { * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. * As specified in the &enum nl80211_rxmgmt_flags. * + * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels. + * + * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported + * supported operating classes. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1806,6 +1811,10 @@ enum nl80211_attrs { NL80211_ATTR_RXMGMT_FLAGS, + NL80211_ATTR_STA_SUPPORTED_CHANNELS, + + NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2838206ddad3..460638ac2d73 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -354,6 +354,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 }, [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 }, }; /* policy for the key attributes */ @@ -3896,9 +3898,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info, return 0; } +static int nl80211_parse_sta_channel_info(struct genl_info *info, + struct station_parameters *params) +{ + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) { + params->supported_channels = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); + params->supported_channels_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); + /* + * Need to include at least one (first channel, number of + * channels) tuple for each subband, and must have proper + * tuples for the rest of the data as well. + */ + if (params->supported_channels_len < 2) + return -EINVAL; + if (params->supported_channels_len % 2) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) { + params->supported_oper_classes = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); + params->supported_oper_classes_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); + /* + * The value of the Length field of the Supported Operating + * Classes element is between 2 and 253. + */ + if (params->supported_oper_classes_len < 2 || + params->supported_oper_classes_len > 253) + return -EINVAL; + } + return 0; +} + static int nl80211_set_station_tdls(struct genl_info *info, struct station_parameters *params) { + int err; /* Dummy STA entry gets updated once the peer capabilities are known */ if (info->attrs[NL80211_ATTR_PEER_AID]) params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); @@ -3909,6 +3947,10 @@ static int nl80211_set_station_tdls(struct genl_info *info, params->vht_capa = nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + err = nl80211_parse_sta_channel_info(info, params); + if (err) + return err; + return nl80211_parse_sta_wme(info, params); } @@ -4089,6 +4131,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + err = nl80211_parse_sta_channel_info(info, ¶ms); + if (err) + return err; + err = nl80211_parse_sta_wme(info, ¶ms); if (err) return err; -- cgit v1.2.3 From cea85247f8725fcad1ac7533ce5b32dace506cfc Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 2 Oct 2013 18:33:09 -0700 Subject: cfg80211: enable regulatory hints for strict custom settings If we have a wiphy with an ISO3166-alpha2 regulatory domain programmed with the strict flag set we wait until the wiphy gets its wiphy->regd programmed before allowing regulatory domains hints other than country IE hints from processing on the wiphy. The existing check however discards the possibility of custom regulatory domains having also used the strict flag and these will not have the wiphy->regd set. Custom strict regulatory domains never set the wiphy->regd though as such currently all regulatory hints other than country IE hints are being ignored on these wiphys. All custom strict regulatory domains set the wiphy with the WIPHY_FLAG_CUSTOM_REGULATORY and use wiphy_apply_custom_regulatory(). Enhance the check for the strict ISO3166-alpha2 regulatory domain case by exempting the WIPHY_FLAG_CUSTOM_REGULATORY case. This will enable other regulatory hints to be processed now for these strict custom regulatory domains. Cc: smihir@qti.qualcomm.com Cc: tushnimb@qca.qualcomm.com Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 8fbe664fdcf8..37c2a63d0697 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -997,6 +997,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, */ if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) && !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_initiator_name(initiator)); -- cgit v1.2.3 From fa1fb9cb1c734204018d2b4e6f38c4a9b4146612 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 2 Oct 2013 18:33:10 -0700 Subject: cfg80211: simplify strict custom alpha2 regdomain check This makes it easier to read. Cc: smihir@qti.qualcomm.com Cc: tushnimb@qca.qualcomm.com Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 37c2a63d0697..edb2ba4e2a18 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -972,6 +972,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) } #endif +static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy) +{ + if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && + !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) + return true; + return false; +} static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) @@ -995,9 +1002,8 @@ static bool ignore_reg_update(struct wiphy *wiphy, * wiphy->regd will be set once the device has its own * desired regulatory domain set */ - if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && + if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) && !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_initiator_name(initiator)); -- cgit v1.2.3 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 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5db5fe24eff6..b1acf36e5f45 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 f2aef2a7a570..f752e9821e71 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 16f3c3a7b2c1..9b8cc877eb19 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 39bff7d36768..fa7461b6ba39 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 460638ac2d73..7502d33a3a70 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 3c8be6104ba4..935dea9485da 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 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 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b1acf36e5f45..3eae46cb1acf 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 edb2ba4e2a18..7da67fd0b418 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 From c6da674aff9425dc41255bcb7f7586a656843f2d Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Mon, 14 Oct 2013 19:08:28 -0700 Subject: {nl,cfg,mac}80211: enable the triggering of CSA frame in mesh Allow the triggering of CSA frame using mesh interface. The rules are more or less same with IBSS, such as not allowed to change between the band and channel width has to be same from the previous mode. Also, move the ieee80211_send_action_csa to a common space so that it can be re-used by mesh interface. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 21 +++++++++++ net/mac80211/ibss.c | 54 ---------------------------- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/util.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 1 + 5 files changed, 111 insertions(+), 54 deletions(-) (limited to 'net/wireless') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 67f62dac54f5..8cdbd29cbc45 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3014,6 +3014,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx *chanctx; + struct ieee80211_if_mesh __maybe_unused *ifmsh; int err, num_chanctx; if (!list_empty(&local->roc_list) || local->scanning) @@ -3097,6 +3098,26 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (err < 0) return err; break; +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + ifmsh = &sdata->u.mesh; + + if (!ifmsh->mesh_id) + return -EINVAL; + + if (params->chandef.width != sdata->vif.bss_conf.chandef.width) + return -EINVAL; + + /* changes into another band are not supported */ + if (sdata->vif.bss_conf.chandef.chan->band != + params->chandef.chan->band) + return -EINVAL; + + err = ieee80211_send_action_csa(sdata, params); + if (err < 0) + return err; + break; +#endif default: return -EOPNOTSUPP; } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a0ae02760139..531be040b9ae 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -464,60 +464,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, tsf, false); } -static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, - struct cfg80211_csa_settings *csa_settings) -{ - struct sk_buff *skb; - struct ieee80211_mgmt *mgmt; - struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - struct ieee80211_local *local = sdata->local; - int freq; - int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + - sizeof(mgmt->u.action.u.chan_switch); - u8 *pos; - - skb = dev_alloc_skb(local->tx_headroom + hdr_len + - 5 + /* channel switch announcement element */ - 3); /* secondary channel offset element */ - if (!skb) - return -1; - - skb_reserve(skb, local->tx_headroom); - mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); - memset(mgmt, 0, hdr_len); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION); - - eth_broadcast_addr(mgmt->da); - memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); - mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; - mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; - pos = skb_put(skb, 5); - *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ - *pos++ = 3; /* IE length */ - *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ - freq = csa_settings->chandef.chan->center_freq; - *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ - *pos++ = csa_settings->count; /* count */ - - if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { - enum nl80211_channel_type ch_type; - - skb_put(skb, 3); - *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ - *pos++ = 1; /* IE length */ - ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); - if (ch_type == NL80211_CHAN_HT40PLUS) - *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - else - *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - } - - ieee80211_tx_skb(sdata, skb); - return 0; -} - int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings *csa_settings) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4ebbcc6f67e0..9aad167e2ebc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1731,6 +1731,8 @@ void ieee80211_dfs_cac_timer(unsigned long data); void ieee80211_dfs_cac_timer_work(struct work_struct *work); void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); void ieee80211_dfs_radar_detected_work(struct work_struct *work); +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 523783cedf6e..a38d58231af8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2384,3 +2384,90 @@ bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, return false; } + +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings) +{ + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + struct ieee80211_local *local = sdata->local; + int freq; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + + sizeof(mgmt->u.action.u.chan_switch); + u8 *pos; + + if (sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + + 5 + /* channel switch announcement element */ + 3 + /* secondary channel offset element */ + 8); /* mesh channel switch parameters element */ + if (!skb) + return -ENOMEM; + + skb_reserve(skb, local->tx_headroom); + mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + eth_broadcast_addr(mgmt->da); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + if (ieee80211_vif_is_mesh(&sdata->vif)) { + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + } else { + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); + } + mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; + mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; + pos = skb_put(skb, 5); + *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ + *pos++ = 3; /* IE length */ + *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ + freq = csa_settings->chandef.chan->center_freq; + *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ + *pos++ = csa_settings->count; /* count */ + + if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { + enum nl80211_channel_type ch_type; + + skb_put(skb, 3); + *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ + *pos++ = 1; /* IE length */ + ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); + if (ch_type == NL80211_CHAN_HT40PLUS) + *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + } + + if (ieee80211_vif_is_mesh(&sdata->vif)) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + __le16 pre_value; + + skb_put(skb, 8); + *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ + *pos++ = 6; /* IE length */ + *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ + *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ + *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + *pos++ |= csa_settings->block_tx ? + WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; + put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ + pos += 2; + if (!ifmsh->pre_value) + ifmsh->pre_value = 1; + else + ifmsh->pre_value++; + pre_value = cpu_to_le16(ifmsh->pre_value); + memcpy(pos, &pre_value, 2); /* Precedence Value */ + pos += 2; + ifmsh->chsw_init = true; + } + + ieee80211_tx_skb(sdata, skb); + return 0; +} diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7502d33a3a70..b8d6f101378a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5700,6 +5700,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) return -EINVAL; break; case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: break; default: return -EOPNOTSUPP; -- cgit v1.2.3 From b8456a14e9d2770846fcf74de18ff95b676149a3 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 17 Oct 2013 15:55:02 -0700 Subject: {nl,cfg,mac}80211: implement mesh channel switch userspace API Implement the required procedures for mesh channel switching as defined in the IEEE Std 802.11-2012 section 10.9.8.4.3 and also handle the CSA and MCSP elements as followed: * Add the function for updating the beacon and probe response frames with CSA and MCSP elements during the period of switching to the new channel. Both CSA and MCSP elements must be included in beacon and probe response frames until the intended channel switch time. * The ifmsh->csa_settings is set to NULL and the CSA and MCSP elements will then be removed from the beacon or probe response frames once the new channel is switched to. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 9 ++++- net/mac80211/ieee80211_i.h | 11 ++++++ net/mac80211/mesh.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/rx.c | 5 ++- net/mac80211/tx.c | 16 ++++++++ net/wireless/nl80211.c | 3 +- 6 files changed, 139 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8cdbd29cbc45..5b1ccb4e0271 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2994,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work) case NL80211_IFTYPE_ADHOC: ieee80211_ibss_finish_csa(sdata); break; +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + err = ieee80211_mesh_finish_csa(sdata); + if (err < 0) + return; + break; +#endif default: WARN_ON(1); return; @@ -3113,7 +3120,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, params->chandef.chan->band) return -EINVAL; - err = ieee80211_send_action_csa(sdata, params); + err = ieee80211_mesh_csa_beacon(sdata, params, true); if (err < 0) return err; break; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9aad167e2ebc..5cfa160be05b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -543,6 +543,11 @@ struct ieee80211_mesh_sync_ops { /* add other framework functions here */ }; +struct mesh_csa_settings { + struct rcu_head rcu_head; + struct cfg80211_csa_settings settings; +}; + struct ieee80211_if_mesh { struct timer_list housekeeping_timer; struct timer_list mesh_path_timer; @@ -604,7 +609,9 @@ struct ieee80211_if_mesh { int ps_peers_deep_sleep; struct ps_data ps; /* Channel Switching Support */ + struct mesh_csa_settings __rcu *csa; bool chsw_init; + u8 chsw_ttl; u16 pre_value; }; @@ -1356,6 +1363,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings, + bool csa_action); +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata); /* scan/BSS handling */ void ieee80211_scan_work(struct work_struct *work); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0a3ccaa275f9..6eb31d6fd8e1 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -12,6 +12,7 @@ #include #include "ieee80211_i.h" #include "mesh.h" +#include "driver-ops.h" static int mesh_allocated; static struct kmem_cache *rm_cache; @@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) struct sk_buff *skb; struct ieee80211_mgmt *mgmt; struct ieee80211_chanctx_conf *chanctx_conf; + struct mesh_csa_settings *csa; enum ieee80211_band band; u8 *pos; struct ieee80211_sub_if_data *sdata; @@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) head_len = hdr_len + 2 + /* NULL SSID */ + /* Channel Switch Announcement */ + 2 + sizeof(struct ieee80211_channel_sw_ie) + + /* Mesh Channel Swith Parameters */ + 2 + sizeof(struct ieee80211_mesh_chansw_params_ie) + 2 + 8 + /* supported rates */ 2 + 3; /* DS params */ tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + @@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) *pos++ = WLAN_EID_SSID; *pos++ = 0x0; + rcu_read_lock(); + csa = rcu_dereference(ifmsh->csa); + if (csa) { + __le16 pre_value; + + pos = skb_put(skb, 13); + memset(pos, 0, 13); + *pos++ = WLAN_EID_CHANNEL_SWITCH; + *pos++ = 3; + *pos++ = 0x0; + *pos++ = ieee80211_frequency_to_channel( + csa->settings.chandef.chan->center_freq); + sdata->csa_counter_offset_beacon = hdr_len + 6; + *pos++ = csa->settings.count; + *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; + *pos++ = 6; + if (ifmsh->chsw_init) { + *pos++ = ifmsh->mshcfg.dot11MeshTTL; + *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + } else { + *pos++ = ifmsh->chsw_ttl; + } + *pos++ |= csa->settings.block_tx ? + WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; + put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); + pos += 2; + pre_value = cpu_to_le16(ifmsh->pre_value); + memcpy(pos, &pre_value, 2); + pos += 2; + } + rcu_read_unlock(); + if (ieee80211_add_srates_ie(sdata, skb, true, band) || mesh_add_ds_params_ie(sdata, skb)) goto out_free; @@ -920,6 +958,65 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, stype, mgmt, &elems, rx_status); } +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct mesh_csa_settings *tmp_csa_settings; + int ret = 0; + + /* Reset the TTL value and Initiator flag */ + ifmsh->chsw_init = false; + ifmsh->chsw_ttl = 0; + + /* Remove the CSA and MCSP elements from the beacon */ + tmp_csa_settings = rcu_dereference(ifmsh->csa); + rcu_assign_pointer(ifmsh->csa, NULL); + kfree_rcu(tmp_csa_settings, rcu_head); + ret = ieee80211_mesh_rebuild_beacon(sdata); + if (ret) + return -EINVAL; + + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + + mcsa_dbg(sdata, "complete switching to center freq %d MHz", + sdata->vif.bss_conf.chandef.chan->center_freq); + return 0; +} + +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings, + bool csa_action) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct mesh_csa_settings *tmp_csa_settings; + int ret = 0; + + tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings), + GFP_ATOMIC); + if (!tmp_csa_settings) + return -ENOMEM; + + memcpy(&tmp_csa_settings->settings, csa_settings, + sizeof(struct cfg80211_csa_settings)); + + rcu_assign_pointer(ifmsh->csa, tmp_csa_settings); + + ret = ieee80211_mesh_rebuild_beacon(sdata); + if (ret) { + tmp_csa_settings = rcu_dereference(ifmsh->csa); + rcu_assign_pointer(ifmsh->csa, NULL); + kfree_rcu(tmp_csa_settings, rcu_head); + return ret; + } + + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + + if (csa_action) + ieee80211_send_action_csa(sdata, csa_settings); + + return 0; +} + static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { @@ -942,6 +1039,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, offset_ttl = (len < 42) ? 7 : 10; *(pos + offset_ttl) -= 1; *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + sdata->u.mesh.chsw_ttl = *(pos + offset_ttl); memcpy(mgmt_fwd, mgmt, len); eth_broadcast_addr(mgmt_fwd->da); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f0247a43a75c..23f49e8d14c1 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) break; if (sdata->vif.type != NL80211_IFTYPE_STATION && - sdata->vif.type != NL80211_IFTYPE_ADHOC) + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) break; if (sdata->vif.type == NL80211_IFTYPE_STATION) bssid = sdata->u.mgd.bssid; else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) bssid = sdata->u.ibss.bssid; + else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) + bssid = mgmt->sa; else break; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index acd9b61fbc07..9868cb72054e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2398,6 +2398,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, beacon_data = beacon->head; beacon_data_len = beacon->head_len; break; + case NL80211_IFTYPE_MESH_POINT: + beacon_data = beacon->head; + beacon_data_len = beacon->head_len; + break; default: return; } @@ -2452,6 +2456,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) if (!beacon) goto out; + beacon_data = beacon->head; + beacon_data_len = beacon->head_len; + } else if (vif->type == NL80211_IFTYPE_MESH_POINT) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + + beacon = rcu_dereference(ifmsh->beacon); + if (!beacon) + goto out; + beacon_data = beacon->head; beacon_data_len = beacon->head_len; } else { @@ -2559,6 +2572,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!bcn) goto out; + if (sdata->vif.csa_active) + ieee80211_update_csa(sdata, bcn); + if (ifmsh->sync_ops) ifmsh->sync_ops->adjust_tbtt( sdata); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b8d6f101378a..c49f0af61d5e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10813,7 +10813,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO && - wdev->iftype != NL80211_IFTYPE_ADHOC)) + wdev->iftype != NL80211_IFTYPE_ADHOC && + wdev->iftype != NL80211_IFTYPE_MESH_POINT)) goto out; wdev->channel = chandef->chan; -- cgit v1.2.3 From d0a361a5b3f5aa28778a0c336de5a911fc0cd678 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Thu, 17 Oct 2013 10:52:17 +0200 Subject: nl80211: fix channel switch parsing The nl80211 attribute NL80211_ATTR_CSA_C_OFF_BEACON should be nested inside NL80211_ATTR_CSA_IES, but commit ee4bc9e75811d2c0cb5f2a2fc5b5 ("nl80211: enable IBSS support for channel switch announcements") added a check in the outer message attributes. Fix channel switch calls by removing the erroneus condition. Signed-off-by: Andrei Otcheretianski [reword commit message] Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c49f0af61d5e..8ced6bc29f4a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5713,9 +5713,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) return -EINVAL; /* only important for AP, IBSS and mesh create IEs internally */ - if (need_new_beacon && - (!info->attrs[NL80211_ATTR_CSA_IES] || - !info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON])) + if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES]) return -EINVAL; params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); -- cgit v1.2.3