diff mbox

[2/3] rtlwifi: Add beacon check mechanism to check if AP settings changed.

Message ID 20171209173710.9879-3-Larry.Finger@lwfinger.net (mailing list archive)
State Rejected
Delegated to: Kalle Valo
Headers show

Commit Message

Larry Finger Dec. 9, 2017, 5:37 p.m. UTC
From: Tsang-Shian Lin <thlin@realtek.com>

AP WiFi settings are changed(channel, bandwidth), but deauth may not
received by STA. For these cases, we need to detect and handle beacon
changes.

Signed-off-by: Tsang-Shian Lin <thlin@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Cc: Yan-Hsuan Chuang <yhchuang@realtek.com>
Cc: Birming Chiu <birming@realtek.com>
Cc: Shaofu <shaofu@realtek.com>
Cc: Steven Ting <steventing@realtek.com>
---
 drivers/net/wireless/realtek/rtlwifi/base.c | 179 ++++++++++++++++++++++++++++
 drivers/net/wireless/realtek/rtlwifi/base.h |   3 +-
 drivers/net/wireless/realtek/rtlwifi/core.c |  48 ++++++++
 drivers/net/wireless/realtek/rtlwifi/pci.c  |   2 +
 drivers/net/wireless/realtek/rtlwifi/wifi.h |  13 ++
 5 files changed, 244 insertions(+), 1 deletion(-)

Comments

Kalle Valo Dec. 14, 2017, 12:35 p.m. UTC | #1
Larry Finger <Larry.Finger@lwfinger.net> writes:

> From: Tsang-Shian Lin <thlin@realtek.com>
>
> AP WiFi settings are changed(channel, bandwidth), but deauth may not
> received by STA. For these cases, we need to detect and handle beacon
> changes.
>
> Signed-off-by: Tsang-Shian Lin <thlin@realtek.com>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
> Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
> Cc: Yan-Hsuan Chuang <yhchuang@realtek.com>
> Cc: Birming Chiu <birming@realtek.com>
> Cc: Shaofu <shaofu@realtek.com>
> Cc: Steven Ting <steventing@realtek.com>

[...]

