From 6bdd253f635f7b2ef027d116933a6c9ec148b87f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 24 Nov 2012 00:32:19 +0100 Subject: mac80211: fix remain-on-channel (non-)cancelling Felix Liao reported that when an interface is set DOWN while another interface is executing a ROC, the warning in ieee80211_start_next_roc() (about the first item on the list having started already) triggers. This is because ieee80211_roc_purge() calls it even if it never actually changed the list of ROC items. To fix this, simply remove the function call. If it is needed then it will be done by the ieee80211_sw_roc_work() function when the ROC item that is being removed while active is cleaned up. Cc: stable@vger.kernel.org Reported-by: Felix Liao Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/offchannel.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 83608ac16780..2c84185dfdb0 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -458,8 +458,6 @@ void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata) list_move_tail(&roc->list, &tmp_list); roc->abort = true; } - - ieee80211_start_next_roc(local); mutex_unlock(&local->mtx); list_for_each_entry_safe(roc, tmp, &tmp_list, list) { -- cgit v1.2.3 From 9f5e8f6efc7c2601136b27d9c7325c245f8fd19a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Nov 2012 16:59:45 +0100 Subject: cfg80211: rework chandef checking and export it Some of the chandef checking that we do in cfg80211 to check if a channel is supported or not is also needed in mac80211, so rework that a bit and export the functions that are needed. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 21 ++++++++++-- net/wireless/chan.c | 92 ++++++++++++++++++++++++++++++++++---------------- net/wireless/core.h | 6 ---- net/wireless/nl80211.c | 38 +++------------------ 4 files changed, 85 insertions(+), 72 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e78db2cf3d1b..a238f41e55c2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -58,6 +58,8 @@ * structures here describe these capabilities in detail. */ +struct wiphy; + /* * wireless hardware capability structures */ @@ -387,6 +389,22 @@ const struct cfg80211_chan_def * cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, const struct cfg80211_chan_def *chandef2); +/** + * cfg80211_chandef_valid - check if a channel definition is valid + * @chandef: the channel definition to check + */ +bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef); + +/** + * cfg80211_chandef_usable - check if secondary channels can be used + * @wiphy: the wiphy to validate against + * @chandef: the channel definition to check + * @prohibited_flags: the regulatory chanenl flags that must not be set + */ +bool cfg80211_chandef_usable(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 prohibited_flags); + /** * enum survey_info_flags - survey information flags * @@ -1045,9 +1063,6 @@ struct ieee80211_txq_params { u8 aifs; }; -/* from net/wireless.h */ -struct wiphy; - /** * DOC: Scanning and BSS list handling * diff --git a/net/wireless/chan.c b/net/wireless/chan.c index bf2dfd54ff3b..b5f69831e318 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -44,7 +44,7 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, } EXPORT_SYMBOL(cfg80211_chandef_create); -bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef) +bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) { u32 control_freq; @@ -105,6 +105,7 @@ bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef) return true; } +EXPORT_SYMBOL(cfg80211_chandef_valid); static void chandef_primary_freqs(const struct cfg80211_chan_def *c, int *pri40, int *pri80) @@ -187,9 +188,9 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, } EXPORT_SYMBOL(cfg80211_chandef_compatible); -bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, - u32 center_freq, u32 bandwidth, - u32 prohibited_flags) +static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, + u32 center_freq, u32 bandwidth, + u32 prohibited_flags) { struct ieee80211_channel *c; u32 freq; @@ -205,55 +206,88 @@ bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, return true; } -static bool cfg80211_check_beacon_chans(struct wiphy *wiphy, - u32 center_freq, u32 bw) +bool cfg80211_chandef_usable(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 prohibited_flags) { - return cfg80211_secondary_chans_ok(wiphy, center_freq, bw, - IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_PASSIVE_SCAN | - IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR); -} + struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_sta_vht_cap *vht_cap; + u32 width, control_freq; -bool cfg80211_reg_can_beacon(struct wiphy *wiphy, - struct cfg80211_chan_def *chandef) -{ - u32 width; - bool res; + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return false; - trace_cfg80211_reg_can_beacon(wiphy, chandef); + ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; + vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; - if (WARN_ON(!cfg80211_chan_def_valid(chandef))) { - trace_cfg80211_return_bool(false); - return false; - } + control_freq = chandef->chan->center_freq; switch (chandef->width) { - case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: + if (!ht_cap->ht_supported) + return false; + case NL80211_CHAN_WIDTH_20_NOHT: width = 20; break; case NL80211_CHAN_WIDTH_40: width = 40; + if (!ht_cap->ht_supported) + return false; + if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || + ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) + return false; + if (chandef->center_freq1 < control_freq && + chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + return false; + if (chandef->center_freq1 > control_freq && + chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + return false; break; - case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_80P80: + if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) + return false; + case NL80211_CHAN_WIDTH_80: + if (!vht_cap->vht_supported) + return false; width = 80; break; case NL80211_CHAN_WIDTH_160: + if (!vht_cap->vht_supported) + return false; + if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) + return false; width = 160; break; default: WARN_ON_ONCE(1); - trace_cfg80211_return_bool(false); return false; } - res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width); + /* TODO: missing regulatory check on 80/160 bandwidth */ + + if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1, + width, prohibited_flags)) + return false; + + if (!chandef->center_freq2) + return true; + return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2, + width, prohibited_flags); +} +EXPORT_SYMBOL(cfg80211_chandef_usable); + +bool cfg80211_reg_can_beacon(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + bool res; + + trace_cfg80211_reg_can_beacon(wiphy, chandef); - if (res && chandef->center_freq2) - res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2, - width); + res = cfg80211_chandef_usable(wiphy, chandef, + IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_RADAR); trace_cfg80211_return_bool(res); return res; diff --git a/net/wireless/core.h b/net/wireless/core.h index a0c8decf6a47..6183a0d25b8b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -483,12 +483,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); -bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef); - -bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, - u32 center_freq, u32 bandwidth, - u32 prohibited_flags); - #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d038fa45ecd1..eb0aa71a02b3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1420,63 +1420,33 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap; - if (!cfg80211_chan_def_valid(chandef)) + if (!cfg80211_chandef_valid(chandef)) return -EINVAL; switch (chandef->width) { case NL80211_CHAN_WIDTH_20: - if (!ht_cap->ht_supported) - return -EINVAL; case NL80211_CHAN_WIDTH_20_NOHT: width = 20; break; case NL80211_CHAN_WIDTH_40: width = 40; - /* quick early regulatory check */ - if (chandef->center_freq1 < control_freq && - chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) - return -EINVAL; - if (chandef->center_freq1 > control_freq && - chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) - return -EINVAL; - if (!ht_cap->ht_supported) - return -EINVAL; - if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || - ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) - return -EINVAL; break; case NL80211_CHAN_WIDTH_80: width = 80; - if (!vht_cap->vht_supported) - return -EINVAL; break; case NL80211_CHAN_WIDTH_80P80: width = 80; - if (!vht_cap->vht_supported) - return -EINVAL; - if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) - return -EINVAL; break; case NL80211_CHAN_WIDTH_160: width = 160; - if (!vht_cap->vht_supported) - return -EINVAL; - if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) - return -EINVAL; break; default: return -EINVAL; } - if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1, - width, IEEE80211_CHAN_DISABLED)) + if (!cfg80211_chandef_usable(&rdev->wiphy, chandef, + IEEE80211_CHAN_DISABLED)) return -EINVAL; - if (chandef->center_freq2 && - !cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2, - width, IEEE80211_CHAN_DISABLED)) - return -EINVAL; - - /* TODO: missing regulatory check on bandwidth */ return 0; } @@ -1841,7 +1811,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev) static int nl80211_send_chandef(struct sk_buff *msg, struct cfg80211_chan_def *chandef) { - WARN_ON(!cfg80211_chan_def_valid(chandef)); + WARN_ON(!cfg80211_chandef_valid(chandef)); if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chandef->chan->center_freq)) -- cgit v1.2.3 From f2d9d270c15ae0139b54a7e7466d738327e97e03 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Nov 2012 14:11:39 +0100 Subject: mac80211: support VHT association Determine the VHT channel from the AP's VHT operation IE (if present) and configure the hardware to that channel if it is supported. If channel contexts cause a channel to not be usable, try a smaller bandwidth. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 15 ++ net/mac80211/ieee80211_i.h | 2 + net/mac80211/mlme.c | 368 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 313 insertions(+), 72 deletions(-) (limited to 'net') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f9c5a787d350..8f690e53dd89 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1212,6 +1212,21 @@ struct ieee80211_vht_cap { struct ieee80211_vht_mcs_info supp_mcs; } __packed; +/** + * enum ieee80211_vht_chanwidth - VHT channel width + * @IEEE80211_VHT_CHANWIDTH_USE_HT: use the HT operation IE to + * determine the channel width (20 or 40 MHz) + * @IEEE80211_VHT_CHANWIDTH_80MHZ: 80 MHz bandwidth + * @IEEE80211_VHT_CHANWIDTH_160MHZ: 160 MHz bandwidth + * @IEEE80211_VHT_CHANWIDTH_80P80MHZ: 80+80 MHz bandwidth + */ +enum ieee80211_vht_chanwidth { + IEEE80211_VHT_CHANWIDTH_USE_HT = 0, + IEEE80211_VHT_CHANWIDTH_80MHZ = 1, + IEEE80211_VHT_CHANWIDTH_160MHZ = 2, + IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3, +}; + /** * struct ieee80211_vht_operation - VHT operation IE * diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0a8f83d142f9..8d8bf7136386 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -371,6 +371,8 @@ enum ieee80211_sta_flags { IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), IEEE80211_STA_DISABLE_40MHZ = BIT(10), IEEE80211_STA_DISABLE_VHT = BIT(11), + IEEE80211_STA_DISABLE_80P80MHZ = BIT(12), + IEEE80211_STA_DISABLE_160MHZ = BIT(13), }; struct ieee80211_mgd_auth_data { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d2a4f78b4b0f..35d8cffc973a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -354,6 +354,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, /* determine capability flags */ cap = vht_cap.cap; + if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) { + cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + + if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) { + cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; + cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + /* reserve and fill IE */ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); @@ -543,6 +553,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) offset = noffset; } + if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) && + !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))) + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, sband, chan, sdata->smps_mode); @@ -3183,23 +3197,270 @@ int ieee80211_max_network_latency(struct notifier_block *nb, return 0; } +static u32 chandef_downgrade(struct cfg80211_chan_def *c) +{ + u32 ret; + int tmp; + + switch (c->width) { + case NL80211_CHAN_WIDTH_20: + c->width = NL80211_CHAN_WIDTH_20_NOHT; + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_40: + c->width = NL80211_CHAN_WIDTH_20; + c->center_freq1 = c->chan->center_freq; + ret = IEEE80211_STA_DISABLE_40MHZ | + IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_80: + tmp = (30 + c->chan->center_freq - c->center_freq1)/20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; + c->width = NL80211_CHAN_WIDTH_40; + ret = IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_80P80: + c->center_freq2 = 0; + c->width = NL80211_CHAN_WIDTH_80; + ret = IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ; + break; + case NL80211_CHAN_WIDTH_160: + /* n_P20 */ + tmp = (70 + c->chan->center_freq - c->center_freq1)/20; + /* n_P80 */ + tmp /= 4; + c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; + c->width = NL80211_CHAN_WIDTH_80; + ret = IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ; + break; + default: + case NL80211_CHAN_WIDTH_20_NOHT: + WARN_ON_ONCE(1); + c->width = NL80211_CHAN_WIDTH_20_NOHT; + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + break; + } + + WARN_ON_ONCE(!cfg80211_chandef_valid(c)); + + return ret; +} + +static u32 +ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct ieee80211_channel *channel, + const struct ieee80211_ht_operation *ht_oper, + const struct ieee80211_vht_operation *vht_oper, + struct cfg80211_chan_def *chandef) +{ + struct cfg80211_chan_def vht_chandef; + u32 ht_cfreq, ret; + + chandef->chan = channel; + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = channel->center_freq; + chandef->center_freq2 = 0; + + if (!ht_oper || !sband->ht_cap.ht_supported) { + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + goto out; + } + + chandef->width = NL80211_CHAN_WIDTH_20; + + ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, + channel->band); + /* check that channel matches the right operating channel */ + if (channel->center_freq != ht_cfreq) { + /* + * It's possible that some APs are confused here; + * Netgear WNDR3700 sometimes reports 4 higher than + * the actual channel in association responses, but + * since we look at probe response/beacon data here + * it should be OK. + */ + sdata_info(sdata, + "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", + channel->center_freq, ht_cfreq, + ht_oper->primary_chan, channel->band); + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + goto out; + } + + /* check 40 MHz support, if we have it */ + if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 += 10; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 -= 10; + break; + } + } else { + /* 40 MHz (and 80 MHz) must be supported for VHT */ + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + if (!vht_oper || !sband->vht_cap.vht_supported) { + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + vht_chandef.chan = channel; + vht_chandef.center_freq1 = + ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx, + channel->band); + vht_chandef.center_freq2 = 0; + + if (vht_oper->center_freq_seg2_idx) + vht_chandef.center_freq2 = + ieee80211_channel_to_frequency( + vht_oper->center_freq_seg2_idx, + channel->band); + + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_USE_HT: + vht_chandef.width = chandef->width; + break; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + vht_chandef.width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + vht_chandef.width = NL80211_CHAN_WIDTH_160; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + vht_chandef.width = NL80211_CHAN_WIDTH_80P80; + break; + default: + sdata_info(sdata, + "AP VHT operation IE has invalid channel width (%d), disable VHT\n", + vht_oper->chan_width); + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + if (!cfg80211_chandef_valid(&vht_chandef)) { + sdata_info(sdata, + "AP VHT information is invalid, disable VHT\n"); + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + if (cfg80211_chandef_identical(chandef, &vht_chandef)) { + ret = 0; + goto out; + } + + if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { + sdata_info(sdata, + "AP VHT information doesn't match HT, disable VHT\n"); + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + *chandef = vht_chandef; + + ret = 0; + + while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, + IEEE80211_CHAN_DISABLED)) { + if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { + ret = IEEE80211_STA_DISABLE_HT | + IEEE80211_STA_DISABLE_VHT; + goto out; + } + + ret = chandef_downgrade(chandef); + } + + if (chandef->width != vht_chandef.width) + sdata_info(sdata, + "local regulatory prevented using AP HT/VHT configuration, downgraded\n"); + +out: + WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); + return ret; +} + +static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, + struct cfg80211_bss *cbss) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *ht_cap_ie, *vht_cap_ie; + const struct ieee80211_ht_cap *ht_cap; + const struct ieee80211_vht_cap *vht_cap; + u8 chains = 1; + + if (ifmgd->flags & IEEE80211_STA_DISABLE_HT) + return chains; + + ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, + cbss->information_elements, + cbss->len_information_elements); + if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { + ht_cap = (void *)(ht_cap_ie + 2); + chains = ieee80211_mcs_to_chains(&ht_cap->mcs); + /* + * TODO: use "Tx Maximum Number Spatial Streams Supported" and + * "Tx Unequal Modulation Supported" fields. + */ + } + + if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) + return chains; + + vht_cap_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, + cbss->information_elements, + cbss->len_information_elements); + if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) { + u8 nss; + u16 tx_mcs_map; + + vht_cap = (void *)(vht_cap_ie + 2); + tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); + for (nss = 8; nss > 0; nss--) { + if (((tx_mcs_map >> (2 * (nss - 1))) & 3) != + IEEE80211_VHT_MCS_NOT_SUPPORTED) + break; + } + /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */ + chains = max(chains, nss); + } + + return chains; +} + static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - int ht_cfreq; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - const u8 *ht_oper_ie; const struct ieee80211_ht_operation *ht_oper = NULL; + const struct ieee80211_vht_operation *vht_oper = NULL; struct ieee80211_supported_band *sband; struct cfg80211_chan_def chandef; + int ret; sband = local->hw.wiphy->bands[cbss->channel->band]; - ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ; + ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ | + IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ); + + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && + sband->ht_cap.ht_supported) { + const u8 *ht_oper_ie; - if (sband->ht_cap.ht_supported) { ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION, cbss->information_elements, cbss->len_information_elements); @@ -3207,82 +3468,45 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ht_oper = (void *)(ht_oper_ie + 2); } - if (ht_oper) { - ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, - cbss->channel->band); - /* check that channel matches the right operating channel */ - if (cbss->channel->center_freq != ht_cfreq) { - /* - * It's possible that some APs are confused here; - * Netgear WNDR3700 sometimes reports 4 higher than - * the actual channel in association responses, but - * since we look at probe response/beacon data here - * it should be OK. - */ + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && + sband->vht_cap.vht_supported) { + const u8 *vht_oper_ie; + + vht_oper_ie = cfg80211_find_ie(WLAN_EID_VHT_OPERATION, + cbss->information_elements, + cbss->len_information_elements); + if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper)) + vht_oper = (void *)(vht_oper_ie + 2); + if (vht_oper && !ht_oper) { + vht_oper = NULL; sdata_info(sdata, - "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", - cbss->channel->center_freq, - ht_cfreq, ht_oper->primary_chan, - cbss->channel->band); - ht_oper = NULL; + "AP advertised VHT without HT, disabling both\n"); + sdata->flags |= IEEE80211_STA_DISABLE_HT; + sdata->flags |= IEEE80211_STA_DISABLE_VHT; } } - if (ht_oper) { - /* - * cfg80211 already verified that the channel itself can - * be used, but it didn't check that we can do the right - * HT type, so do that here as well. If HT40 isn't allowed - * on this channel, disable 40 MHz operation. - */ - const u8 *ht_cap_ie; - const struct ieee80211_ht_cap *ht_cap; - u8 chains = 1; - - channel_type = NL80211_CHAN_HT20; - - if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - switch (ht_oper->ht_param & - IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - if (cbss->channel->flags & - IEEE80211_CHAN_NO_HT40PLUS) - ifmgd->flags |= - IEEE80211_STA_DISABLE_40MHZ; - else - channel_type = NL80211_CHAN_HT40PLUS; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - if (cbss->channel->flags & - IEEE80211_CHAN_NO_HT40MINUS) - ifmgd->flags |= - IEEE80211_STA_DISABLE_40MHZ; - else - channel_type = NL80211_CHAN_HT40MINUS; - break; - } - } + ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, + cbss->channel, + ht_oper, vht_oper, + &chandef); - ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, - cbss->information_elements, - cbss->len_information_elements); - if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { - ht_cap = (void *)(ht_cap_ie + 2); - chains = ieee80211_mcs_to_chains(&ht_cap->mcs); - } - sdata->needed_rx_chains = min(chains, local->rx_chains); - } else { - sdata->needed_rx_chains = 1; - sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT; - } + sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), + local->rx_chains); /* will change later if needed */ sdata->smps_mode = IEEE80211_SMPS_OFF; - ieee80211_vif_release_channel(sdata); - cfg80211_chandef_create(&chandef, cbss->channel, channel_type); - return ieee80211_vif_use_channel(sdata, &chandef, - IEEE80211_CHANCTX_SHARED); + /* + * If this fails (possibly due to channel context sharing + * on incompatible channels, e.g. 80+80 and 160 sharing the + * same control channel) try to use a smaller bandwidth. + */ + ret = ieee80211_vif_use_channel(sdata, &chandef, + IEEE80211_CHANCTX_SHARED); + while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) + ifmgd->flags |= chandef_downgrade(&chandef); + return ret; } static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, -- cgit v1.2.3 From 5164892184d1b9ce19e45e97e9ca405ea8b9ceb2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Nov 2012 23:00:18 +0100 Subject: mac80211: support (partial) VHT radiotap information Add some information that we have about VHT to radiotap. This at least lets one see the MCS and NSS information. Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 24 ++++++++++++++++++++++++ include/net/mac80211.h | 5 +++++ net/mac80211/main.c | 2 ++ net/mac80211/rx.c | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+) (limited to 'net') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 7f0df133d119..c3999632e616 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -186,6 +186,10 @@ struct ieee80211_radiotap_header { * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless * * Contains the AMPDU information for the subframe. + * + * IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16 + * + * Contains VHT information about this frame. */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, @@ -209,6 +213,7 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_MCS = 19, IEEE80211_RADIOTAP_AMPDU_STATUS = 20, + IEEE80211_RADIOTAP_VHT = 21, /* valid in every it_present bitmap, even vendor namespaces */ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, @@ -282,6 +287,25 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 +/* For IEEE80211_RADIOTAP_VHT */ +#define IEEE80211_RADIOTAP_VHT_KNOWN_STBC 0x0001 +#define IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA 0x0002 +#define IEEE80211_RADIOTAP_VHT_KNOWN_GI 0x0004 +#define IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS 0x0008 +#define IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM 0x0010 +#define IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED 0x0020 +#define IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH 0x0040 +#define IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID 0x0080 +#define IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID 0x0100 + +#define IEEE80211_RADIOTAP_VHT_FLAG_STBC 0x01 +#define IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA 0x02 +#define IEEE80211_RADIOTAP_VHT_FLAG_SGI 0x04 +#define IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 0x08 +#define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10 +#define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED 0x20 + + /* helpers */ static inline int ieee80211_get_radiotap_len(unsigned char *data) { diff --git a/include/net/mac80211.h b/include/net/mac80211.h index db7680acd0da..e806f1d81f66 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1473,6 +1473,10 @@ enum ieee80211_hw_flags { * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only * adding _BW is supported today. * + * @radiotap_vht_details: lists which VHT MCS information the HW reports, + * the default is _GI | _BANDWIDTH. + * Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values. + * * @netdev_features: netdev features to be set in each netdev created * from this HW. Note only HW checksum features are currently * compatible with mac80211. Other feature bits will be rejected. @@ -1499,6 +1503,7 @@ struct ieee80211_hw { u8 max_tx_aggregation_subframes; u8 offchannel_tx_hw_queue; u8 radiotap_mcs_details; + u16 radiotap_vht_details; netdev_features_t netdev_features; }; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6e933409979a..c4ed83b74e52 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -638,6 +638,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_BW; + local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI | + IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 825f33cf7bbc..9b13b8b24245 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -111,6 +111,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local, len += 8; } + if (status->flag & RX_FLAG_VHT) { + len = ALIGN(len, 2); + len += 12; + } + if (status->vendor_radiotap_len) { if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) status->vendor_radiotap_align = 1; @@ -297,6 +302,41 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = 0; } + if (status->flag & RX_FLAG_VHT) { + u16 known = local->hw.radiotap_vht_details; + + rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); + /* known field - how to handle 80+80? */ + if (status->flag & RX_FLAG_80P80MHZ) + known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; + put_unaligned_le16(known, pos); + pos += 2; + /* flags */ + if (status->flag & RX_FLAG_SHORT_GI) + *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; + pos++; + /* bandwidth */ + if (status->flag & RX_FLAG_80MHZ) + *pos++ = 4; + else if (status->flag & RX_FLAG_80P80MHZ) + *pos++ = 0; /* marked not known above */ + else if (status->flag & RX_FLAG_160MHZ) + *pos++ = 11; + else if (status->flag & RX_FLAG_40MHZ) + *pos++ = 1; + else /* 20 MHz */ + *pos++ = 0; + /* MCS/NSS */ + *pos = (status->rate_idx << 4) | status->vht_nss; + pos += 4; + /* coding field */ + pos++; + /* group ID */ + pos++; + /* partial_aid */ + pos += 2; + } + if (status->vendor_radiotap_len) { /* ensure 2 byte alignment for the vendor field as required */ if ((pos - (u8 *)rthdr) & 1) -- cgit v1.2.3 From 53cabad70ecf0c245b41285de64a74a6c3ee9933 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 14 Nov 2012 15:17:28 +0100 Subject: nl80211: support P2P GO powersave configuration If a driver supports P2P GO powersave, allow it to set the new feature flags for it and allow userspace to configure the parameters for it. This can be done at GO startup and later changed with SET_BSS. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 ++++++ include/uapi/linux/nl80211.h | 16 +++++++++++++ net/wireless/nl80211.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a238f41e55c2..731b48fa238b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -538,6 +538,8 @@ struct cfg80211_beacon_data { * @privacy: the BSS uses privacy * @auth_type: Authentication type (algorithm) * @inactivity_timeout: time in seconds to determine station's inactivity. + * @p2p_ctwindow: P2P CT Window + * @p2p_opp_ps: P2P opportunistic PS */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -552,6 +554,8 @@ struct cfg80211_ap_settings { bool privacy; enum nl80211_auth_type auth_type; int inactivity_timeout; + u8 p2p_ctwindow; + bool p2p_opp_ps; }; /** @@ -913,6 +917,8 @@ struct mpath_info { * @ap_isolate: do not forward packets between connected stations * @ht_opmode: HT Operation mode * (u16 = opmode, -1 = do not change) + * @p2p_ctwindow: P2P CT Window (-1 = no change) + * @p2p_opp_ps: P2P opportunistic PS (-1 = no change) */ struct bss_parameters { int use_cts_prot; @@ -922,6 +928,7 @@ struct bss_parameters { u8 basic_rates_len; int ap_isolate; int ht_opmode; + s8 p2p_ctwindow, p2p_opp_ps; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 33a417481ad8..e3e19f8b16f2 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1303,6 +1303,13 @@ enum nl80211_commands { * * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) * + * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with + * the START_AP and SET_BSS commands + * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the + * START_AP and SET_BSS commands. This can have the values 0 or 1; + * if not given in START_AP 0 is assumed, if not given in SET_BSS + * no change is made. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1570,6 +1577,9 @@ enum nl80211_attrs { NL80211_ATTR_CENTER_FREQ1, NL80211_ATTR_CENTER_FREQ2, + NL80211_ATTR_P2P_CTWINDOW, + NL80211_ATTR_P2P_OPPPS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3126,6 +3136,10 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform * OBSS scans and generate 20/40 BSS coex reports. This flag is used only * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. + * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window + * setting + * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic + * powersave */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3139,6 +3153,8 @@ enum nl80211_feature_flags { NL80211_FEATURE_AP_SCAN = 1 << 8, NL80211_FEATURE_VIF_TXPOWER = 1 << 9, NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, + NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, + NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index eb0aa71a02b3..7f53aafd47f7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -363,6 +363,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, }, [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN }, [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, + [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, }; /* policy for the key attributes */ @@ -2702,6 +2704,32 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); } + if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + params.p2p_ctwindow = + nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]); + if (params.p2p_ctwindow > 127) + return -EINVAL; + if (params.p2p_ctwindow != 0 && + !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { + u8 tmp; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); + if (tmp > 1) + return -EINVAL; + params.p2p_opp_ps = tmp; + if (params.p2p_opp_ps != 0 && + !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); if (err) @@ -3668,6 +3696,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) params.use_short_slot_time = -1; params.ap_isolate = -1; params.ht_opmode = -1; + params.p2p_ctwindow = -1; + params.p2p_opp_ps = -1; if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) params.use_cts_prot = @@ -3690,6 +3720,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) params.ht_opmode = nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); + if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + params.p2p_ctwindow = + nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]); + if (params.p2p_ctwindow < 0) + return -EINVAL; + if (params.p2p_ctwindow != 0 && + !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { + u8 tmp; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); + if (tmp > 1) + return -EINVAL; + params.p2p_opp_ps = tmp; + if (params.p2p_opp_ps && + !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) + return -EINVAL; + } + if (!rdev->ops->change_bss) return -EOPNOTSUPP; -- cgit v1.2.3 From 339afbf4819e5c7c0a0422af43b8c2eccd059abf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 14 Nov 2012 15:21:17 +0100 Subject: mac80211: support P2P GO powersave configuration If the low-level driver wants to support P2P GO powersave configuration, it must set the cfg80211 flags and mac80211 will pass the parameters to it. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 16 +++++++++++++++- net/mac80211/util.c | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5d30e5f57ff0..97cf615eb36a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -893,7 +893,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, u32 changed = BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | - BSS_CHANGED_SSID; + BSS_CHANGED_SSID | + BSS_CHANGED_P2P_PS; int err; old = rtnl_dereference(sdata->u.ap.beacon); @@ -932,6 +933,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->vif.bss_conf.hidden_ssid = (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); + sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow; + sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps; + err = ieee80211_assign_beacon(sdata, ¶ms->beacon); if (err < 0) return err; @@ -1807,6 +1811,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy, changed |= BSS_CHANGED_HT; } + if (params->p2p_ctwindow >= 0) { + sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow; + changed |= BSS_CHANGED_P2P_PS; + } + + if (params->p2p_opp_ps >= 0) { + sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps; + changed |= BSS_CHANGED_P2P_PS; + } + ieee80211_bss_info_change_notify(sdata, changed); return 0; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index dc7f6b264593..6e4c8bd5bfee 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1527,7 +1527,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) changed |= BSS_CHANGED_IBSS; /* fall through */ case NL80211_IFTYPE_AP: - changed |= BSS_CHANGED_SSID; + changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS; if (sdata->vif.type == NL80211_IFTYPE_AP) { changed |= BSS_CHANGED_AP_PROBE_RESP; -- cgit v1.2.3 From d9d8b01978f6510a262ddc4da3b3694d5b149cfe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 26 Nov 2012 12:51:52 +0100 Subject: nl80211: remove unnecessary checks The CQM TX-error rate/interval can't be less than zero since they're unsigned values, remove checks. Also fix indentation of the function. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7f53aafd47f7..7cce43bc9489 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6555,14 +6555,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { }; static int nl80211_set_cqm_txe(struct genl_info *info, - u32 rate, u32 pkts, u32 intvl) + 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)) + if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL) return -EINVAL; wdev = dev->ieee80211_ptr; -- cgit v1.2.3 From 0624760c96da3ffeaec8027a016475ae752d75e1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Nov 2012 20:31:19 +0300 Subject: mac80211: fix potential NULL dereference Smatch complains that we could dereference skb later in the function. It's probably unlikely, but we may as well return here and avoid it. Signed-off-by: Dan Carpenter [change summary] Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9b13b8b24245..db343fa8033c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -49,7 +49,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, /* driver bug */ WARN_ON(1); dev_kfree_skb(skb); - skb = NULL; + return NULL; } } -- cgit v1.2.3 From 478622e81c735ecd83d45ee6be3fd45d500aaa3b Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Tue, 27 Nov 2012 18:23:06 +0100 Subject: mac80211: reject setting masked mac addresses If a driver registers an address mask we should ensure that no interface gets an address assigned that isn't covered by the registered address mask. This prevents invalid configurations from reaching the device and causing problems. Signed-off-by: Helmut Schaa [change function flow to reduce indentation, fix locking] Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5331662489f7..40c36d5d7377 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -223,6 +223,47 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) return 0; } +static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr) +{ + struct ieee80211_sub_if_data *sdata; + u64 new, mask, tmp; + u8 *m; + int ret = 0; + + if (is_zero_ether_addr(local->hw.wiphy->addr_mask)) + return 0; + + m = addr; + new = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | + ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | + ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); + + m = local->hw.wiphy->addr_mask; + mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | + ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | + ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); + + + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type == NL80211_IFTYPE_MONITOR) + continue; + + m = sdata->vif.addr; + tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | + ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | + ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); + + if ((new & ~mask) != (tmp & ~mask)) { + ret = -EINVAL; + break; + } + } + mutex_unlock(&local->iflist_mtx); + + return ret; +} + static int ieee80211_change_mac(struct net_device *dev, void *addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -232,6 +273,10 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr) if (ieee80211_sdata_running(sdata)) return -EBUSY; + ret = ieee80211_verify_mac(sdata->local, sa->sa_data); + if (ret) + return ret; + ret = eth_mac_addr(dev, sa); if (ret == 0) -- cgit v1.2.3 From 59cf1d65f7d69739a29a16fe678ebc4e1215e9c0 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Tue, 27 Nov 2012 18:03:13 +0100 Subject: mac80211: don't reinit rate control when mesh sta exists This fixes some unintended resets of the rate control statistics when minstrel_ht is used resulting in non-optimal throughput on mesh links. Tested-by: Emanuel Taube Signed-off-by: Helmut Schaa Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index ca52dfdd5375..4b274e9c91a5 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -391,7 +391,8 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, sta->ch_width = chandef.width; } - rate_control_rate_init(sta); + if (insert) + rate_control_rate_init(sta); spin_unlock_bh(&sta->lock); if (insert && sta_info_insert(sta)) -- cgit v1.2.3 From 289814918ce3af1296ac7d9b05508bde64e97348 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 08:40:04 +0100 Subject: NFC: Fix incorrect llcp pointer dereference nfc_llcp_ns(s) dereferences the s pointer which is freed a line above. In a result, it can produce a crash or you will read incorrect value. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- net/nfc/llcp/llcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 9e8f4b2801f6..3d63636120b1 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -903,15 +903,18 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local, /* Remove skbs from the pending queue */ if (llcp_sock->send_ack_n != nr) { struct sk_buff *s, *tmp; + u8 n; llcp_sock->send_ack_n = nr; /* Remove and free all skbs until ns == nr */ skb_queue_walk_safe(&llcp_sock->tx_pending_queue, s, tmp) { + n = nfc_llcp_ns(s); + skb_unlink(s, &llcp_sock->tx_pending_queue); kfree_skb(s); - if (nfc_llcp_ns(s) == nr) + if (n == nr) break; } -- cgit v1.2.3 From dbeca2ea4642d9de0a59ac141b2825ea6804f408 Mon Sep 17 00:00:00 2001 From: Mahesh Palivela Date: Thu, 29 Nov 2012 14:11:07 +0530 Subject: cfg80211: Remove unused VHT chan code Cleanup of unused VHT channel config related code. Signed-off-by: Mahesh Palivela Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7cce43bc9489..0e7e8d533d98 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1371,9 +1371,7 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, struct genl_info *info, struct cfg80211_chan_def *chandef) { - struct ieee80211_sta_ht_cap *ht_cap; - struct ieee80211_sta_vht_cap *vht_cap; - u32 control_freq, width; + u32 control_freq; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; @@ -1419,33 +1417,9 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, info->attrs[NL80211_ATTR_CENTER_FREQ2]); } - ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; - vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap; - if (!cfg80211_chandef_valid(chandef)) return -EINVAL; - switch (chandef->width) { - case NL80211_CHAN_WIDTH_20: - case NL80211_CHAN_WIDTH_20_NOHT: - width = 20; - break; - case NL80211_CHAN_WIDTH_40: - width = 40; - break; - case NL80211_CHAN_WIDTH_80: - width = 80; - break; - case NL80211_CHAN_WIDTH_80P80: - width = 80; - break; - case NL80211_CHAN_WIDTH_160: - width = 160; - break; - default: - return -EINVAL; - } - if (!cfg80211_chandef_usable(&rdev->wiphy, chandef, IEEE80211_CHAN_DISABLED)) return -EINVAL; -- cgit v1.2.3 From db94357dbd79359496cd5189640cc36bb3c35733 Mon Sep 17 00:00:00 2001 From: Saravana Date: Wed, 28 Nov 2012 18:27:09 +0530 Subject: mac80211: add debugfs file for last ack signal Add a debugfs file showing the signal strength of the ack frame that is received for the currently sent tx packet Signed-off-by: Saravana Signed-off-by: Johannes Berg --- net/mac80211/debugfs_sta.c | 2 ++ net/mac80211/sta_info.h | 2 ++ net/mac80211/status.c | 3 +++ 3 files changed, 7 insertions(+) (limited to 'net') diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 89281d24b094..15669415ee98 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -53,6 +53,7 @@ static const struct file_operations sta_ ##name## _ops = { \ STA_FILE(aid, sta.aid, D); STA_FILE(dev, sdata->name, S); STA_FILE(last_signal, last_signal, D); +STA_FILE(last_ack_signal, last_ack_signal, D); static ssize_t sta_flags_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) @@ -369,6 +370,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(dev); DEBUGFS_ADD(last_signal); DEBUGFS_ADD(ht_capa); + DEBUGFS_ADD(last_ack_signal); DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 6835cea4e402..dce76939f526 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -250,6 +250,7 @@ struct sta_ampdu_mlme { * @rx_dropped: number of dropped MPDUs from this STA * @last_signal: signal of last received frame from this STA * @avg_signal: moving average of signal of received frames from this STA + * @last_ack_signal: signal of last received Ack frame from this STA * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue) * @tx_filtered_count: number of frames the hardware filtered for this STA * @tx_retry_failed: number of frames that failed retry @@ -329,6 +330,7 @@ struct sta_info { unsigned long rx_dropped; int last_signal; struct ewma avg_signal; + int last_ack_signal; /* Plus 1 for non-QoS frames */ __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 2d931ad0e90a..6400037dd8ec 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -539,6 +539,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) sta->lost_packets = 0; } } + + if (acked) + sta->last_ack_signal = info->status.ack_signal; } rcu_read_unlock(); -- cgit v1.2.3 From 91b8c050b2a2ed3a1277de53d90e1899f28225e8 Mon Sep 17 00:00:00 2001 From: Saravana Date: Thu, 29 Nov 2012 19:22:37 +0530 Subject: mac80211: add debugfs file for current tx rate Add a debugfs file showing the current tx rate. The information available in the rc_stats file doesn't evidently provides us the current tx rate. This patch adds the support for the same. Signed-off-by: Saravana Signed-off-by: Johannes Berg --- net/mac80211/debugfs_sta.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'net') diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 15669415ee98..b6fd9f6ed80c 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -322,6 +322,21 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, } STA_OPS(ht_capa); +static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct rate_info rinfo; + u16 rate; + sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo); + rate = cfg80211_calculate_bitrate(&rinfo); + + return mac80211_format_buffer(userbuf, count, ppos, + "%d.%d MBit/s\n", + rate/10, rate%10); +} +STA_OPS(current_tx_rate); + #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, \ sta->debugfs.dir, sta, &sta_ ##name## _ops); @@ -371,6 +386,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(last_signal); DEBUGFS_ADD(ht_capa); DEBUGFS_ADD(last_ack_signal); + DEBUGFS_ADD(current_tx_rate); DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); -- cgit v1.2.3 From 003e676af5044c2168dafbb49c7b8d61dd68cc60 Mon Sep 17 00:00:00 2001 From: Saravana Date: Wed, 28 Nov 2012 18:29:38 +0530 Subject: mac80211: re-organize the rx rate calculation logic Currently the logic to fill a struct rate_info with a STA's last RX rate is accessible only in the cfg.c. As the RX rate calculation might be needed elsewhere, split this out into a separate function. Signed-off-by: Saravana [fix various whitespace issues, reword commit log] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 61 ++++++++++++++++++++++++++----------------------- net/mac80211/sta_info.h | 2 ++ 2 files changed, 35 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 97cf615eb36a..d6456107c373 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -398,6 +398,38 @@ void sta_set_rate_info_tx(struct sta_info *sta, rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; } +void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) +{ + rinfo->flags = 0; + + if (sta->last_rx_rate_flag & RX_FLAG_HT) { + rinfo->flags |= RATE_INFO_FLAGS_MCS; + rinfo->mcs = sta->last_rx_rate_idx; + } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) { + rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; + rinfo->nss = sta->last_rx_rate_vht_nss; + rinfo->mcs = sta->last_rx_rate_idx; + } else { + struct ieee80211_supported_band *sband; + + sband = sta->local->hw.wiphy->bands[ + ieee80211_get_sdata_band(sta->sdata)]; + rinfo->legacy = + sband->bitrates[sta->last_rx_rate_idx].bitrate; + } + + if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) + rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + if (sta->last_rx_rate_flag & RX_FLAG_80MHZ) + rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; + if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) + rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; + if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) + rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; +} + static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -444,34 +476,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) } sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); - - sinfo->rxrate.flags = 0; - if (sta->last_rx_rate_flag & RX_FLAG_HT) { - sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS; - sinfo->rxrate.mcs = sta->last_rx_rate_idx; - } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) { - sinfo->rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS; - sinfo->rxrate.nss = sta->last_rx_rate_vht_nss; - sinfo->rxrate.mcs = sta->last_rx_rate_idx; - } else { - struct ieee80211_supported_band *sband; - - sband = sta->local->hw.wiphy->bands[ - ieee80211_get_sdata_band(sta->sdata)]; - sinfo->rxrate.legacy = - sband->bitrates[sta->last_rx_rate_idx].bitrate; - } - - if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) - sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; - if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) - sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - if (sta->last_rx_rate_flag & RX_FLAG_80MHZ) - sinfo->rxrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; - if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) - sinfo->rxrate.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; - if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) - sinfo->rxrate.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; + sta_set_rate_info_rx(sta, &sinfo->rxrate); if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index dce76939f526..1489bca9ea97 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -554,6 +554,8 @@ int sta_info_flush(struct ieee80211_local *local, void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); +void sta_set_rate_info_rx(struct sta_info *sta, + struct rate_info *rinfo); void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time); -- cgit v1.2.3 From 1f2e651c9aa4be5ace193b6a8f48e4b068a60207 Mon Sep 17 00:00:00 2001 From: Saravana Date: Thu, 29 Nov 2012 19:54:19 +0530 Subject: mac80211: add debugfs file for last rx rate Add a debugfs file showing the rate at which the last packet is received. Signed-off-by: Saravana [fix whitespace] Signed-off-by: Johannes Berg --- net/mac80211/debugfs_sta.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'net') diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index b6fd9f6ed80c..49a1c70bbd70 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -337,6 +337,23 @@ static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf, } STA_OPS(current_tx_rate); +static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct rate_info rinfo; + u16 rate; + + sta_set_rate_info_rx(sta, &rinfo); + + rate = cfg80211_calculate_bitrate(&rinfo); + + return mac80211_format_buffer(userbuf, count, ppos, + "%d.%d MBit/s\n", + rate/10, rate%10); +} +STA_OPS(last_rx_rate); + #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, \ sta->debugfs.dir, sta, &sta_ ##name## _ops); @@ -387,6 +404,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(ht_capa); DEBUGFS_ADD(last_ack_signal); DEBUGFS_ADD(current_tx_rate); + DEBUGFS_ADD(last_rx_rate); DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); -- cgit v1.2.3 From c04d61500d706b20361eef02e819b9bed8e92c01 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 29 Nov 2012 18:37:22 +0100 Subject: nl80211: Fix HT_IBSS feature check in ibss_join There is a standalone if, seems to be a regression of commit "nl80211/cfg80211: add VHT MCS support". Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0e7e8d533d98..4a719770eaaf 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5502,6 +5502,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) + return -EINVAL; ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; -- cgit v1.2.3 From 372039031c62886c8c5b57a7b0111446e9caefa8 Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Fri, 23 Nov 2012 12:23:18 -0800 Subject: mac80211: fix for mesh sync to indicate TBTT adjustment Currently the mesh sync code checks, whether peers indicate TBTT adjustment, but it never sets the corresponding flag itself. By setting ifmsh->tbtt_adjusting to true, it will set the corresponding field in the mesh configuration IE of own beacons. This indication will be set in the current beacon. The TBTT adjustment will be performed afterwards, affecting the next beacon. Thus, the first beacon with stable TBTT will not indicate adjustment anymore and peers will continue tracking the new offset. Signed-off-by: Marco Porsch Signed-off-by: Johannes Berg --- net/mac80211/mesh_sync.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 0f40086cce18..aa8d1e437385 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -195,11 +195,15 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) ifmsh->sync_offset_clockdrift_max); set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); + + ifmsh->adjusting_tbtt = true; } else { msync_dbg(sdata, "TBTT : max clockdrift=%lld; too small to adjust\n", (long long)ifmsh->sync_offset_clockdrift_max); ifmsh->sync_offset_clockdrift_max = 0; + + ifmsh->adjusting_tbtt = false; } spin_unlock_bh(&ifmsh->sync_offset_lock); } -- cgit v1.2.3 From 41e31b8b902397d5d507b275050fcbe8adaf4ff4 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Fri, 30 Nov 2012 00:18:12 +0100 Subject: mac80211: allow userspace registration for probe requests in IBSS This change allows userspace to register for probe request frames on an IBSS interface. Userspace then has to handle them and send replies. Signed-off-by: Antonio Quartulli Signed-off-by: Johannes Berg --- net/mac80211/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index c4ed83b74e52..4ece42ca87bb 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -474,7 +474,8 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | - BIT(IEEE80211_STYPE_DEAUTH >> 4), + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), }, [NL80211_IFTYPE_STATION] = { .tx = 0xffff, -- cgit v1.2.3 From b629ea3db4c0da4465ffa532914e4a3081f93cdf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 28 Nov 2012 22:14:56 +0100 Subject: cfg80211: don't BUG_ON BSS struct issues There's no need to stop the machine, just leak the BSS entry if there's an issue with its hold counter when freeing. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9596015975d2..5af431f2f90a 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -26,6 +26,10 @@ static void bss_release(struct kref *ref) struct cfg80211_internal_bss *bss; bss = container_of(ref, struct cfg80211_internal_bss, ref); + + if (WARN_ON(atomic_read(&bss->hold))) + return; + if (bss->pub.free_priv) bss->pub.free_priv(&bss->pub); @@ -34,8 +38,6 @@ static void bss_release(struct kref *ref) if (bss->proberesp_ies_allocated) kfree(bss->pub.proberesp_ies); - BUG_ON(atomic_read(&bss->hold)); - kfree(bss); } -- cgit v1.2.3 From 915de2ff4a79f1f98362035060777b6c8ce889bb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 28 Nov 2012 22:39:37 +0100 Subject: cfg80211: fix whitespace in scan handling Fix a number of indentation and similar issues. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 5af431f2f90a..92339105aa3b 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -238,9 +238,8 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss; unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); - list_for_each_entry(bss, &dev->bss_list, list) { + list_for_each_entry(bss, &dev->bss_list, list) bss->ts -= age_jiffies; - } } void cfg80211_bss_expire(struct cfg80211_registered_device *dev) @@ -309,8 +308,7 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) return memcmp(ie1 + 2, ie2 + 2, ie1[1]); } -static bool is_bss(struct cfg80211_bss *a, - const u8 *bssid, +static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, const u8 *ssid, size_t ssid_len) { const u8 *ssidie; @@ -386,11 +384,10 @@ static bool is_mesh(struct cfg80211_bss *a, * part in the same mesh. */ return memcmp(ie + 2, meshcfg, - sizeof(struct ieee80211_meshconf_ie) - 2) == 0; + sizeof(struct ieee80211_meshconf_ie) - 2) == 0; } -static int cmp_bss_core(struct cfg80211_bss *a, - struct cfg80211_bss *b) +static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) { int r; @@ -435,8 +432,7 @@ static int cmp_bss(struct cfg80211_bss *a, b->len_information_elements); } -static int cmp_hidden_bss(struct cfg80211_bss *a, - struct cfg80211_bss *b) +static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) { const u8 *ie1; const u8 *ie2; @@ -448,11 +444,11 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, return r; ie1 = cfg80211_find_ie(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements); + a->information_elements, + a->len_information_elements); ie2 = cfg80211_find_ie(WLAN_EID_SSID, - b->information_elements, - b->len_information_elements); + b->information_elements, + b->len_information_elements); /* Key comparator must use same algorithm in any rb-tree * search function (order is important), otherwise ordering @@ -602,7 +598,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, static struct cfg80211_internal_bss * rb_find_hidden_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) + struct cfg80211_internal_bss *res) { struct rb_node *n = dev->bss_tree.rb_node; struct cfg80211_internal_bss *bss; @@ -625,7 +621,7 @@ rb_find_hidden_bss(struct cfg80211_registered_device *dev, static void copy_hidden_ies(struct cfg80211_internal_bss *res, - struct cfg80211_internal_bss *hidden) + struct cfg80211_internal_bss *hidden) { if (unlikely(res->pub.beacon_ies)) return; @@ -639,7 +635,7 @@ copy_hidden_ies(struct cfg80211_internal_bss *res, res->beacon_ies_allocated = true; res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, - res->pub.len_beacon_ies); + res->pub.len_beacon_ies); } static struct cfg80211_internal_bss * @@ -704,6 +700,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, found->pub.len_information_elements = found->pub.len_proberesp_ies; } + if (res->pub.beacon_ies) { size_t used = dev->wiphy.bss_priv_size + sizeof(*res); size_t ielen = res->pub.len_beacon_ies; -- cgit v1.2.3 From f94f8b168cf2e46da180bbba2afd626d7af0579d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 28 Nov 2012 22:42:34 +0100 Subject: cfg80211: fix cmp_hidden_bss The cmp_bss() comparator function uses memcmp() to compare the SSID. This means that cmp_hidden_bss() needs to similarly return a number bigger than zero (use 1) instead of -1 when ie1 is bigger than ie2, which is the case if an ie2 byte is non-zero. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 92339105aa3b..834e0d153fbe 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -450,10 +450,16 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) b->information_elements, b->len_information_elements); - /* Key comparator must use same algorithm in any rb-tree + /* + * Key comparator must use same algorithm in any rb-tree * search function (order is important), otherwise ordering * of items in the tree is broken and search gives incorrect - * results. This code uses same order as cmp_ies() does. */ + * results. This code uses same order as cmp_ies() does. + * + * Note that due to the differring behaviour with hidden SSIDs + * this function only works when "b" is the tree element and + * "a" is the key we're looking for. + */ /* sort missing IE before (left of) present IE */ if (!ie1) @@ -469,10 +475,14 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; - /* zeroed SSID ie is another indication of a hidden bss */ + /* + * zeroed SSID ie is another indication of a hidden bss; + * if it isn't zeroed just return the regular sort value + * to find the next candidate + */ for (i = 0; i < ie2[1]; i++) if (ie2[i + 2]) - return -1; + return memcmp(ie1 + 2, ie2 + 2, ie1[1]); return 0; } -- cgit v1.2.3 From c604b9f219422e969fe371cc7259de34c3c5601d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Nov 2012 12:45:18 +0100 Subject: mac80211: make ieee80211_build_preq_ies safer Instead of assuming 200 bytes are always enough for all the IEs we add, give the length of the buffer to the function and warn instead of overrunning. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/scan.c | 21 ++++++++++++--------- net/mac80211/util.c | 34 +++++++++++++++++++++++++++------- 3 files changed, 41 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8d8bf7136386..53e97dc4e06a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1034,6 +1034,7 @@ struct ieee80211_local { enum ieee80211_band hw_scan_band; int scan_channel_idx; int scan_ies_len; + int hw_scan_ies_bufsize; struct work_struct sched_scan_stopped_work; struct ieee80211_sub_if_data __rcu *sched_scan_sdata; @@ -1573,7 +1574,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *bssid, u16 stype, u16 reason, bool send_frame, u8 *frame_buf); int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, - const u8 *ie, size_t ie_len, + size_t buffer_len, const u8 *ie, size_t ie_len, enum ieee80211_band band, u32 rate_mask, u8 channel); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index ddd1a9aaff38..d7c190b80670 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -247,6 +247,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) local->hw_scan_req->n_channels = n_chans; ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, + local->hw_scan_ies_bufsize, req->ie, req->ie_len, band, req->rates[band], 0); local->hw_scan_req->ie_len = ielen; @@ -445,11 +446,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (local->ops->hw_scan) { u8 *ies; + local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN + + local->scan_ies_len + + req->ie_len; local->hw_scan_req = kmalloc( sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]) + - 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + - req->ie_len, GFP_KERNEL); + local->hw_scan_ies_bufsize, GFP_KERNEL); if (!local->hw_scan_req) return -ENOMEM; @@ -928,7 +931,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_sched_scan_ies sched_scan_ies; - int ret, i; + int ret, i, iebufsz; + + iebufsz = 2 + IEEE80211_MAX_SSID_LEN + + local->scan_ies_len + req->ie_len; mutex_lock(&local->mtx); @@ -946,10 +952,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, if (!local->hw.wiphy->bands[i]) continue; - sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN + - local->scan_ies_len + - req->ie_len, - GFP_KERNEL); + sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL); if (!sched_scan_ies.ie[i]) { ret = -ENOMEM; goto out_free; @@ -957,8 +960,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, sched_scan_ies.len[i] = ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], - req->ie, req->ie_len, i, - (u32) -1, 0); + iebufsz, req->ie, req->ie_len, + i, (u32) -1, 0); } ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 6e4c8bd5bfee..f119b1b6c1a7 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1107,12 +1107,12 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, } int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, - const u8 *ie, size_t ie_len, + size_t buffer_len, const u8 *ie, size_t ie_len, enum ieee80211_band band, u32 rate_mask, u8 channel) { struct ieee80211_supported_band *sband; - u8 *pos; + u8 *pos = buffer, *end = buffer + buffer_len; size_t offset = 0, noffset; int supp_rates_len, i; u8 rates[32]; @@ -1123,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, if (WARN_ON_ONCE(!sband)) return 0; - pos = buffer; - num_rates = 0; for (i = 0; i < sband->n_bitrates; i++) { if ((BIT(i) & rate_mask) == 0) @@ -1134,6 +1132,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, supp_rates_len = min_t(int, num_rates, 8); + if (end - pos < 2 + supp_rates_len) + goto out_err; *pos++ = WLAN_EID_SUPP_RATES; *pos++ = supp_rates_len; memcpy(pos, rates, supp_rates_len); @@ -1150,6 +1150,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, before_extrates, ARRAY_SIZE(before_extrates), offset); + if (end - pos < noffset - offset) + goto out_err; memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; offset = noffset; @@ -1157,6 +1159,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, ext_rates_len = num_rates - supp_rates_len; if (ext_rates_len > 0) { + if (end - pos < 2 + ext_rates_len) + goto out_err; *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = ext_rates_len; memcpy(pos, rates + supp_rates_len, ext_rates_len); @@ -1164,6 +1168,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, } if (channel && sband->band == IEEE80211_BAND_2GHZ) { + if (end - pos < 3) + goto out_err; *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = channel; @@ -1182,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, noffset = ieee80211_ie_split(ie, ie_len, before_ht, ARRAY_SIZE(before_ht), offset); + if (end - pos < noffset - offset) + goto out_err; memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; offset = noffset; } - if (sband->ht_cap.ht_supported) + if (sband->ht_cap.ht_supported) { + if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) + goto out_err; pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); + } /* * If adding more here, adjust code in main.c @@ -1199,14 +1210,22 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, /* add any remaining custom IEs */ if (ie && ie_len) { noffset = ie_len; + if (end - pos < noffset - offset) + goto out_err; memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; } - if (sband->vht_cap.vht_supported) + if (sband->vht_cap.vht_supported) { + if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) + goto out_err; pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, sband->vht_cap.cap); + } + return pos - buffer; + out_err: + WARN_ONCE(1, "not enough space for preq IEs\n"); return pos - buffer; } @@ -1239,7 +1258,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, else chan_no = ieee80211_frequency_to_channel(chan->center_freq); - buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band, + buf_len = ieee80211_build_preq_ies(local, buf, 200 + ie_len, + ie, ie_len, chan->band, ratemask, chan_no); skb = ieee80211_probereq_get(&local->hw, &sdata->vif, -- cgit v1.2.3 From b9a9ada14aab17f08c1d9735601f1097cdcfc6de Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Nov 2012 13:00:10 +0100 Subject: mac80211: remove probe response temporary buffer allocation Instead of allocating a temporary buffer to build IEs build them right into the SKB. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 7 ++++++- drivers/net/wireless/ti/wl1251/main.c | 4 +++- drivers/net/wireless/ti/wlcore/cmd.c | 4 +++- include/net/mac80211.h | 5 ++--- net/mac80211/tx.c | 9 ++------- net/mac80211/util.c | 26 +++++++++----------------- 6 files changed, 25 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 2aa8a1aa1184..8a61dbd320e6 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1347,9 +1347,14 @@ static void hw_scan_work(struct work_struct *work) hwsim->hw_scan_vif, req->ssids[i].ssid, req->ssids[i].ssid_len, - req->ie, req->ie_len); + req->ie_len); if (!probe) continue; + + if (req->ie_len) + memcpy(skb_put(probe, req->ie_len), req->ie, + req->ie_len); + local_bh_disable(); mac80211_hwsim_tx_frame(hwsim->hw, probe, hwsim->tmp_chan); diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 441cbccbd381..f47e8b0482ad 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -896,11 +896,13 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw, goto out; skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, - req->ie, req->ie_len); + req->ie_len); if (!skb) { ret = -ENOMEM; goto out; } + if (req->ie_len) + memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, skb->len); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index eaef3f41b252..27f83f72a93b 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1038,11 +1038,13 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, - ie, ie_len); + ie_len); if (!skb) { ret = -ENOMEM; goto out; } + if (ie_len) + memcpy(skb_put(skb, ie_len), ie, ie_len); wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e806f1d81f66..0472d806fb41 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3144,8 +3144,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @ssid: SSID buffer * @ssid_len: length of SSID - * @ie: buffer containing all IEs except SSID for the template - * @ie_len: length of the IE buffer + * @tailroom: tailroom to reserve at end of SKB for IEs * * Creates a Probe Request template which can, for example, be uploaded to * hardware. @@ -3153,7 +3152,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len); + size_t tailroom); /** * ieee80211_rts_get - RTS frame generation function diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d8ef3417bf2b..ba1ac9d62b08 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2620,7 +2620,7 @@ EXPORT_SYMBOL(ieee80211_nullfunc_get); struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len) + size_t tailroom) { struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local; @@ -2634,7 +2634,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, ie_ssid_len = 2 + ssid_len; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + - ie_ssid_len + ie_len); + ie_ssid_len + tailroom); if (!skb) return NULL; @@ -2655,11 +2655,6 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, memcpy(pos, ssid, ssid_len); pos += ssid_len; - if (ie) { - pos = skb_put(skb, ie_len); - memcpy(pos, ie, ie_len); - } - return skb; } EXPORT_SYMBOL(ieee80211_probereq_get); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f119b1b6c1a7..41c9841614b8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1239,14 +1239,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - size_t buf_len; - u8 *buf; u8 chan_no; - - /* FIXME: come up with a proper value */ - buf = kmalloc(200 + ie_len, GFP_KERNEL); - if (!buf) - return NULL; + int ies_len; /* * Do not send DS Channel parameter for directed probe requests @@ -1258,15 +1252,16 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, else chan_no = ieee80211_frequency_to_channel(chan->center_freq); - buf_len = ieee80211_build_preq_ies(local, buf, 200 + ie_len, - ie, ie_len, chan->band, - ratemask, chan_no); - skb = ieee80211_probereq_get(&local->hw, &sdata->vif, - ssid, ssid_len, - buf, buf_len); + ssid, ssid_len, 100 + ie_len); if (!skb) - goto out; + return NULL; + + ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), + skb_tailroom(skb), + ie, ie_len, chan->band, + ratemask, chan_no); + skb_put(skb, ies_len); if (dst) { mgmt = (struct ieee80211_mgmt *) skb->data; @@ -1276,9 +1271,6 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - out: - kfree(buf); - return skb; } -- cgit v1.2.3 From 9caf03640279e64d0ba36539b42daa1b43a49486 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Nov 2012 01:25:20 +0100 Subject: cfg80211: fix BSS struct IE access races When a BSS struct is updated, the IEs are currently overwritten or freed. This can lead to races if some other CPU is accessing the BSS struct and using the IEs concurrently. Fix this by always allocating the IEs in a new struct that holds the data and length and protecting access to this new struct with RCU. Signed-off-by: Johannes Berg --- drivers/net/wireless/libertas/cfg.c | 9 +- drivers/net/wireless/mwifiex/sta_ioctl.c | 35 ++- include/net/cfg80211.h | 41 ++-- net/mac80211/mlme.c | 80 +++--- net/wireless/core.h | 2 - net/wireless/nl80211.c | 27 +- net/wireless/reg.c | 2 +- net/wireless/reg.h | 2 +- net/wireless/scan.c | 409 +++++++++++++++---------------- net/wireless/sme.c | 13 +- net/wireless/util.c | 9 +- net/wireless/wext-sme.c | 8 +- 12 files changed, 353 insertions(+), 284 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index ec36868f6fc5..ec6d5d6b452e 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -298,6 +298,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) const u8 *rates_eid, *ext_rates_eid; int n = 0; + rcu_read_lock(); rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); @@ -325,6 +326,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) *tlv++ = 0x96; n = 4; } + rcu_read_unlock(); rate_tlv->header.len = cpu_to_le16(n); return sizeof(rate_tlv->header) + n; @@ -1140,11 +1142,13 @@ static int lbs_associate(struct lbs_private *priv, cmd->capability = cpu_to_le16(bss->capability); /* add SSID TLV */ + rcu_read_lock(); ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); if (ssid_eid) pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); else lbs_deb_assoc("no SSID\n"); + rcu_read_unlock(); /* add DS param TLV */ if (bss->channel) @@ -1782,7 +1786,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, struct cfg80211_ibss_params *params, struct cfg80211_bss *bss) { - const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + const u8 *rates_eid; struct cmd_ds_802_11_ad_hoc_join cmd; u8 preamble = RADIO_PREAMBLE_SHORT; int ret = 0; @@ -1841,6 +1845,8 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, /* set rates to the intersection of our rates and the rates in the bss */ + rcu_read_lock(); + rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); if (!rates_eid) { lbs_add_rates(cmd.bss.rates); } else { @@ -1860,6 +1866,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, } } } + rcu_read_unlock(); /* Only v8 and below support setting this */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 24af6ba7d8a1..5d7b83e2dc4d 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -158,12 +158,22 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct mwifiex_bssdescriptor *bss_desc) { - int ret; + int ret, beacon_ie_len; u8 *beacon_ie; struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + ies = rcu_dereference(bss->ies); + if (WARN_ON(!ies)) { + /* should never happen */ + rcu_read_unlock(); + return -EINVAL; + } + beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); + beacon_ie_len = ies->len; + rcu_read_unlock(); - beacon_ie = kmemdup(bss->information_elements, bss->len_beacon_ies, - GFP_KERNEL); if (!beacon_ie) { dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); return -ENOMEM; @@ -172,7 +182,7 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); bss_desc->rssi = bss->signal; bss_desc->beacon_buf = beacon_ie; - bss_desc->beacon_buf_size = bss->len_beacon_ies; + bss_desc->beacon_buf_size = beacon_ie_len; bss_desc->beacon_period = bss->beacon_interval; bss_desc->cap_info_bitmap = bss->capability; bss_desc->bss_band = bss_priv->band; @@ -198,18 +208,23 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, static int mwifiex_process_country_ie(struct mwifiex_private *priv, struct cfg80211_bss *bss) { - u8 *country_ie, country_ie_len; + const u8 *country_ie; + u8 country_ie_len; struct mwifiex_802_11d_domain_reg *domain_info = &priv->adapter->domain_reg; - country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); - - if (!country_ie) + rcu_read_lock(); + country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + rcu_read_unlock(); return 0; + } country_ie_len = country_ie[1]; - if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { + rcu_read_unlock(); return 0; + } domain_info->country_code[0] = country_ie[2]; domain_info->country_code[1] = country_ie[3]; @@ -223,6 +238,8 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, memcpy((u8 *)domain_info->triplet, &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); + rcu_read_unlock(); + if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(priv->adapter->wiphy, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 731b48fa238b..8e6a6b73b9c9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1205,6 +1205,18 @@ enum cfg80211_signal_type { CFG80211_SIGNAL_TYPE_UNSPEC, }; +/** + * struct cfg80211_bss_ie_data - BSS entry IE data + * @rcu_head: internal use, for freeing + * @len: length of the IEs + * @data: IE data + */ +struct cfg80211_bss_ies { + struct rcu_head rcu_head; + int len; + u8 data[]; +}; + /** * struct cfg80211_bss - BSS description * @@ -1216,36 +1228,34 @@ enum cfg80211_signal_type { * @tsf: timestamp of last received update * @beacon_interval: the beacon interval as from the frame * @capability: the capability field in host byte order - * @information_elements: the information elements (Note that there + * @ies: the information elements (Note that there * is no guarantee that these are well-formed!); this is a pointer to * either the beacon_ies or proberesp_ies depending on whether Probe * Response frame has been received - * @len_information_elements: total length of the information elements * @beacon_ies: the information elements from the last Beacon frame - * @len_beacon_ies: total length of the beacon_ies * @proberesp_ies: the information elements from the last Probe Response frame - * @len_proberesp_ies: total length of the proberesp_ies * @signal: signal strength value (type depends on the wiphy's signal_type) * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ struct cfg80211_bss { + u64 tsf; + struct ieee80211_channel *channel; - u8 bssid[ETH_ALEN]; - u64 tsf; + const struct cfg80211_bss_ies __rcu *ies; + const struct cfg80211_bss_ies __rcu *beacon_ies; + const struct cfg80211_bss_ies __rcu *proberesp_ies; + + void (*free_priv)(struct cfg80211_bss *bss); + + s32 signal; + u16 beacon_interval; u16 capability; - u8 *information_elements; - size_t len_information_elements; - u8 *beacon_ies; - size_t len_beacon_ies; - u8 *proberesp_ies; - size_t len_proberesp_ies; - s32 signal; + u8 bssid[ETH_ALEN]; - void (*free_priv)(struct cfg80211_bss *bss); u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); }; @@ -1253,6 +1263,9 @@ struct cfg80211_bss { * ieee80211_bss_get_ie - find IE with given ID * @bss: the bss to search * @ie: the IE ID + * + * Note that the return value is an RCU-protected pointer, so + * rcu_read_lock() must be held when calling this function. * Returns %NULL if not found. */ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 35d8cffc973a..481d5035b397 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1382,19 +1382,26 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; if (sdata->vif.p2p) { - u8 noa[2]; - int ret; + const struct cfg80211_bss_ies *ies; - ret = cfg80211_get_p2p_attr(cbss->information_elements, - cbss->len_information_elements, - IEEE80211_P2P_ATTR_ABSENCE_NOTICE, - noa, sizeof(noa)); - if (ret >= 2) { - bss_conf->p2p_oppps = noa[1] & 0x80; - bss_conf->p2p_ctwindow = noa[1] & 0x7f; - bss_info_changed |= BSS_CHANGED_P2P_PS; - sdata->u.mgd.p2p_noa_index = noa[0]; + rcu_read_lock(); + ies = rcu_dereference(cbss->ies); + if (ies) { + u8 noa[2]; + int ret; + + ret = cfg80211_get_p2p_attr( + ies->data, ies->len, + IEEE80211_P2P_ATTR_ABSENCE_NOTICE, + noa, sizeof(noa)); + if (ret >= 2) { + bss_conf->p2p_oppps = noa[1] & 0x80; + bss_conf->p2p_ctwindow = noa[1] & 0x7f; + bss_info_changed |= BSS_CHANGED_P2P_PS; + sdata->u.mgd.p2p_noa_index = noa[0]; + } } + rcu_read_unlock(); } /* just to be sure */ @@ -1659,6 +1666,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) } else { int ssid_len; + rcu_read_lock(); ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); if (WARN_ON_ONCE(ssid == NULL)) ssid_len = 0; @@ -1668,6 +1676,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, 0, (u32) -1, true, false, ifmgd->associated->channel, false); + rcu_read_unlock(); } ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); @@ -1763,6 +1772,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, else return NULL; + rcu_read_lock(); ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); if (WARN_ON_ONCE(ssid == NULL)) ssid_len = 0; @@ -1773,6 +1783,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, (u32) -1, cbss->channel, ssid + 2, ssid_len, NULL, 0, true); + rcu_read_unlock(); return skb; } @@ -2858,9 +2869,12 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) auth_data->bss->bssid, auth_data->tries, IEEE80211_AUTH_MAX_TRIES); + rcu_read_lock(); ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); - if (!ssidie) + if (!ssidie) { + rcu_read_unlock(); return -EINVAL; + } /* * Direct probe is sent to broadcast address as some APs * will not answer to direct packet in unassociated state. @@ -2868,6 +2882,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], NULL, 0, (u32) -1, true, false, auth_data->bss->channel, false); + rcu_read_unlock(); } auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; @@ -3404,9 +3419,7 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, if (ifmgd->flags & IEEE80211_STA_DISABLE_HT) return chains; - ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, - cbss->information_elements, - cbss->len_information_elements); + ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { ht_cap = (void *)(ht_cap_ie + 2); chains = ieee80211_mcs_to_chains(&ht_cap->mcs); @@ -3419,9 +3432,7 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) return chains; - vht_cap_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, - cbss->information_elements, - cbss->len_information_elements); + vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY); if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) { u8 nss; u16 tx_mcs_map; @@ -3457,13 +3468,13 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, IEEE80211_STA_DISABLE_80P80MHZ | IEEE80211_STA_DISABLE_160MHZ); + rcu_read_lock(); + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && sband->ht_cap.ht_supported) { const u8 *ht_oper_ie; - ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION, - cbss->information_elements, - cbss->len_information_elements); + ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION); if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) ht_oper = (void *)(ht_oper_ie + 2); } @@ -3472,9 +3483,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, sband->vht_cap.vht_supported) { const u8 *vht_oper_ie; - vht_oper_ie = cfg80211_find_ie(WLAN_EID_VHT_OPERATION, - cbss->information_elements, - cbss->len_information_elements); + vht_oper_ie = ieee80211_bss_get_ie(cbss, + WLAN_EID_VHT_OPERATION); if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper)) vht_oper = (void *)(vht_oper_ie + 2); if (vht_oper && !ht_oper) { @@ -3494,6 +3504,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), local->rx_chains); + rcu_read_unlock(); + /* will change later if needed */ sdata->smps_mode = IEEE80211_SMPS_OFF; @@ -3734,14 +3746,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, const u8 *ssidie, *ht_ie; int i, err; - ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); - if (!ssidie) - return -EINVAL; - assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); if (!assoc_data) return -ENOMEM; + rcu_read_lock(); + ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + if (!ssidie) { + rcu_read_unlock(); + kfree(assoc_data); + return -EINVAL; + } + memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); + assoc_data->ssid_len = ssidie[1]; + rcu_read_unlock(); + mutex_lock(&ifmgd->mtx); if (ifmgd->associated) @@ -3836,12 +3855,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->supp_rates = bss->supp_rates; assoc_data->supp_rates_len = bss->supp_rates_len; + rcu_read_lock(); ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) assoc_data->ap_ht_param = ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; else ifmgd->flags |= IEEE80211_STA_DISABLE_HT; + rcu_read_unlock(); if (bss->wmm_used && bss->uapsd_supported && (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { @@ -3852,9 +3873,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; } - memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); - assoc_data->ssid_len = ssidie[1]; - if (req->prev_bssid) memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); diff --git a/net/wireless/core.h b/net/wireless/core.h index 6183a0d25b8b..3563097169cb 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -138,8 +138,6 @@ struct cfg80211_internal_bss { unsigned long ts; struct kref ref; atomic_t hold; - bool beacon_ies_allocated; - bool proberesp_ies_allocated; /* must be last because of priv member */ struct cfg80211_bss pub; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4a719770eaaf..f45706adaf34 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4808,6 +4808,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, struct cfg80211_internal_bss *intbss) { struct cfg80211_bss *res = &intbss->pub; + const struct cfg80211_bss_ies *ies; void *hdr; struct nlattr *bss; @@ -4828,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, if (!bss) goto nla_put_failure; if ((!is_zero_ether_addr(res->bssid) && - nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) || - (res->information_elements && res->len_information_elements && - nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, - res->len_information_elements, - res->information_elements)) || - (res->beacon_ies && res->len_beacon_ies && - res->beacon_ies != res->information_elements && - nla_put(msg, NL80211_BSS_BEACON_IES, - res->len_beacon_ies, res->beacon_ies))) + nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid))) goto nla_put_failure; + + rcu_read_lock(); + ies = rcu_dereference(res->ies); + if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, + ies->len, ies->data)) { + rcu_read_unlock(); + goto nla_put_failure; + } + ies = rcu_dereference(res->beacon_ies); + if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, + ies->len, ies->data)) { + rcu_read_unlock(); + goto nla_put_failure; + } + rcu_read_unlock(); + if (res->tsf && nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) goto nla_put_failure; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index bcc7d7ee5a51..b6c7ea6d98f8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1797,7 +1797,7 @@ EXPORT_SYMBOL(regulatory_hint); */ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, - u8 *country_ie, + const u8 *country_ie, u8 country_ie_len) { char alpha2[2]; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index f023c8a31c60..4c0a32ffd530 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -81,7 +81,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, */ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, - u8 *country_ie, + const u8 *country_ie, u8 country_ie_len); /** diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 834e0d153fbe..01592d7d4789 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -23,6 +23,7 @@ static void bss_release(struct kref *ref) { + struct cfg80211_bss_ies *ies; struct cfg80211_internal_bss *bss; bss = container_of(ref, struct cfg80211_internal_bss, ref); @@ -33,10 +34,12 @@ static void bss_release(struct kref *ref) if (bss->pub.free_priv) bss->pub.free_priv(&bss->pub); - if (bss->beacon_ies_allocated) - kfree(bss->pub.beacon_ies); - if (bss->proberesp_ies_allocated) - kfree(bss->pub.proberesp_ies); + ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); + if (ies) + kfree_rcu(ies, rcu_head); + ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); + if (ies) + kfree_rcu(ies, rcu_head); kfree(bss); } @@ -288,7 +291,7 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, } EXPORT_SYMBOL(cfg80211_find_vendor_ie); -static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) +static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2) { const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); @@ -311,6 +314,7 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, const u8 *ssid, size_t ssid_len) { + const struct cfg80211_bss_ies *ies; const u8 *ssidie; if (bssid && !ether_addr_equal(a->bssid, bssid)) @@ -319,9 +323,10 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, if (!ssid) return true; - ssidie = cfg80211_find_ie(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements); + ies = rcu_access_pointer(a->ies); + if (!ies) + return false; + ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); if (!ssidie) return false; if (ssidie[1] != ssid_len) @@ -331,20 +336,21 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, static bool is_mesh_bss(struct cfg80211_bss *a) { + const struct cfg80211_bss_ies *ies; const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements); + ies = rcu_access_pointer(a->ies); + if (!ies) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); if (!ie) return false; - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements); + ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); if (!ie) return false; @@ -355,14 +361,17 @@ static bool is_mesh(struct cfg80211_bss *a, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) { + const struct cfg80211_bss_ies *ies; const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements); + ies = rcu_access_pointer(a->ies); + if (!ies) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); if (!ie) return false; if (ie[1] != meshidlen) @@ -370,9 +379,7 @@ static bool is_mesh(struct cfg80211_bss *a, if (memcmp(ie + 2, meshid, meshidlen)) return false; - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements); + ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); if (!ie) return false; if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) @@ -389,24 +396,28 @@ static bool is_mesh(struct cfg80211_bss *a, static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) { + const struct cfg80211_bss_ies *a_ies, *b_ies; int r; if (a->channel != b->channel) return b->channel->center_freq - a->channel->center_freq; if (is_mesh_bss(a) && is_mesh_bss(b)) { + a_ies = rcu_access_pointer(a->ies); + if (!a_ies) + return -1; + b_ies = rcu_access_pointer(b->ies); + if (!b_ies) + return 1; + r = cmp_ies(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); + a_ies->data, a_ies->len, + b_ies->data, b_ies->len); if (r) return r; return cmp_ies(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); + a_ies->data, a_ies->len, + b_ies->data, b_ies->len); } /* @@ -419,21 +430,28 @@ static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) static int cmp_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) { + const struct cfg80211_bss_ies *a_ies, *b_ies; int r; r = cmp_bss_core(a, b); if (r) return r; + a_ies = rcu_access_pointer(a->ies); + if (!a_ies) + return -1; + b_ies = rcu_access_pointer(b->ies); + if (!b_ies) + return 1; + return cmp_ies(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); + a_ies->data, a_ies->len, + b_ies->data, b_ies->len); } static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) { + const struct cfg80211_bss_ies *a_ies, *b_ies; const u8 *ie1; const u8 *ie2; int i; @@ -443,12 +461,15 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) if (r) return r; - ie1 = cfg80211_find_ie(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements); - ie2 = cfg80211_find_ie(WLAN_EID_SSID, - b->information_elements, - b->len_information_elements); + a_ies = rcu_access_pointer(a->ies); + if (!a_ies) + return -1; + b_ies = rcu_access_pointer(b->ies); + if (!b_ies) + return 1; + + ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); + ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); /* * Key comparator must use same algorithm in any rb-tree @@ -633,126 +654,84 @@ static void copy_hidden_ies(struct cfg80211_internal_bss *res, struct cfg80211_internal_bss *hidden) { - if (unlikely(res->pub.beacon_ies)) - return; - if (WARN_ON(!hidden->pub.beacon_ies)) + const struct cfg80211_bss_ies *ies; + + if (rcu_access_pointer(res->pub.beacon_ies)) return; - res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); - if (unlikely(!res->pub.beacon_ies)) + ies = rcu_access_pointer(hidden->pub.beacon_ies); + if (WARN_ON(!ies)) return; - res->beacon_ies_allocated = true; - res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; - memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, - res->pub.len_beacon_ies); + ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC); + if (unlikely(!ies)) + return; + rcu_assign_pointer(res->pub.beacon_ies, ies); } static struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) + struct cfg80211_internal_bss *tmp) { struct cfg80211_internal_bss *found = NULL; - /* - * The reference to "res" is donated to this function. - */ - - if (WARN_ON(!res->pub.channel)) { - kref_put(&res->ref, bss_release); + if (WARN_ON(!tmp->pub.channel)) return NULL; - } - res->ts = jiffies; + tmp->ts = jiffies; spin_lock_bh(&dev->bss_lock); - found = rb_find_bss(dev, res); + if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) { + spin_unlock_bh(&dev->bss_lock); + return NULL; + } + + found = rb_find_bss(dev, tmp); if (found) { - found->pub.beacon_interval = res->pub.beacon_interval; - found->pub.tsf = res->pub.tsf; - found->pub.signal = res->pub.signal; - found->pub.capability = res->pub.capability; - found->ts = res->ts; + found->pub.beacon_interval = tmp->pub.beacon_interval; + found->pub.tsf = tmp->pub.tsf; + found->pub.signal = tmp->pub.signal; + found->pub.capability = tmp->pub.capability; + found->ts = tmp->ts; /* Update IEs */ - if (res->pub.proberesp_ies) { - size_t used = dev->wiphy.bss_priv_size + sizeof(*res); - size_t ielen = res->pub.len_proberesp_ies; - - if (found->pub.proberesp_ies && - !found->proberesp_ies_allocated && - ksize(found) >= used + ielen) { - memcpy(found->pub.proberesp_ies, - res->pub.proberesp_ies, ielen); - found->pub.len_proberesp_ies = ielen; - } else { - u8 *ies = found->pub.proberesp_ies; - - if (found->proberesp_ies_allocated) - ies = krealloc(ies, ielen, GFP_ATOMIC); - else - ies = kmalloc(ielen, GFP_ATOMIC); - - if (ies) { - memcpy(ies, res->pub.proberesp_ies, - ielen); - found->proberesp_ies_allocated = true; - found->pub.proberesp_ies = ies; - found->pub.len_proberesp_ies = ielen; - } - } + if (rcu_access_pointer(tmp->pub.proberesp_ies)) { + const struct cfg80211_bss_ies *old; + + old = rcu_access_pointer(found->pub.proberesp_ies); + rcu_assign_pointer(found->pub.proberesp_ies, + tmp->pub.proberesp_ies); /* Override possible earlier Beacon frame IEs */ - found->pub.information_elements = - found->pub.proberesp_ies; - found->pub.len_information_elements = - found->pub.len_proberesp_ies; - } + rcu_assign_pointer(found->pub.ies, + tmp->pub.proberesp_ies); + if (old) + kfree_rcu((struct cfg80211_bss_ies *)old, + rcu_head); + } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { + const struct cfg80211_bss_ies *old, *ies; - if (res->pub.beacon_ies) { - size_t used = dev->wiphy.bss_priv_size + sizeof(*res); - size_t ielen = res->pub.len_beacon_ies; - bool information_elements_is_beacon_ies = - (found->pub.information_elements == - found->pub.beacon_ies); - - if (found->pub.beacon_ies && - !found->beacon_ies_allocated && - ksize(found) >= used + ielen) { - memcpy(found->pub.beacon_ies, - res->pub.beacon_ies, ielen); - found->pub.len_beacon_ies = ielen; - } else { - u8 *ies = found->pub.beacon_ies; - - if (found->beacon_ies_allocated) - ies = krealloc(ies, ielen, GFP_ATOMIC); - else - ies = kmalloc(ielen, GFP_ATOMIC); - - if (ies) { - memcpy(ies, res->pub.beacon_ies, - ielen); - found->beacon_ies_allocated = true; - found->pub.beacon_ies = ies; - found->pub.len_beacon_ies = ielen; - } - } + old = rcu_access_pointer(found->pub.beacon_ies); + ies = rcu_access_pointer(found->pub.ies); + + rcu_assign_pointer(found->pub.beacon_ies, + tmp->pub.beacon_ies); /* Override IEs if they were from a beacon before */ - if (information_elements_is_beacon_ies) { - found->pub.information_elements = - found->pub.beacon_ies; - found->pub.len_information_elements = - found->pub.len_beacon_ies; - } - } + if (old == ies) + rcu_assign_pointer(found->pub.ies, + tmp->pub.beacon_ies); - kref_put(&res->ref, bss_release); + if (old) + kfree_rcu((struct cfg80211_bss_ies *)old, + rcu_head); + } } else { + struct cfg80211_internal_bss *new; struct cfg80211_internal_bss *hidden; + struct cfg80211_bss_ies *ies; /* First check if the beacon is a probe response from * a hidden bss. If so, copy beacon ies (with nullified @@ -763,14 +742,32 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, /* TODO: The code is not trying to update existing probe * response bss entries when beacon ies are * getting changed. */ - hidden = rb_find_hidden_bss(dev, res); + hidden = rb_find_hidden_bss(dev, tmp); if (hidden) - copy_hidden_ies(res, hidden); - - /* this "consumes" the reference */ - list_add_tail(&res->list, &dev->bss_list); - rb_insert_bss(dev, res); - found = res; + copy_hidden_ies(tmp, hidden); + + /* + * create a copy -- the "res" variable that is passed in + * is allocated on the stack since it's not needed in the + * more common case of an update + */ + new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size, + GFP_ATOMIC); + if (!new) { + ies = (void *)rcu_dereference(tmp->pub.beacon_ies); + if (ies) + kfree_rcu(ies, rcu_head); + ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); + if (ies) + kfree_rcu(ies, rcu_head); + spin_unlock_bh(&dev->bss_lock); + return NULL; + } + memcpy(new, tmp, sizeof(*new)); + kref_init(&new->ref); + list_add_tail(&new->list, &dev->bss_list); + rb_insert_bss(dev, new); + found = new; } dev->bss_generation++; @@ -819,14 +816,12 @@ cfg80211_inform_bss(struct wiphy *wiphy, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp) { - struct cfg80211_internal_bss *res; - size_t privsz; + struct cfg80211_bss_ies *ies; + struct cfg80211_internal_bss tmp = {}, *res; if (WARN_ON(!wiphy)) return NULL; - privsz = wiphy->bss_priv_size; - if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && (signal < 0 || signal > 100))) return NULL; @@ -835,36 +830,33 @@ cfg80211_inform_bss(struct wiphy *wiphy, if (!channel) return NULL; - res = kzalloc(sizeof(*res) + privsz + ielen, gfp); - if (!res) - return NULL; - - memcpy(res->pub.bssid, bssid, ETH_ALEN); - res->pub.channel = channel; - res->pub.signal = signal; - res->pub.tsf = tsf; - res->pub.beacon_interval = beacon_interval; - res->pub.capability = capability; + memcpy(tmp.pub.bssid, bssid, ETH_ALEN); + tmp.pub.channel = channel; + tmp.pub.signal = signal; + tmp.pub.tsf = tsf; + tmp.pub.beacon_interval = beacon_interval; + tmp.pub.capability = capability; /* * Since we do not know here whether the IEs are from a Beacon or Probe * Response frame, we need to pick one of the options and only use it * with the driver that does not provide the full Beacon/Probe Response * frame. Use Beacon frame pointer to avoid indicating that this should - * override the information_elements pointer should we have received an - * earlier indication of Probe Response data. + * override the iies pointer should we have received an earlier + * indication of Probe Response data. * * The initial buffer for the IEs is allocated with the BSS entry and * is located after the private area. */ - res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; - memcpy(res->pub.beacon_ies, ie, ielen); - res->pub.len_beacon_ies = ielen; - res->pub.information_elements = res->pub.beacon_ies; - res->pub.len_information_elements = res->pub.len_beacon_ies; + ies = kmalloc(sizeof(*ies) + ielen, gfp); + if (!ies) + return NULL; + ies->len = ielen; + memcpy(ies->data, ie, ielen); - kref_init(&res->ref); + rcu_assign_pointer(tmp.pub.beacon_ies, ies); + rcu_assign_pointer(tmp.pub.ies, ies); - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); + res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp); if (!res) return NULL; @@ -883,10 +875,10 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, struct ieee80211_mgmt *mgmt, size_t len, s32 signal, gfp_t gfp) { - struct cfg80211_internal_bss *res; + struct cfg80211_internal_bss tmp = {}, *res; + struct cfg80211_bss_ies *ies; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - size_t privsz; BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != offsetof(struct ieee80211_mgmt, u.beacon.variable)); @@ -906,45 +898,31 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) return NULL; - privsz = wiphy->bss_priv_size; - channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, ielen, channel); if (!channel) return NULL; - res = kzalloc(sizeof(*res) + privsz + ielen, gfp); - if (!res) + ies = kmalloc(sizeof(*ies) + ielen, gfp); + if (!ies) return NULL; + ies->len = ielen; + memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); - memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); - res->pub.channel = channel; - res->pub.signal = signal; - res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); - res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); - res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); - /* - * The initial buffer for the IEs is allocated with the BSS entry and - * is located after the private area. - */ - if (ieee80211_is_probe_resp(mgmt->frame_control)) { - res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; - memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, - ielen); - res->pub.len_proberesp_ies = ielen; - res->pub.information_elements = res->pub.proberesp_ies; - res->pub.len_information_elements = res->pub.len_proberesp_ies; - } else { - res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; - memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); - res->pub.len_beacon_ies = ielen; - res->pub.information_elements = res->pub.beacon_ies; - res->pub.len_information_elements = res->pub.len_beacon_ies; - } - - kref_init(&res->ref); - - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); + if (ieee80211_is_probe_resp(mgmt->frame_control)) + rcu_assign_pointer(tmp.pub.proberesp_ies, ies); + else + rcu_assign_pointer(tmp.pub.beacon_ies, ies); + rcu_assign_pointer(tmp.pub.ies, ies); + + memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); + tmp.pub.channel = channel; + tmp.pub.signal = signal; + tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); + tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); + + res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp); if (!res) return NULL; @@ -1136,22 +1114,21 @@ int cfg80211_wext_siwscan(struct net_device *dev, EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); static void ieee80211_scan_add_ies(struct iw_request_info *info, - struct cfg80211_bss *bss, + const struct cfg80211_bss_ies *ies, char **current_ev, char *end_buf) { - u8 *pos, *end, *next; + const u8 *pos, *end, *next; struct iw_event iwe; - if (!bss->information_elements || - !bss->len_information_elements) + if (!ies) return; /* * If needed, fragment the IEs buffer (at IE boundaries) into short * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. */ - pos = bss->information_elements; - end = pos + bss->len_information_elements; + pos = ies->data; + end = pos + ies->len; while (end - pos > IW_GENERIC_IE_MAX) { next = pos + 2 + pos[1]; @@ -1162,7 +1139,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info, iwe.cmd = IWEVGENIE; iwe.u.data.length = next - pos; *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); + end_buf, &iwe, + (void *)pos); pos = next; } @@ -1172,7 +1150,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info, iwe.cmd = IWEVGENIE; iwe.u.data.length = end - pos; *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); + end_buf, &iwe, + (void *)pos); } } @@ -1191,10 +1170,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, struct cfg80211_internal_bss *bss, char *current_ev, char *end_buf) { + const struct cfg80211_bss_ies *ies; struct iw_event iwe; + const u8 *ie; u8 *buf, *cfg, *p; - u8 *ie = bss->pub.information_elements; - int rem = bss->pub.len_information_elements, i, sig; + int rem, i, sig; bool ismesh = false; memset(&iwe, 0, sizeof(iwe)); @@ -1259,7 +1239,17 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ""); - while (rem >= 2) { + rcu_read_lock(); + ies = rcu_dereference(bss->pub.ies); + if (ies) { + rem = ies->len; + ie = ies->data; + } else { + rem = 0; + ie = NULL; + } + + while (ies && rem >= 2) { /* invalid data */ if (ie[1] > rem - 2) break; @@ -1271,7 +1261,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, iwe.u.data.length = ie[1]; iwe.u.data.flags = 1; current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie + 2); + &iwe, (u8 *)ie + 2); break; case WLAN_EID_MESH_ID: memset(&iwe, 0, sizeof(iwe)); @@ -1279,7 +1269,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, iwe.u.data.length = ie[1]; iwe.u.data.flags = 1; current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie + 2); + &iwe, (u8 *)ie + 2); break; case WLAN_EID_MESH_CONFIG: ismesh = true; @@ -1288,7 +1278,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, buf = kmalloc(50, GFP_ATOMIC); if (!buf) break; - cfg = ie + 2; + cfg = (u8 *)ie + 2; memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, "Mesh Network Path Selection Protocol ID: " @@ -1386,7 +1376,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, kfree(buf); } - ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); + ieee80211_scan_add_ies(info, ies, ¤t_ev, end_buf); + rcu_read_unlock(); return current_ev; } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index c7490027237d..f2431e41a373 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -417,7 +417,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, struct cfg80211_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; - u8 *country_ie; + const u8 *country_ie; #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif @@ -501,7 +501,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_upload_connect_keys(wdev); - country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + rcu_read_lock(); + country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + rcu_read_unlock(); + return; + } + + country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC); + rcu_read_unlock(); if (!country_ie) return; @@ -515,6 +523,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, bss->channel->band, country_ie + 2, country_ie[1]); + kfree(country_ie); } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, diff --git a/net/wireless/util.c b/net/wireless/util.c index 3cce6e486219..16d76a807c2f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -688,10 +688,13 @@ EXPORT_SYMBOL(cfg80211_classify8021d); const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) { - if (bss->information_elements == NULL) + const struct cfg80211_bss_ies *ies; + + ies = rcu_dereference(bss->ies); + if (!ies) return NULL; - return cfg80211_find_ie(ie, bss->information_elements, - bss->len_information_elements); + + return cfg80211_find_ie(ie, ies->data, ies->len); } EXPORT_SYMBOL(ieee80211_bss_get_ie); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 873af63187c0..fb9622f6d99c 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -242,13 +242,17 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, wdev_lock(wdev); if (wdev->current_bss) { - const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, - WLAN_EID_SSID); + const u8 *ie; + + rcu_read_lock(); + ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, + WLAN_EID_SSID); if (ie) { data->flags = 1; data->length = ie[1]; memcpy(ssid, ie + 2, data->length); } + rcu_read_unlock(); } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { data->flags = 1; data->length = wdev->wext.connect.ssid_len; -- cgit v1.2.3 From a544ab7f9c09df591758931301a7c509bba6c913 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 13 Nov 2012 21:36:27 +0100 Subject: mac80211: simplify loop in minstrel_ht Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index fb1d4aa65e8c..9f9c453bc45d 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -389,9 +389,9 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_tx_rate *ar = info->status.rates; struct minstrel_rate_stats *rate, *rate2; struct minstrel_priv *mp = priv; - bool last = false; + bool last; int group; - int i = 0; + int i; if (!msp->is_ht) return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); @@ -419,13 +419,11 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) mi->sample_packets += info->status.ampdu_len; + last = !minstrel_ht_txstat_valid(&ar[0]); for (i = 0; !last; i++) { last = (i == IEEE80211_TX_MAX_RATES - 1) || !minstrel_ht_txstat_valid(&ar[i + 1]); - if (!minstrel_ht_txstat_valid(&ar[i])) - break; - group = minstrel_ht_get_group_idx(&ar[i]); rate = &mi->groups[group].rates[ar[i].idx % 8]; -- cgit v1.2.3 From 246dc3fddf4a01bb47dd56215a48dd2b270520db Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Fri, 30 Nov 2012 19:17:27 +0100 Subject: mac80211: return if CSA is not handle If channel contexts are enabled, the CSA should not be processed further. A return is missing here. Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 481d5035b397..09556303c7e1 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -789,6 +789,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, "not handling channel switch with channel contexts\n"); ieee80211_queue_work(&sdata->local->hw, &ifmgd->csa_connection_drop_work); + return; } mutex_lock(&sdata->local->chanctx_mtx); -- cgit v1.2.3 From da29d2a5780d80857773d7776b7603a449b0b6e0 Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 3 Dec 2012 11:23:37 +0100 Subject: cfg80211: fix channel error on mesh join Fix an error on mesh join when no channel has been explicitly set beforehand. Also remove a double semicolon. Signed-off-by: Marco Porsch Signed-off-by: Johannes Berg --- net/wireless/mesh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 3ee5a7282283..f9d6ce5cfabb 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -146,7 +146,8 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!setup->chandef.chan) return -EINVAL; - setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;; + setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT; + setup->chandef.center_freq1 = setup->chandef.chan->center_freq; } if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) -- cgit v1.2.3 From dc2a0e20fbc85a71c63aa4330b496fda33f6bf80 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 20 Nov 2012 23:25:54 -0200 Subject: Bluetooth: Add missing lock nesting notation This patch fixes the following report, it happens when accepting rfcomm connections: [ 228.165378] ============================================= [ 228.165378] [ INFO: possible recursive locking detected ] [ 228.165378] 3.7.0-rc1-00536-gc1d5dc4 #120 Tainted: G W [ 228.165378] --------------------------------------------- [ 228.165378] bluetoothd/1341 is trying to acquire lock: [ 228.165378] (sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM){+.+...}, at: [] bt_accept_dequeue+0xa0/0x180 [bluetooth] [ 228.165378] [ 228.165378] but task is already holding lock: [ 228.165378] (sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM){+.+...}, at: [] rfcomm_sock_accept+0x58/0x2d0 [rfcomm] [ 228.165378] [ 228.165378] other info that might help us debug this: [ 228.165378] Possible unsafe locking scenario: [ 228.165378] [ 228.165378] CPU0 [ 228.165378] ---- [ 228.165378] lock(sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM); [ 228.165378] lock(sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM); [ 228.165378] [ 228.165378] *** DEADLOCK *** [ 228.165378] [ 228.165378] May be due to missing lock nesting notation Cc: stable@vger.kernel.org Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/sock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 4ddef57d03a7..ce3f6658f4b2 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -467,7 +467,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f long timeo; int err = 0; - lock_sock(sk); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; @@ -504,7 +504,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f release_sock(sk); timeo = schedule_timeout(timeo); - lock_sock(sk); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); -- cgit v1.2.3 From b9b5ef188e5a2222cfc16ef62a4703080750b451 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Wed, 21 Nov 2012 00:50:21 -0200 Subject: Bluetooth: cancel power_on work when unregistering the device We need to cancel the hci_power_on work in order to avoid it run when we try to free the hdev. [ 1434.201149] ------------[ cut here ]------------ [ 1434.204998] WARNING: at lib/debugobjects.c:261 debug_print_object+0x8e/0xb0() [ 1434.208324] ODEBUG: free active (active state 0) object type: work_struct hint: hci _power_on+0x0/0x90 [ 1434.210386] Pid: 8564, comm: trinity-child25 Tainted: G W 3.7.0-rc5-next- 20121112-sasha-00018-g2f4ce0e #127 [ 1434.210760] Call Trace: [ 1434.210760] [] ? debug_print_object+0x8e/0xb0 [ 1434.210760] [] warn_slowpath_common+0x87/0xb0 [ 1434.210760] [] warn_slowpath_fmt+0x41/0x50 [ 1434.210760] [] debug_print_object+0x8e/0xb0 [ 1434.210760] [] ? hci_dev_open+0x310/0x310 [ 1434.210760] [] ? _raw_spin_unlock_irqrestore+0x55/0xa0 [ 1434.210760] [] __debug_check_no_obj_freed+0xa5/0x230 [ 1434.210760] [] ? bt_host_release+0x10/0x20 [ 1434.210760] [] debug_check_no_obj_freed+0x15/0x20 [ 1434.210760] [] kfree+0x227/0x330 [ 1434.210760] [] bt_host_release+0x10/0x20 [ 1434.210760] [] device_release+0x65/0xc0 [ 1434.210760] [] kobject_cleanup+0x145/0x190 [ 1434.210760] [] kobject_release+0xd/0x10 [ 1434.210760] [] kobject_put+0x4c/0x60 [ 1434.210760] [] put_device+0x12/0x20 [ 1434.210760] [] hci_free_dev+0x24/0x30 [ 1434.210760] [] vhci_release+0x31/0x60 [ 1434.210760] [] __fput+0x122/0x250 [ 1434.210760] [] ? rcu_user_exit+0x9d/0xd0 [ 1434.210760] [] ____fput+0x9/0x10 [ 1434.210760] [] task_work_run+0xb2/0xf0 [ 1434.210760] [] do_notify_resume+0x77/0xa0 [ 1434.210760] [] int_signal+0x12/0x17 [ 1434.210760] ---[ end trace a6d57fefbc8a8cc7 ]--- Cc: stable@vger.kernel.org Reported-by: Sasha Levin Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 81f4bac098c9..69eb644f0d48 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1854,6 +1854,8 @@ void hci_unregister_dev(struct hci_dev *hdev) for (i = 0; i < NUM_REASSEMBLY; i++) kfree_skb(hdev->reassembly[i]); + cancel_work_sync(&hdev->power_on); + if (!test_bit(HCI_INIT, &hdev->flags) && !test_bit(HCI_SETUP, &hdev->dev_flags)) { hci_dev_lock(hdev); -- cgit v1.2.3 From b96e9c671b05f95126753a22145d4509d45ca197 Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Wed, 21 Nov 2012 10:51:11 +0100 Subject: Bluetooth: Add BT_DEFER_SETUP option to sco socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This option will set the BT_SK_DEFER_SETUP bit in socket flags. Signed-off-by: Frédéric Dalleau Signed-off-by: Gustavo Padovan --- net/bluetooth/sco.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'net') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 450cdcd88e5c..c6678f2bffc9 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -666,12 +666,31 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char { struct sock *sk = sock->sk; int err = 0; + u32 opt; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt) + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + else + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + break; + default: err = -ENOPROTOOPT; break; @@ -753,6 +772,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char lock_sock(sk); switch (optname) { + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), + (u32 __user *) optval)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; -- cgit v1.2.3 From 20714bfef84d3e690c9c6f8e9cd46543b5ae1eed Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Wed, 21 Nov 2012 10:51:12 +0100 Subject: Bluetooth: Implement deferred sco socket setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to authenticate and configure an incoming SCO connection, the BT_DEFER_SETUP option was added. This option is intended to defer reply to Connect Request on SCO sockets. When a connection is requested, the listening socket is unblocked but the effective connection setup happens only on first recv. Any send between accept and recv fails with -ENOTCONN. Signed-off-by: Frédéric Dalleau Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 9 ++++--- net/bluetooth/hci_event.c | 52 ++++++++++++++++++++++++++++++++++++---- net/bluetooth/sco.c | 35 ++++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ef5b85dac3f7..76891a914e75 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -376,7 +376,7 @@ extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); -extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); +extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status); extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); @@ -577,6 +577,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); +void hci_conn_accept(struct hci_conn *conn, int mask); struct hci_chan *hci_chan_create(struct hci_conn *conn); void hci_chan_del(struct hci_chan *chan); @@ -779,8 +780,10 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR) /* ----- HCI protocols ----- */ +#define HCI_PROTO_DEFER 0x01 + static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, - __u8 type) + __u8 type, __u8 *flags) { switch (type) { case ACL_LINK: @@ -788,7 +791,7 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, case SCO_LINK: case ESCO_LINK: - return sco_connect_ind(hdev, bdaddr); + return sco_connect_ind(hdev, bdaddr, flags); default: BT_ERR("unknown link type %d", type); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9f5c5f244502..3843f1897c86 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2047,15 +2047,53 @@ unlock: hci_conn_check_pending(hdev); } +void hci_conn_accept(struct hci_conn *conn, int mask) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("conn %p", conn); + + conn->state = BT_CONFIG; + + if (!lmp_esco_capable(hdev)) { + struct hci_cp_accept_conn_req cp; + + bacpy(&cp.bdaddr, &conn->dst); + + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) + cp.role = 0x00; /* Become master */ + else + cp.role = 0x01; /* Remain slave */ + + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); + } else /* lmp_esco_capable(hdev)) */ { + struct hci_cp_accept_sync_conn_req cp; + + bacpy(&cp.bdaddr, &conn->dst); + cp.pkt_type = cpu_to_le16(conn->pkt_type); + + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40); + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40); + cp.max_latency = __constant_cpu_to_le16(0xffff); + cp.content_format = cpu_to_le16(hdev->voice_setting); + cp.retrans_effort = 0xff; + + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, + sizeof(cp), &cp); + } +} + static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; + __u8 flags = 0; BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr, ev->link_type); - mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); + mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type, + &flags); if ((mask & HCI_LM_ACCEPT) && !hci_blacklist_lookup(hdev, &ev->bdaddr)) { @@ -2081,12 +2119,13 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) } memcpy(conn->dev_class, ev->dev_class, 3); - conn->state = BT_CONNECT; hci_dev_unlock(hdev); - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) { + if (ev->link_type == ACL_LINK || + (!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) { struct hci_cp_accept_conn_req cp; + conn->state = BT_CONNECT; bacpy(&cp.bdaddr, &ev->bdaddr); @@ -2097,8 +2136,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); - } else { + } else if (!(flags & HCI_PROTO_DEFER)) { struct hci_cp_accept_sync_conn_req cp; + conn->state = BT_CONNECT; bacpy(&cp.bdaddr, &ev->bdaddr); cp.pkt_type = cpu_to_le16(conn->pkt_type); @@ -2111,6 +2151,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(cp), &cp); + } else { + conn->state = BT_CONNECT2; + hci_proto_connect_cfm(conn, 0); + hci_conn_put(conn); } } else { /* Connection rejected */ diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index c6678f2bffc9..eea17cdcaf7f 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -397,6 +397,7 @@ static void sco_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; + bt_sk(sk)->flags = bt_sk(parent)->flags; security_sk_clone(parent, sk); } } @@ -662,6 +663,28 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, return err; } +static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk = sock->sk; + struct sco_pinfo *pi = sco_pi(sk); + + lock_sock(sk); + + if (sk->sk_state == BT_CONNECT2 && + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { + hci_conn_accept(pi->conn->hcon, 0); + sk->sk_state = BT_CONFIG; + + release_sock(sk); + return 0; + } + + release_sock(sk); + + return bt_sock_recvmsg(iocb, sock, msg, len, flags); +} + static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; @@ -906,7 +929,10 @@ static void sco_conn_ready(struct sco_conn *conn) hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); - sk->sk_state = BT_CONNECTED; + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) + sk->sk_state = BT_CONNECT2; + else + sk->sk_state = BT_CONNECTED; /* Wake up parent */ parent->sk_data_ready(parent, 1); @@ -919,7 +945,7 @@ done: } /* ----- SCO interface with lower layer (HCI) ----- */ -int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) +int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) { struct sock *sk; struct hlist_node *node; @@ -936,6 +962,9 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) || !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { lm |= HCI_LM_ACCEPT; + + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) + *flags |= HCI_PROTO_DEFER; break; } } @@ -1024,7 +1053,7 @@ static const struct proto_ops sco_sock_ops = { .accept = sco_sock_accept, .getname = sco_sock_getname, .sendmsg = sco_sock_sendmsg, - .recvmsg = bt_sock_recvmsg, + .recvmsg = sco_sock_recvmsg, .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, -- cgit v1.2.3 From ffa88e02bc67a1496fae762ad899e8f49136e7a1 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 23 Nov 2012 16:50:51 -0200 Subject: Bluetooth: Move double negation to macros Some comparisons needs to double negation(!!) in order to make the value of the field boolean. Add it to the macro makes the code more readable. Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 6 +++--- net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/mgmt.c | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 76891a914e75..2f2b743f5b10 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -767,7 +767,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) #define lmp_pause_enc_capable(dev) ((dev)->features[5] & LMP_PAUSE_ENC) #define lmp_ext_inq_capable(dev) ((dev)->features[6] & LMP_EXT_INQ) -#define lmp_le_br_capable(dev) ((dev)->features[6] & LMP_SIMUL_LE_BR) +#define lmp_le_br_capable(dev) !!((dev)->features[6] & LMP_SIMUL_LE_BR) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) #define lmp_lsto_capable(dev) ((dev)->features[7] & LMP_LSTO) @@ -776,8 +776,8 @@ void hci_conn_del_sysfs(struct hci_conn *conn); /* ----- Extended LMP capabilities ----- */ #define lmp_host_ssp_capable(dev) ((dev)->host_features[0] & LMP_HOST_SSP) -#define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE) -#define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR) +#define lmp_host_le_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE) +#define lmp_host_le_br_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE_BREDR) /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3843f1897c86..705078a0cc39 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -794,10 +794,10 @@ static void hci_set_le_support(struct hci_dev *hdev) if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { cp.le = 1; - cp.simul = !!lmp_le_br_capable(hdev); + cp.simul = lmp_le_br_capable(hdev); } - if (cp.le != !!lmp_host_le_capable(hdev)) + if (cp.le != lmp_host_le_capable(hdev)) hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dedbb1d8b2d2..5d0ef759ef49 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1225,7 +1225,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } val = !!cp->val; - enabled = !!lmp_host_le_capable(hdev); + enabled = lmp_host_le_capable(hdev); if (!hdev_is_powered(hdev) || val == enabled) { bool changed = false; @@ -1261,7 +1261,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (val) { hci_cp.le = val; - hci_cp.simul = !!lmp_le_br_capable(hdev); + hci_cp.simul = lmp_le_br_capable(hdev); } err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), @@ -2924,13 +2924,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) struct hci_cp_write_le_host_supported cp; cp.le = 1; - cp.simul = !!lmp_le_br_capable(hdev); + cp.simul = lmp_le_br_capable(hdev); /* Check first if we already have the right * host state (host features set) */ - if (cp.le != !!lmp_host_le_capable(hdev) || - cp.simul != !!lmp_host_le_br_capable(hdev)) + if (cp.le != lmp_host_le_capable(hdev) || + cp.simul != lmp_host_le_br_capable(hdev)) hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); -- cgit v1.2.3 From 5e4e3972b8da23e270fa37670caec4d32f2197f7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 28 Nov 2012 17:59:39 +0200 Subject: Bluetooth: Refactor l2cap_send_disconn_req l2cap_send_disconn_req takes 3 parameters of which conn might be derived from chan. Make this conversion inside l2cap_send_disconn_req. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 56 +++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b52f66d22437..f7ee037c7934 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -53,8 +53,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data); static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); -static void l2cap_send_disconn_req(struct l2cap_conn *conn, - struct l2cap_chan *chan, int err); +static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err); static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event); @@ -632,7 +631,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { __set_chan_timer(chan, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, chan, reason); + l2cap_send_disconn_req(chan, reason); } else l2cap_chan_del(chan, reason); break; @@ -1180,10 +1179,10 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -static void l2cap_send_disconn_req(struct l2cap_conn *conn, - struct l2cap_chan *chan, int err) +static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; + struct l2cap_conn *conn = chan->conn; struct l2cap_disconn_req req; if (!conn) @@ -1960,7 +1959,7 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) if (chan->max_tx != 0 && bt_cb(skb)->control.retries > chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); l2cap_seq_list_clear(&chan->retrans_list); break; } @@ -2666,7 +2665,7 @@ static void l2cap_tx_state_wait_f(struct l2cap_chan *chan, __set_monitor_timer(chan); chan->retry_count++; } else { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); + l2cap_send_disconn_req(chan, ECONNABORTED); } break; default: @@ -3877,7 +3876,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, /* Complete config. */ len = l2cap_parse_conf_req(chan, rsp); if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto unlock; } @@ -3899,7 +3898,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, err = l2cap_ertm_init(chan); if (err < 0) - l2cap_send_disconn_req(chan->conn, chan, -err); + l2cap_send_disconn_req(chan, -err); else l2cap_chan_ready(chan); @@ -3967,7 +3966,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, len = l2cap_parse_conf_rsp(chan, rsp->data, len, buf, &result); if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -3988,7 +3987,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, char req[64]; if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -3997,7 +3996,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, len = l2cap_parse_conf_rsp(chan, rsp->data, len, req, &result); if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -4013,7 +4012,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_chan_set_err(chan, ECONNRESET); __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -4030,7 +4029,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, err = l2cap_ertm_init(chan); if (err < 0) - l2cap_send_disconn_req(chan->conn, chan, -err); + l2cap_send_disconn_req(chan, -err); else l2cap_chan_ready(chan); } @@ -4392,7 +4391,7 @@ static void l2cap_logical_fail(struct l2cap_chan *chan) /* Logical link setup failed */ if (chan->state != BT_CONNECTED) { /* Create channel failure, disconnect */ - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -4435,7 +4434,7 @@ static void l2cap_logical_finish_create(struct l2cap_chan *chan, err = l2cap_ertm_init(chan); if (err < 0) - l2cap_send_disconn_req(chan->conn, chan, -err); + l2cap_send_disconn_req(chan, -err); else l2cap_chan_ready(chan); } @@ -5400,7 +5399,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan, if (control->reqseq == chan->next_tx_seq) { BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5414,7 +5413,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan, if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5458,7 +5457,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan, if (control->reqseq == chan->next_tx_seq) { BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5467,7 +5466,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan, if (chan->max_tx && skb && bt_cb(skb)->control.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5651,8 +5650,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, break; case L2CAP_TXSEQ_INVALID: default: - l2cap_send_disconn_req(chan->conn, chan, - ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); break; } break; @@ -5785,8 +5783,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, break; case L2CAP_TXSEQ_INVALID: default: - l2cap_send_disconn_req(chan->conn, chan, - ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); break; } break; @@ -5981,7 +5978,7 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d", control->reqseq, chan->next_tx_seq, chan->expected_ack_seq); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); } return err; @@ -6050,7 +6047,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) len -= L2CAP_FCS_SIZE; if (len > chan->mps) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto drop; } @@ -6075,8 +6072,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) } if (err) - l2cap_send_disconn_req(chan->conn, chan, - ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); } else { const u8 rx_func_to_event[4] = { L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ, @@ -6093,7 +6089,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) if (len != 0) { BT_ERR("Trailing bytes: %d in sframe", len); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto drop; } @@ -6104,7 +6100,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) event = rx_func_to_event[control->super]; if (l2cap_rx(chan, control, skb, event)) - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); } return 0; -- cgit v1.2.3 From ced5c338d7b696021058c23fb6a286def2171df5 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 28 Nov 2012 17:59:42 +0200 Subject: Bluetooth: AMP: Mark controller radio powered down after HCIDEVDOWN After getting HCIDEVDOWN controller did not mark itself as 0x00 which means: "The Controller radio is available but is currently physically powered down". The result was even if the hdev was down we return in controller list value 0x01 "status 0x01 (Bluetooth only)". Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 69eb644f0d48..ec7d3a7adf74 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -861,6 +861,9 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Clear flags */ hdev->flags = 0; + /* Controller radio is available but is currently powered down */ + hdev->amp_status = 0; + memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); -- cgit v1.2.3 From 5d05416e0907b0dd30b62b002bed3b85e6efbd61 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:05 +0200 Subject: Bluetooth: AMP: Check that AMP is present and active Before starting quering remote AMP controllers make sure that there is local active AMP controller. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 16 ++++++++++++++++ net/bluetooth/l2cap_core.c | 1 + 2 files changed, 17 insertions(+) (limited to 'net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2f2b743f5b10..014a2eaa5389 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -779,6 +779,22 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_host_le_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE) #define lmp_host_le_br_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE_BREDR) +/* returns true if at least one AMP active */ +static inline bool hci_amp_capable(void) +{ + struct hci_dev *hdev; + bool ret = false; + + read_lock(&hci_dev_list_lock); + list_for_each_entry(hdev, &hci_dev_list, list) + if (hdev->amp_type == HCI_AMP && + test_bit(HCI_UP, &hdev->flags)) + ret = true; + read_unlock(&hci_dev_list_lock); + + return ret; +} + /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f7ee037c7934..d8cffdbf0d37 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1013,6 +1013,7 @@ static bool __amp_capable(struct l2cap_chan *chan) struct l2cap_conn *conn = chan->conn; if (enable_hs && + hci_amp_capable() && chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED && conn->fixed_chan_mask & L2CAP_FC_A2MP) return true; -- cgit v1.2.3 From 60918918a9f3455859a4be0d4e381003cbdb843f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:06 +0200 Subject: Bluetooth: Fix missing L2CAP EWS Conf parameter If L2CAP_FEAT_FCS is not supported we sould miss EWS option configuration because of break. Make code more readable by combining FCS configuration in the single block. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d8cffdbf0d37..d22d183ddd45 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3106,18 +3106,17 @@ done: if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { - chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); - } - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, chan->tx_win); + + if (chan->conn->feat_mask & L2CAP_FEAT_FCS) + if (chan->fcs == L2CAP_FCS_NONE || + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + chan->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, + chan->fcs); + } break; case L2CAP_MODE_STREAMING: @@ -3139,14 +3138,13 @@ done: if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { - chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); - } + if (chan->conn->feat_mask & L2CAP_FEAT_FCS) + if (chan->fcs == L2CAP_FCS_NONE || + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + chan->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, + chan->fcs); + } break; } -- cgit v1.2.3 From cbabee788ff881b87bbaf258caf080b49a61fa43 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:07 +0200 Subject: Bluetooth: Process receiving FCS_NONE in L2CAP Conf Rsp Process L2CAP Config rsp Pending with FCS Option 0x00 (No FCS) which is sent by Motorola Windows 7 Bluetooth stack. The trace is shown below (all other options are skipped). ... < ACL data: handle 1 flags 0x00 dlen 48 L2CAP(s): Config req: dcid 0x0043 flags 0x00 clen 36 ... FCS Option 0x00 (No FCS) > ACL data: handle 1 flags 0x02 dlen 48 L2CAP(s): Config req: dcid 0x0041 flags 0x00 clen 36 ... FCS Option 0x01 (CRC16 Check) < ACL data: handle 1 flags 0x00 dlen 47 L2CAP(s): Config rsp: scid 0x0043 flags 0x00 result 4 clen 33 Pending ... > ACL data: handle 1 flags 0x02 dlen 50 L2CAP(s): Config rsp: scid 0x0041 flags 0x00 result 4 clen 36 Pending ... FCS Option 0x00 (No FCS) < ACL data: handle 1 flags 0x00 dlen 14 L2CAP(s): Config rsp: scid 0x0043 flags 0x00 result 0 clen 0 Success > ACL data: handle 1 flags 0x02 dlen 14 L2CAP(s): Config rsp: scid 0x0041 flags 0x00 result 0 clen 0 Success ... Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d22d183ddd45..8c432771d6f9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3431,6 +3431,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); break; + + case L2CAP_CONF_FCS: + if (*result == L2CAP_CONF_PENDING) + if (val == L2CAP_FCS_NONE) + set_bit(CONF_NO_FCS_RECV, + &chan->conf_state); + break; } } -- cgit v1.2.3 From f2592d3ee3c5817981f343b90bfb9c5612f38d23 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:08 +0200 Subject: Bluetooth: trivial: Change NO_FCS_RECV to RECV_NO_FCS Make code more readable by changing CONF_NO_FCS_RECV which is read as "No L2CAP FCS option received" to CONF_RECV_NO_FCS which means "Received L2CAP option NO_FCS". This flag really means that we have received L2CAP FRAME CHECK SEQUENCE (FCS) OPTION with value "No FCS". Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f57fab04e7c5..7588ef44ebaf 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -611,7 +611,7 @@ enum { CONF_MTU_DONE, CONF_MODE_DONE, CONF_CONNECT_PEND, - CONF_NO_FCS_RECV, + CONF_RECV_NO_FCS, CONF_STATE2_DEVICE, CONF_EWS_RECV, CONF_LOC_CONF_PEND, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8c432771d6f9..2c78208d793e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3112,7 +3112,7 @@ done: if (chan->conn->feat_mask & L2CAP_FEAT_FCS) if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); @@ -3140,7 +3140,7 @@ done: if (chan->conn->feat_mask & L2CAP_FEAT_FCS) if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); @@ -3196,7 +3196,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) - set_bit(CONF_NO_FCS_RECV, &chan->conf_state); + set_bit(CONF_RECV_NO_FCS, &chan->conf_state); break; case L2CAP_CONF_EFS: @@ -3435,7 +3435,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, case L2CAP_CONF_FCS: if (*result == L2CAP_CONF_PENDING) if (val == L2CAP_FCS_NONE) - set_bit(CONF_NO_FCS_RECV, + set_bit(CONF_RECV_NO_FCS, &chan->conf_state); break; } @@ -3807,7 +3807,7 @@ static inline void set_default_fcs(struct l2cap_chan *chan) */ if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) chan->fcs = L2CAP_FCS_NONE; - else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) + else if (!test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) chan->fcs = L2CAP_FCS_CRC16; } -- cgit v1.2.3 From 0b27a4b97cb1874503c78453c0903df53c0c86b2 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Mon, 3 Dec 2012 15:36:51 -0200 Subject: Revert "Bluetooth: Fix possible deadlock in SCO code" This reverts commit 269c4845d5b3627b95b1934107251bacbe99bb68. The commit was causing dead locks and NULL dereferences in the sco code: [28084.104013] BUG: soft lockup - CPU#0 stuck for 22s! [kworker/u:0H:7] [28084.104021] Modules linked in: btusb bluetooth ] _raw_spin_lock+0xd/0x10 [28084.104021] [] sco_conn_del+0x58/0x1b0 [bluetooth] [28084.104021] [] sco_connect_cfm+0xb9/0x2b0 [bluetooth] [28084.104021] [] hci_sync_conn_complete_evt.isra.94+0x1c9/0x260 [bluetooth] [28084.104021] [] hci_event_packet+0x74d/0x2b40 [bluetooth] [28084.104021] [] ? __kfree_skb+0x3d/0x90 [28084.104021] [] ? kfree_skb+0x36/0x90 [28084.104021] [] ? hci_send_to_monitor+0x10e/0x190 [bluetooth] [28084.104021] [] ? hci_send_to_monitor+0x10e/0x190 [bluetooth] Cc: stable@vger.kernel.org Reported-by: Chan-yeol Park Signed-off-by: Gustavo Padovan --- net/bluetooth/sco.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index eea17cdcaf7f..531a93d613d4 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -131,15 +131,6 @@ static int sco_conn_del(struct hci_conn *hcon, int err) sco_sock_clear_timer(sk); sco_chan_del(sk, err); bh_unlock_sock(sk); - - sco_conn_lock(conn); - conn->sk = NULL; - sco_pi(sk)->conn = NULL; - sco_conn_unlock(conn); - - if (conn->hcon) - hci_conn_put(conn->hcon); - sco_sock_kill(sk); } @@ -885,6 +876,16 @@ static void sco_chan_del(struct sock *sk, int err) BT_DBG("sk %p, conn %p, err %d", sk, conn, err); + if (conn) { + sco_conn_lock(conn); + conn->sk = NULL; + sco_pi(sk)->conn = NULL; + sco_conn_unlock(conn); + + if (conn->hcon) + hci_conn_put(conn->hcon); + } + sk->sk_state = BT_CLOSED; sk->sk_err = err; sk->sk_state_change(sk); -- cgit v1.2.3 From 9e2b29d0d6ae05fb1ead7df68a209eece9c7749a Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 3 Dec 2012 09:56:26 -0500 Subject: rfkill: remove __dev* attributes CONFIG_HOTPLUG is going away as an option. As result the __dev* markings will be going away. Remove use of __devinit, __devexit_p, __devinitdata, __devinitconst, and __devexit. Signed-off-by: Bill Pemberton Cc: Johannes Berg Signed-off-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- net/rfkill/rfkill-gpio.c | 2 +- net/rfkill/rfkill-regulator.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 865adb61685a..78fc0937948d 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -213,7 +213,7 @@ static int rfkill_gpio_remove(struct platform_device *pdev) static struct platform_driver rfkill_gpio_driver = { .probe = rfkill_gpio_probe, - .remove = __devexit_p(rfkill_gpio_remove), + .remove = rfkill_gpio_remove, .driver = { .name = "rfkill_gpio", .owner = THIS_MODULE, diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c index 11da3018a853..4b5ab21ecb24 100644 --- a/net/rfkill/rfkill-regulator.c +++ b/net/rfkill/rfkill-regulator.c @@ -55,7 +55,7 @@ struct rfkill_ops rfkill_regulator_ops = { .set_block = rfkill_regulator_set_block, }; -static int __devinit rfkill_regulator_probe(struct platform_device *pdev) +static int rfkill_regulator_probe(struct platform_device *pdev) { struct rfkill_regulator_platform_data *pdata = pdev->dev.platform_data; struct rfkill_regulator_data *rfkill_data; @@ -122,7 +122,7 @@ out: return ret; } -static int __devexit rfkill_regulator_remove(struct platform_device *pdev) +static int rfkill_regulator_remove(struct platform_device *pdev) { struct rfkill_regulator_data *rfkill_data = platform_get_drvdata(pdev); struct rfkill *rf_kill = rfkill_data->rf_kill; @@ -137,7 +137,7 @@ static int __devexit rfkill_regulator_remove(struct platform_device *pdev) static struct platform_driver rfkill_regulator_driver = { .probe = rfkill_regulator_probe, - .remove = __devexit_p(rfkill_regulator_remove), + .remove = rfkill_regulator_remove, .driver = { .name = "rfkill-regulator", .owner = THIS_MODULE, -- cgit v1.2.3