> --- a/drivers/net/wireless/realtek/rtlwifi/base.c
> +++ b/drivers/net/wireless/realtek/rtlwifi/base.c
> @@ -2360,6 +2360,185 @@ struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw,
>  	return skb;
>  }
>  
> +bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data, unsigned int len)
> +{
> +	struct rtl_priv *rtlpriv = rtl_priv(hw);
> +	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
> +	struct rtl_phy *rtlphy = &rtlpriv->phy;
> +	struct ieee80211_hdr *hdr = data;
> +	struct ieee80211_ht_cap *ht_cap_ie;
> +	struct ieee80211_ht_operation *ht_oper_ie = NULL;
> +	struct rtl_beacon_keys bcn_key = {0};
> +	struct rtl_beacon_keys *cur_bcn_key;
> +	u8 *ht_cap;
> +	u8 ht_cap_len;
> +	u8 *ht_oper;
> +	u8 ht_oper_len;
> +	u8 *ds_param;
> +	u8 ds_param_len;
> +
> +	if (mac->opmode != NL80211_IFTYPE_STATION)
> +		return false;
> +
> +	/* check if this really is a beacon*/
> +	if (!ieee80211_is_beacon(hdr->frame_control))
> +		return false;
> +
> +	/* min. beacon length + FCS_LEN */
> +	if (len <= 40 + FCS_LEN)
> +		return false;
> +
> +	cur_bcn_key = &mac->cur_beacon_keys;
> +
> +	if (rtlpriv->mac80211.link_state == MAC80211_NOLINK) {
> +		if (cur_bcn_key->valid) {
> +			cur_bcn_key->valid = false;
> +			RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
> +				 "Reset cur_beacon_keys.valid to false!\n");
> +		}
> +		return false;
> +	}
> +
> +	/* and only beacons from the associated BSSID, please */
> +	if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
> +		return false;
> +
> +	/***** Parsing DS Param IE ******/
> +	ds_param = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_DS_PARAMS);
> +
> +	if (ds_param && !(ds_param[1] < sizeof(*ds_param)))
> +		ds_param_len = ds_param[1];
> +	else
> +		ds_param = NULL;
> +
> +	/***** Parsing HT Cap. IE ******/
> +	ht_cap = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_CAPABILITY);
> +
> +	if (ht_cap && !(ht_cap[1] < sizeof(*ht_cap))) {
> +		ht_cap_len = ht_cap[1];
> +		ht_cap_ie = (struct ieee80211_ht_cap *)&ht_cap[2];
> +	} else  {
> +		ht_cap = NULL;
> +		ht_cap_ie = NULL;
> +	}
> +
> +	/***** Parsing HT Info. IE ******/
> +	ht_oper = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_OPERATION);
> +
> +	if (ht_oper && !(ht_oper[1] < sizeof(*ht_oper))) {
> +		ht_oper_len = ht_oper[1];
> +		ht_oper_ie = (struct ieee80211_ht_operation *)&ht_oper[2];
> +	} else {
> +		ht_oper = NULL;
> +	}
> +
> +	/* update bcn_key */
> +	memset(&bcn_key, 0, sizeof(bcn_key));
> +
> +	if (ds_param)
> +		bcn_key.bcn_channel = ds_param[2];
> +	else if (ht_oper && ht_oper_ie)
> +		bcn_key.bcn_channel = ht_oper_ie->primary_chan;
> +
> +	if (ht_cap_ie)
> +		bcn_key.ht_cap_info = ht_cap_ie->cap_info;
> +
> +	if (ht_oper && ht_oper_ie)
> +		bcn_key.ht_info_infos_0_sco = ht_oper_ie->ht_param & 0x03;
> +
> +	bcn_key.valid = true;
> +
> +	/* update cur_beacon_keys or compare beacon key */
> +	if (rtlpriv->mac80211.link_state != MAC80211_LINKED &&
> +	    rtlpriv->mac80211.link_state != MAC80211_LINKED_SCANNING)
> +		return true;
> +
> +	if (!cur_bcn_key->valid) {
> +		/* update cur_beacon_keys */
> +		memset(cur_bcn_key, 0, sizeof(bcn_key));
> +		memcpy(cur_bcn_key, &bcn_key, sizeof(bcn_key));
> +		cur_bcn_key->valid = true;
> +
> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
> +			 "Beacon key update!ch=%d, ht_cap_info=0x%x, sco=0x%x\n",
> +			 cur_bcn_key->bcn_channel,
> +			 cur_bcn_key->ht_cap_info,
> +			 cur_bcn_key->ht_info_infos_0_sco);
> +
> +		return true;
> +	}
> +
> +	/* compare beacon key */
> +	if (!memcmp(cur_bcn_key, &bcn_key, sizeof(bcn_key))) {
> +		/* same beacon key */
> +		mac->new_beacon_cnt = 0;
> +		goto chk_exit;
> +	}
> +
> +	if (cur_bcn_key->bcn_channel == bcn_key.bcn_channel &&
> +	    cur_bcn_key->ht_cap_info == bcn_key.ht_cap_info) {
> +		/* Beacon HT info IE, secondary channel offset check */
> +		/* 40M -> 20M */
> +		if (cur_bcn_key->ht_info_infos_0_sco >
> +		    bcn_key.ht_info_infos_0_sco) {
> +			/* Not a new beacon */
> +			RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
> +				 "Beacon BW change! sco:0x%x -> 0x%x\n",
> +				 cur_bcn_key->ht_info_infos_0_sco,
> +				 bcn_key.ht_info_infos_0_sco);
> +
> +			cur_bcn_key->ht_info_infos_0_sco =
> +					bcn_key.ht_info_infos_0_sco;
> +		} else {
> +			/* 20M -> 40M */
> +			if (rtlphy->max_ht_chan_bw >= HT_CHANNEL_WIDTH_20_40) {
> +				/* Not a new beacon */
> +				RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
> +					 "Beacon BW change! sco:0x%x -> 0x%x\n",
> +					 cur_bcn_key->ht_info_infos_0_sco,
> +					 bcn_key.ht_info_infos_0_sco);
> +
> +				cur_bcn_key->ht_info_infos_0_sco =
> +					bcn_key.ht_info_infos_0_sco;
> +			} else {
> +				mac->new_beacon_cnt++;
> +			}
> +		}
> +	} else {
> +		mac->new_beacon_cnt++;
> +	}
> +
> +	if (mac->new_beacon_cnt == 1) {
> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
> +			 "Get new beacon.\n");
> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
> +			 "Cur : ch=%d, ht_cap=0x%x, sco=0x%x\n",
> +			 cur_bcn_key->bcn_channel,
> +			 cur_bcn_key->ht_cap_info,
> +			 cur_bcn_key->ht_info_infos_0_sco);
> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
> +			 "New RX : ch=%d, ht_cap=0x%x, sco=0x%x\n",
> +			 bcn_key.bcn_channel,
> +			 bcn_key.ht_cap_info,
> +			 bcn_key.ht_info_infos_0_sco);
> +
> +	} else if (mac->new_beacon_cnt > 1) {
> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
> +			 "new beacon cnt: %d\n",
> +			 mac->new_beacon_cnt);
> +	}
> +
> +	if (mac->new_beacon_cnt > 3) {
> +		ieee80211_connection_loss(rtlpriv->mac80211.vif);
> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
> +			 "new beacon cnt >3, disconnect !\n");
> +	}
> +
> +chk_exit:
> +
> +	return true;
> +}
> +EXPORT_SYMBOL_GPL(rtl_check_beacon_key);

Why do all this in the driver? I would expect something like this to be
done in mac80211, not in the driver.

> +struct rtl_beacon_keys {
> +	/*u8 ssid[32];*/
> +	/*u32 ssid_len;*/

Commented out fields, please drop.
Larry Finger Dec. 14, 2017, 4:06 p.m. UTC | #2
On 12/14/2017 06:35 AM, Kalle Valo wrote:
> Larry Finger <Larry.Finger@lwfinger.net> writes:
> 
>> From: Tsang-Shian Lin <thlin@realtek.com>
>>
>> +bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data, unsigned int len)
>> +{
>> +	struct rtl_priv *rtlpriv = rtl_priv(hw);
>> +	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
>> +	struct rtl_phy *rtlphy = &rtlpriv->phy;
>> +	struct ieee80211_hdr *hdr = data;
>> +	struct ieee80211_ht_cap *ht_cap_ie;
>> +	struct ieee80211_ht_operation *ht_oper_ie = NULL;
>> +	struct rtl_beacon_keys bcn_key = {0};
>> +	struct rtl_beacon_keys *cur_bcn_key;
>> +	u8 *ht_cap;
>> +	u8 ht_cap_len;
>> +	u8 *ht_oper;
>> +	u8 ht_oper_len;
>> +	u8 *ds_param;
>> +	u8 ds_param_len;
>> +
>> +	if (mac->opmode != NL80211_IFTYPE_STATION)
>> +		return false;
>> +
>> +	/* check if this really is a beacon*/
>> +	if (!ieee80211_is_beacon(hdr->frame_control))
>> +		return false;
>> +
>> +	/* min. beacon length + FCS_LEN */
>> +	if (len <= 40 + FCS_LEN)
>> +		return false;
>> +
>> +	cur_bcn_key = &mac->cur_beacon_keys;
>> +
>> +	if (rtlpriv->mac80211.link_state == MAC80211_NOLINK) {
>> +		if (cur_bcn_key->valid) {
>> +			cur_bcn_key->valid = false;
>> +			RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
>> +				 "Reset cur_beacon_keys.valid to false!\n");
>> +		}
>> +		return false;
>> +	}
>> +
>> +	/* and only beacons from the associated BSSID, please */
>> +	if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
>> +		return false;
>> +
>> +	/***** Parsing DS Param IE ******/
>> +	ds_param = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_DS_PARAMS);
>> +
>> +	if (ds_param && !(ds_param[1] < sizeof(*ds_param)))
>> +		ds_param_len = ds_param[1];
>> +	else
>> +		ds_param = NULL;
>> +
>> +	/***** Parsing HT Cap. IE ******/
>> +	ht_cap = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_CAPABILITY);
>> +
>> +	if (ht_cap && !(ht_cap[1] < sizeof(*ht_cap))) {
>> +		ht_cap_len = ht_cap[1];
>> +		ht_cap_ie = (struct ieee80211_ht_cap *)&ht_cap[2];
>> +	} else  {
>> +		ht_cap = NULL;
>> +		ht_cap_ie = NULL;
>> +	}
>> +
>> +	/***** Parsing HT Info. IE ******/
>> +	ht_oper = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_OPERATION);
>> +
>> +	if (ht_oper && !(ht_oper[1] < sizeof(*ht_oper))) {
>> +		ht_oper_len = ht_oper[1];
>> +		ht_oper_ie = (struct ieee80211_ht_operation *)&ht_oper[2];
>> +	} else {
>> +		ht_oper = NULL;
>> +	}
>> +
>> +	/* update bcn_key */
>> +	memset(&bcn_key, 0, sizeof(bcn_key));
>> +
>> +	if (ds_param)
>> +		bcn_key.bcn_channel = ds_param[2];
>> +	else if (ht_oper && ht_oper_ie)
>> +		bcn_key.bcn_channel = ht_oper_ie->primary_chan;
>> +
>> +	if (ht_cap_ie)
>> +		bcn_key.ht_cap_info = ht_cap_ie->cap_info;
>> +
>> +	if (ht_oper && ht_oper_ie)
>> +		bcn_key.ht_info_infos_0_sco = ht_oper_ie->ht_param & 0x03;
>> +
>> +	bcn_key.valid = true;
>> +
>> +	/* update cur_beacon_keys or compare beacon key */
>> +	if (rtlpriv->mac80211.link_state != MAC80211_LINKED &&
>> +	    rtlpriv->mac80211.link_state != MAC80211_LINKED_SCANNING)
>> +		return true;
>> +
>> +	if (!cur_bcn_key->valid) {
>> +		/* update cur_beacon_keys */
>> +		memset(cur_bcn_key, 0, sizeof(bcn_key));
>> +		memcpy(cur_bcn_key, &bcn_key, sizeof(bcn_key));
>> +		cur_bcn_key->valid = true;
>> +
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
>> +			 "Beacon key update!ch=%d, ht_cap_info=0x%x, sco=0x%x\n",
>> +			 cur_bcn_key->bcn_channel,
>> +			 cur_bcn_key->ht_cap_info,
>> +			 cur_bcn_key->ht_info_infos_0_sco);
>> +
>> +		return true;
>> +	}
>> +
>> +	/* compare beacon key */
>> +	if (!memcmp(cur_bcn_key, &bcn_key, sizeof(bcn_key))) {
>> +		/* same beacon key */
>> +		mac->new_beacon_cnt = 0;
>> +		goto chk_exit;
>> +	}
>> +
>> +	if (cur_bcn_key->bcn_channel == bcn_key.bcn_channel &&
>> +	    cur_bcn_key->ht_cap_info == bcn_key.ht_cap_info) {
>> +		/* Beacon HT info IE, secondary channel offset check */
>> +		/* 40M -> 20M */
>> +		if (cur_bcn_key->ht_info_infos_0_sco >
>> +		    bcn_key.ht_info_infos_0_sco) {
>> +			/* Not a new beacon */
>> +			RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +				 "Beacon BW change! sco:0x%x -> 0x%x\n",
>> +				 cur_bcn_key->ht_info_infos_0_sco,
>> +				 bcn_key.ht_info_infos_0_sco);
>> +
>> +			cur_bcn_key->ht_info_infos_0_sco =
>> +					bcn_key.ht_info_infos_0_sco;
>> +		} else {
>> +			/* 20M -> 40M */
>> +			if (rtlphy->max_ht_chan_bw >= HT_CHANNEL_WIDTH_20_40) {
>> +				/* Not a new beacon */
>> +				RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +					 "Beacon BW change! sco:0x%x -> 0x%x\n",
>> +					 cur_bcn_key->ht_info_infos_0_sco,
>> +					 bcn_key.ht_info_infos_0_sco);
>> +
>> +				cur_bcn_key->ht_info_infos_0_sco =
>> +					bcn_key.ht_info_infos_0_sco;
>> +			} else {
>> +				mac->new_beacon_cnt++;
>> +			}
>> +		}
>> +	} else {
>> +		mac->new_beacon_cnt++;
>> +	}
>> +
>> +	if (mac->new_beacon_cnt == 1) {
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "Get new beacon.\n");
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "Cur : ch=%d, ht_cap=0x%x, sco=0x%x\n",
>> +			 cur_bcn_key->bcn_channel,
>> +			 cur_bcn_key->ht_cap_info,
>> +			 cur_bcn_key->ht_info_infos_0_sco);
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "New RX : ch=%d, ht_cap=0x%x, sco=0x%x\n",
>> +			 bcn_key.bcn_channel,
>> +			 bcn_key.ht_cap_info,
>> +			 bcn_key.ht_info_infos_0_sco);
>> +
>> +	} else if (mac->new_beacon_cnt > 1) {
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "new beacon cnt: %d\n",
>> +			 mac->new_beacon_cnt);
>> +	}
>> +
>> +	if (mac->new_beacon_cnt > 3) {
>> +		ieee80211_connection_loss(rtlpriv->mac80211.vif);
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "new beacon cnt >3, disconnect !\n");
>> +	}
>> +
>> +chk_exit:
>> +
>> +	return true;
>> +}
>> +EXPORT_SYMBOL_GPL(rtl_check_beacon_key);
> 
> Why do all this in the driver? I would expect something like this to be
> done in mac80211, not in the driver.

Johannes,

Does mac80211 have this facility? If so, how would we tap into it? If this 
capability does not exist in mac80211, how would one add it? I have never 
devoted much effort to looking at the internals of mac80211.
> 
>> +struct rtl_beacon_keys {
>> +	/*u8 ssid[32];*/
>> +	/*u32 ssid_len;*/
> 
> Commented out fields, please drop.

Yes, those will be dropped.

Larry
Johannes Berg Dec. 19, 2017, 9:41 a.m. UTC | #3
On Thu, 2017-12-14 at 10:06 -0600, Larry Finger wrote:

> Does mac80211 have this facility? If so, how would we tap into it? If this 
> capability does not exist in mac80211, how would one add it? I have never 
> devoted much effort to looking at the internals of mac80211.

It really should, and this code looks awful - but can you tell me what
exactly this is doing?

If the AP changed settings, then we should handle this in
ieee80211_rx_mgmt_beacon() and simply reconfigure the driver
accordingly.

johannes
diff mbox

Patch

diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index cad2272ae21b..a04afdd55569 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -2360,6 +2360,185 @@  struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw,
 	return skb;
 }
 
+bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data, unsigned int len)
+{
+	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+	struct rtl_phy *rtlphy = &rtlpriv->phy;
+	struct ieee80211_hdr *hdr = data;
+	struct ieee80211_ht_cap *ht_cap_ie;
+	struct ieee80211_ht_operation *ht_oper_ie = NULL;
+	struct rtl_beacon_keys bcn_key = {0};
+	struct rtl_beacon_keys *cur_bcn_key;
+	u8 *ht_cap;
+	u8 ht_cap_len;
+	u8 *ht_oper;
+	u8 ht_oper_len;
+	u8 *ds_param;
+	u8 ds_param_len;
+
+	if (mac->opmode != NL80211_IFTYPE_STATION)
+		return false;
+
+	/* check if this really is a beacon*/
+	if (!ieee80211_is_beacon(hdr->frame_control))
+		return false;
+
+	/* min. beacon length + FCS_LEN */
+	if (len <= 40 + FCS_LEN)
+		return false;
+
+	cur_bcn_key = &mac->cur_beacon_keys;
+
+	if (rtlpriv->mac80211.link_state == MAC80211_NOLINK) {
+		if (cur_bcn_key->valid) {
+			cur_bcn_key->valid = false;
+			RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
+				 "Reset cur_beacon_keys.valid to false!\n");
+		}
+		return false;
+	}
+
+	/* and only beacons from the associated BSSID, please */
+	if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
+		return false;
+
+	/***** Parsing DS Param IE ******/
+	ds_param = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_DS_PARAMS);
+
+	if (ds_param && !(ds_param[1] < sizeof(*ds_param)))
+		ds_param_len = ds_param[1];
+	else
+		ds_param = NULL;
+
+	/***** Parsing HT Cap. IE ******/
+	ht_cap = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_CAPABILITY);
+
+	if (ht_cap && !(ht_cap[1] < sizeof(*ht_cap))) {
+		ht_cap_len = ht_cap[1];
+		ht_cap_ie = (struct ieee80211_ht_cap *)&ht_cap[2];
+	} else  {
+		ht_cap = NULL;
+		ht_cap_ie = NULL;
+	}
+
+	/***** Parsing HT Info. IE ******/
+	ht_oper = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_OPERATION);
+
+	if (ht_oper && !(ht_oper[1] < sizeof(*ht_oper))) {
+		ht_oper_len = ht_oper[1];
+		ht_oper_ie = (struct ieee80211_ht_operation *)&ht_oper[2];
+	} else {
+		ht_oper = NULL;
+	}
+
+	/* update bcn_key */
+	memset(&bcn_key, 0, sizeof(bcn_key));
+
+	if (ds_param)
+		bcn_key.bcn_channel = ds_param[2];
+	else if (ht_oper && ht_oper_ie)
+		bcn_key.bcn_channel = ht_oper_ie->primary_chan;
+
+	if (ht_cap_ie)
+		bcn_key.ht_cap_info = ht_cap_ie->cap_info;
+
+	if (ht_oper && ht_oper_ie)
+		bcn_key.ht_info_infos_0_sco = ht_oper_ie->ht_param & 0x03;
+
+	bcn_key.valid = true;
+
+	/* update cur_beacon_keys or compare beacon key */
+	if (rtlpriv->mac80211.link_state != MAC80211_LINKED &&
+	    rtlpriv->mac80211.link_state != MAC80211_LINKED_SCANNING)
+		return true;
+
+	if (!cur_bcn_key->valid) {
+		/* update cur_beacon_keys */
+		memset(cur_bcn_key, 0, sizeof(bcn_key));
+		memcpy(cur_bcn_key, &bcn_key, sizeof(bcn_key));
+		cur_bcn_key->valid = true;
+
+		RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
+			 "Beacon key update!ch=%d, ht_cap_info=0x%x, sco=0x%x\n",
+			 cur_bcn_key->bcn_channel,
+			 cur_bcn_key->ht_cap_info,
+			 cur_bcn_key->ht_info_infos_0_sco);
+
+		return true;
+	}
+
+	/* compare beacon key */
+	if (!memcmp(cur_bcn_key, &bcn_key, sizeof(bcn_key))) {
+		/* same beacon key */
+		mac->new_beacon_cnt = 0;
+		goto chk_exit;
+	}
+
+	if (cur_bcn_key->bcn_channel == bcn_key.bcn_channel &&
+	    cur_bcn_key->ht_cap_info == bcn_key.ht_cap_info) {
+		/* Beacon HT info IE, secondary channel offset check */
+		/* 40M -> 20M */
+		if (cur_bcn_key->ht_info_infos_0_sco >
+		    bcn_key.ht_info_infos_0_sco) {
+			/* Not a new beacon */
+			RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+				 "Beacon BW change! sco:0x%x -> 0x%x\n",
+				 cur_bcn_key->ht_info_infos_0_sco,
+				 bcn_key.ht_info_infos_0_sco);
+
+			cur_bcn_key->ht_info_infos_0_sco =
+					bcn_key.ht_info_infos_0_sco;
+		} else {
+			/* 20M -> 40M */
+			if (rtlphy->max_ht_chan_bw >= HT_CHANNEL_WIDTH_20_40) {
+				/* Not a new beacon */
+				RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+					 "Beacon BW change! sco:0x%x -> 0x%x\n",
+					 cur_bcn_key->ht_info_infos_0_sco,
+					 bcn_key.ht_info_infos_0_sco);
+
+				cur_bcn_key->ht_info_infos_0_sco =
+					bcn_key.ht_info_infos_0_sco;
+			} else {
+				mac->new_beacon_cnt++;
+			}
+		}
+	} else {
+		mac->new_beacon_cnt++;
+	}
+
+	if (mac->new_beacon_cnt == 1) {
+		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+			 "Get new beacon.\n");
+		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+			 "Cur : ch=%d, ht_cap=0x%x, sco=0x%x\n",
+			 cur_bcn_key->bcn_channel,
+			 cur_bcn_key->ht_cap_info,
+			 cur_bcn_key->ht_info_infos_0_sco);
+		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+			 "New RX : ch=%d, ht_cap=0x%x, sco=0x%x\n",
+			 bcn_key.bcn_channel,
+			 bcn_key.ht_cap_info,
+			 bcn_key.ht_info_infos_0_sco);
+
+	} else if (mac->new_beacon_cnt > 1) {
+		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+			 "new beacon cnt: %d\n",
+			 mac->new_beacon_cnt);
+	}
+
+	if (mac->new_beacon_cnt > 3) {
+		ieee80211_connection_loss(rtlpriv->mac80211.vif);
+		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+			 "new beacon cnt >3, disconnect !\n");
+	}
+
+chk_exit:
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(rtl_check_beacon_key);
 /*********************************************************
  *
  * IOT functions
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.h b/drivers/net/wireless/realtek/rtlwifi/base.h
index 26735319b38f..3ad8e8107209 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.h
+++ b/drivers/net/wireless/realtek/rtlwifi/base.h
@@ -176,5 +176,6 @@  u8 rtl_tid_to_ac(u8 tid);
 void rtl_easy_concurrent_retrytimer_callback(struct timer_list *t);
 extern struct rtl_global_var rtl_global_var;
 void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation);
-
+bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data,
+			  unsigned int len);
 #endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index 3cb88825473e..195f7c41b4aa 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -909,6 +909,7 @@  static int rtl_op_sta_add(struct ieee80211_hw *hw,
 			 struct ieee80211_sta *sta)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	struct rtl_phy *rtlphy = &rtlpriv->phy;
 	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 	struct rtl_sta_info *sta_entry;
@@ -941,6 +942,16 @@  static int rtl_op_sta_add(struct ieee80211_hw *hw,
 		if (mac->p2p)
 			sta->supp_rates[0] &= 0xfffffff0;
 
+		if (sta->ht_cap.ht_supported) {
+			if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+				rtlphy->max_ht_chan_bw = HT_CHANNEL_WIDTH_20_40;
+			else
+				rtlphy->max_ht_chan_bw = HT_CHANNEL_WIDTH_20;
+		}
+
+		if (sta->vht_cap.vht_supported)
+			rtlphy->max_vht_chan_bw = HT_CHANNEL_WIDTH_80;
+
 		memcpy(sta_entry->mac_addr, sta->addr, ETH_ALEN);
 		RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG,
 			"Add sta addr is %pM\n", sta->addr);
@@ -1039,6 +1050,7 @@  static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
 				    u32 changed)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
+	struct rtl_phy *rtlphy = &rtlpriv->phy;
 	struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -1241,6 +1253,16 @@  static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
 			    mac->current_ampdu_factor)
 				mac->current_ampdu_factor =
 				    sta->ht_cap.ampdu_factor;
+
+			if (sta->ht_cap.ht_supported) {
+				if (sta->ht_cap.cap &
+				    IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+					rtlphy->max_ht_chan_bw =
+							HT_CHANNEL_WIDTH_20_40;
+				else
+					rtlphy->max_ht_chan_bw =
+							HT_CHANNEL_WIDTH_20;
+			}
 		}
 		rcu_read_unlock();
 
@@ -1252,6 +1274,32 @@  static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
 					      &mac->current_ampdu_density);
 	}
 
+	if (changed & BSS_CHANGED_BANDWIDTH) {
+		struct ieee80211_sta *sta = NULL;
+
+		RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
+			 "BSS_CHANGED_BANDWIDTH\n");
+
+		rcu_read_lock();
+		sta = ieee80211_find_sta(vif, (u8 *)bss_conf->bssid);
+
+		if (sta) {
+			if (sta->ht_cap.ht_supported) {
+				if (sta->ht_cap.cap &
+				    IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+					rtlphy->max_ht_chan_bw =
+							HT_CHANNEL_WIDTH_20_40;
+				else
+					rtlphy->max_ht_chan_bw =
+							HT_CHANNEL_WIDTH_20;
+			}
+
+			if (sta->vht_cap.vht_supported)
+				rtlphy->max_vht_chan_bw = HT_CHANNEL_WIDTH_80;
+		}
+		rcu_read_unlock();
+	}
+
 	if (changed & BSS_CHANGED_BSSID) {
 		u32 basic_rates;
 		struct ieee80211_sta *sta = NULL;
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 8ae36a263426..0bafcefacad0 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -877,6 +877,8 @@  static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
 			     ieee80211_is_probe_resp(fc))) {
 				dev_kfree_skb_any(skb);
 			} else {
+				rtl_check_beacon_key(hw, (void *)skb->data,
+						     skb->len);
 				_rtl_pci_rx_to_mac80211(hw, skb, rx_status);
 			}
 		} else {
diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h
index e2b14793b705..d3ea6260f01c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/wifi.h
+++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h
@@ -962,6 +962,15 @@  struct rtl_probe_rsp {
 	struct rtl_info_element info_element[0];
 } __packed;
 
+struct rtl_beacon_keys {
+	/*u8 ssid[32];*/
+	/*u32 ssid_len;*/
+	u8 bcn_channel;
+	__le16 ht_cap_info;
+	u8 ht_info_infos_0_sco; /* bit0 & bit1 in infos[0] is 2nd ch offset */
+	bool valid;
+};
+
 /*LED related.*/
 /*ledpin Identify how to implement this SW led.*/
 struct rtl_led {
@@ -1209,6 +1218,8 @@  struct rtl_phy {
 	u8 rf_mode;
 	u8 rf_type;
 	u8 current_chan_bw;
+	u8 max_ht_chan_bw;
+	u8 max_vht_chan_bw;
 	u8 set_bwmode_inprogress;
 	u8 sw_chnl_inprogress;
 	u8 sw_chnl_stage;
@@ -1380,6 +1391,8 @@  struct rtl_mac {
 	/*Probe Beacon management */
 	struct rtl_tid_data tids[MAX_TID_COUNT];
 	enum rtl_link_state link_state;
+	struct rtl_beacon_keys cur_beacon_keys;
+	u8 new_beacon_cnt;
 
 	int n_channels;
 	int n_bitrates;