diff options
Diffstat (limited to 'drivers/net/wireless/realtek/rtlwifi/base.c')
| -rw-r--r-- | drivers/net/wireless/realtek/rtlwifi/base.c | 288 | 
1 files changed, 268 insertions, 20 deletions
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index bdc379178e87..e36ee592c660 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -405,6 +405,10 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw)  		ieee80211_hw_set(hw, SUPPORTS_PS);  		ieee80211_hw_set(hw, PS_NULLFUNC_STACK);  	} +	if (rtlpriv->psc.fwctrl_lps) { +		ieee80211_hw_set(hw, SUPPORTS_PS); +		ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); +	}  	hw->wiphy->interface_modes =  	    BIT(NL80211_IFTYPE_AP) |  	    BIT(NL80211_IFTYPE_STATION) | @@ -560,6 +564,7 @@ int rtl_init_core(struct ieee80211_hw *hw)  	spin_lock_init(&rtlpriv->locks.waitq_lock);  	spin_lock_init(&rtlpriv->locks.entry_list_lock);  	spin_lock_init(&rtlpriv->locks.c2hcmd_lock); +	spin_lock_init(&rtlpriv->locks.scan_list_lock);  	spin_lock_init(&rtlpriv->locks.cck_and_rw_pagea_lock);  	spin_lock_init(&rtlpriv->locks.check_sendpkt_lock);  	spin_lock_init(&rtlpriv->locks.fw_ps_lock); @@ -568,6 +573,7 @@ int rtl_init_core(struct ieee80211_hw *hw)  	/* <5> init list */  	INIT_LIST_HEAD(&rtlpriv->entry_list);  	INIT_LIST_HEAD(&rtlpriv->c2hcmd_list); +	INIT_LIST_HEAD(&rtlpriv->scan_list.list);  	rtlmac->link_state = MAC80211_NOLINK; @@ -578,9 +584,12 @@ int rtl_init_core(struct ieee80211_hw *hw)  }  EXPORT_SYMBOL_GPL(rtl_init_core); +static void rtl_free_entries_from_scan_list(struct ieee80211_hw *hw); +  void rtl_deinit_core(struct ieee80211_hw *hw)  {  	rtl_c2hcmd_launcher(hw, 0); +	rtl_free_entries_from_scan_list(hw);  }  EXPORT_SYMBOL_GPL(rtl_deinit_core); @@ -1110,6 +1119,9 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw,  	if (txrate)  		tcb_desc->hw_rate = txrate->hw_value; +	if (rtl_is_tx_report_skb(hw, skb)) +		tcb_desc->use_spe_rpt = 1; +  	if (ieee80211_is_data(fc)) {  		/*  		 *we set data rate INX 0 @@ -1306,33 +1318,26 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)  }  EXPORT_SYMBOL_GPL(rtl_action_proc); -static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc) +static void setup_special_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc, +			     int type)  {  	struct ieee80211_hw *hw = rtlpriv->hw;  	rtlpriv->ra.is_special_data = true;  	if (rtlpriv->cfg->ops->get_btc_status())  		rtlpriv->btcoexist.btc_ops->btc_special_packet_notify( -					rtlpriv, 1); +					rtlpriv, type);  	rtl_lps_leave(hw);  	ppsc->last_delaylps_stamp_jiffies = jiffies;  } -/*should call before software enc*/ -u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx, -		       bool is_enc) +static const u8 *rtl_skb_ether_type_ptr(struct ieee80211_hw *hw, +					struct sk_buff *skb, bool is_enc)  {  	struct rtl_priv *rtlpriv = rtl_priv(hw); -	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); -	__le16 fc = rtl_get_fc(skb); -	u16 ether_type;  	u8 mac_hdr_len = ieee80211_get_hdrlen_from_skb(skb);  	u8 encrypt_header_len = 0;  	u8 offset; -	const struct iphdr *ip; - -	if (!ieee80211_is_data(fc)) -		goto end;  	switch (rtlpriv->sec.pairwise_enc_algorithm) {  	case WEP40_ENCRYPTION: @@ -1352,10 +1357,29 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,  	offset = mac_hdr_len + SNAP_SIZE;  	if (is_enc)  		offset += encrypt_header_len; -	ether_type = be16_to_cpup((__be16 *)(skb->data + offset)); + +	return skb->data + offset; +} + +/*should call before software enc*/ +u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx, +		       bool is_enc) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); +	__le16 fc = rtl_get_fc(skb); +	u16 ether_type; +	const u8 *ether_type_ptr; +	const struct iphdr *ip; + +	if (!ieee80211_is_data(fc)) +		goto end; + +	ether_type_ptr = rtl_skb_ether_type_ptr(hw, skb, is_enc); +	ether_type = be16_to_cpup((__be16 *)ether_type_ptr);  	if (ETH_P_IP == ether_type) { -		ip = (struct iphdr *)((u8 *)skb->data + offset + +		ip = (struct iphdr *)((u8 *)ether_type_ptr +  		     PROTOC_TYPE_SIZE);  		if (IPPROTO_UDP == ip->protocol) {  			struct udphdr *udp = (struct udphdr *)((u8 *)ip + @@ -1372,13 +1396,15 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,  					 (is_tx) ? "Tx" : "Rx");  				if (is_tx) -					setup_arp_tx(rtlpriv, ppsc); +					setup_special_tx(rtlpriv, ppsc, +							 PACKET_DHCP); +  				return true;  			}  		}  	} else if (ETH_P_ARP == ether_type) {  		if (is_tx) -			setup_arp_tx(rtlpriv, ppsc); +			setup_special_tx(rtlpriv, ppsc, PACKET_ARP);  		return true;  	} else if (ETH_P_PAE == ether_type) { @@ -1389,6 +1415,8 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,  			rtlpriv->ra.is_special_data = true;  			rtl_lps_leave(hw);  			ppsc->last_delaylps_stamp_jiffies = jiffies; + +			setup_special_tx(rtlpriv, ppsc, PACKET_EAPOL);  		}  		return true; @@ -1405,6 +1433,96 @@ end:  }  EXPORT_SYMBOL_GPL(rtl_is_special_data); +bool rtl_is_tx_report_skb(struct ieee80211_hw *hw, struct sk_buff *skb) +{ +	u16 ether_type; +	const u8 *ether_type_ptr; + +	ether_type_ptr = rtl_skb_ether_type_ptr(hw, skb, true); +	ether_type = be16_to_cpup((__be16 *)ether_type_ptr); + +	/* EAPOL */ +	if (ether_type == ETH_P_PAE) +		return true; + +	return false; +} + +static u16 rtl_get_tx_report_sn(struct ieee80211_hw *hw) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	struct rtl_tx_report *tx_report = &rtlpriv->tx_report; +	u16 sn; + +	sn = atomic_inc_return(&tx_report->sn) & 0x0FFF; + +	tx_report->last_sent_sn = sn; +	tx_report->last_sent_time = jiffies; + +	RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_DMESG, +		 "Send TX-Report sn=0x%X\n", sn); + +	return sn; +} + +void rtl_get_tx_report(struct rtl_tcb_desc *ptcb_desc, u8 *pdesc, +		       struct ieee80211_hw *hw) +{ +	if (ptcb_desc->use_spe_rpt) { +		u16 sn = rtl_get_tx_report_sn(hw); + +		SET_TX_DESC_SPE_RPT(pdesc, 1); +		SET_TX_DESC_SW_DEFINE(pdesc, sn); +	} +} +EXPORT_SYMBOL_GPL(rtl_get_tx_report); + +void rtl_tx_report_handler(struct ieee80211_hw *hw, u8 *tmp_buf, u8 c2h_cmd_len) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	struct rtl_tx_report *tx_report = &rtlpriv->tx_report; +	u16 sn; + +	sn = ((tmp_buf[7] & 0x0F) << 8) | tmp_buf[6]; + +	tx_report->last_recv_sn = sn; + +	RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_DMESG, +		 "Recv TX-Report st=0x%02X sn=0x%X retry=0x%X\n", +		 tmp_buf[0], sn, tmp_buf[2]); +} +EXPORT_SYMBOL_GPL(rtl_tx_report_handler); + +bool rtl_check_tx_report_acked(struct ieee80211_hw *hw) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	struct rtl_tx_report *tx_report = &rtlpriv->tx_report; + +	if (tx_report->last_sent_sn == tx_report->last_recv_sn) +		return true; + +	if (time_before(tx_report->last_sent_time + 3 * HZ, jiffies)) { +		RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_WARNING, +			 "Check TX-Report timeout!!\n"); +		return true;	/* 3 sec. (timeout) seen as acked */ +	} + +	return false; +} + +void rtl_wait_tx_report_acked(struct ieee80211_hw *hw, u32 wait_ms) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	int i; + +	for (i = 0; i < wait_ms; i++) { +		if (rtl_check_tx_report_acked(hw)) +			break; +		usleep_range(1000, 2000); +		RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, +			 "Wait 1ms (%d/%d) to disable key.\n", i, wait_ms); +	} +}  /*********************************************************   *   * functions called by core.c @@ -1469,6 +1587,7 @@ int rtl_rx_agg_start(struct ieee80211_hw *hw,  	struct rtl_priv *rtlpriv = rtl_priv(hw);  	struct rtl_tid_data *tid_data;  	struct rtl_sta_info *sta_entry = NULL; +	u8 reject_agg;  	if (sta == NULL)  		return -EINVAL; @@ -1476,6 +1595,14 @@ int rtl_rx_agg_start(struct ieee80211_hw *hw,  	if (unlikely(tid >= MAX_TID_COUNT))  		return -EINVAL; +	if (rtlpriv->cfg->ops->get_btc_status()) { +		rtlpriv->btcoexist.btc_ops->btc_get_ampdu_cfg(rtlpriv, +							      &reject_agg, +							      NULL, NULL); +		if (reject_agg) +			return -EINVAL; +	} +  	sta_entry = (struct rtl_sta_info *)sta->drv_priv;  	if (!sta_entry)  		return -ENXIO; @@ -1530,6 +1657,24 @@ int rtl_tx_agg_oper(struct ieee80211_hw *hw,  	return 0;  } +void rtl_rx_ampdu_apply(struct rtl_priv *rtlpriv) +{ +	struct rtl_btc_ops *btc_ops = rtlpriv->btcoexist.btc_ops; +	u8 reject_agg, ctrl_agg_size = 0, agg_size; + +	if (rtlpriv->cfg->ops->get_btc_status()) +		btc_ops->btc_get_ampdu_cfg(rtlpriv, &reject_agg, +					   &ctrl_agg_size, &agg_size); + +	RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, +		 "Set RX AMPDU: coex - reject=%d, ctrl_agg_size=%d, size=%d", +		 reject_agg, ctrl_agg_size, agg_size); + +	rtlpriv->hw->max_rx_aggregation_subframes = +		(ctrl_agg_size ? agg_size : IEEE80211_MAX_AMPDU_BUF); +} +EXPORT_SYMBOL(rtl_rx_ampdu_apply); +  /*********************************************************   *   * wq & timer callback functions @@ -1564,6 +1709,100 @@ void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb)  }  EXPORT_SYMBOL_GPL(rtl_beacon_statistic); +static void rtl_free_entries_from_scan_list(struct ieee80211_hw *hw) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	struct rtl_bssid_entry *entry, *next; + +	list_for_each_entry_safe(entry, next, &rtlpriv->scan_list.list, list) { +		list_del(&entry->list); +		kfree(entry); +		rtlpriv->scan_list.num--; +	} +} + +void rtl_scan_list_expire(struct ieee80211_hw *hw) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	struct rtl_bssid_entry *entry, *next; +	unsigned long flags; + +	spin_lock_irqsave(&rtlpriv->locks.scan_list_lock, flags); + +	list_for_each_entry_safe(entry, next, &rtlpriv->scan_list.list, list) { +		/* 180 seconds */ +		if (jiffies_to_msecs(jiffies - entry->age) < 180000) +			continue; + +		list_del(&entry->list); +		kfree(entry); +		rtlpriv->scan_list.num--; + +		RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, +			 "BSSID=%pM is expire in scan list (total=%d)\n", +			 entry->bssid, rtlpriv->scan_list.num); +	} + +	spin_unlock_irqrestore(&rtlpriv->locks.scan_list_lock, flags); + +	rtlpriv->btcoexist.btc_info.ap_num = rtlpriv->scan_list.num; +} + +void rtl_collect_scan_list(struct ieee80211_hw *hw, struct sk_buff *skb) +{ +	struct rtl_priv *rtlpriv = rtl_priv(hw); +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); +	unsigned long flags; + +	struct rtl_bssid_entry *entry; +	bool entry_found = false; + +	/* check if it is scanning */ +	if (!mac->act_scanning) +		return; + +	/* check if this really is a beacon */ +	if (!ieee80211_is_beacon(hdr->frame_control) && +	    !ieee80211_is_probe_resp(hdr->frame_control)) +		return; + +	spin_lock_irqsave(&rtlpriv->locks.scan_list_lock, flags); + +	list_for_each_entry(entry, &rtlpriv->scan_list.list, list) { +		if (memcmp(entry->bssid, hdr->addr3, ETH_ALEN) == 0) { +			list_del_init(&entry->list); +			entry_found = true; +			RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, +				 "Update BSSID=%pM to scan list (total=%d)\n", +				 hdr->addr3, rtlpriv->scan_list.num); +			break; +		} +	} + +	if (!entry_found) { +		entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + +		if (!entry) +			goto label_err; + +		memcpy(entry->bssid, hdr->addr3, ETH_ALEN); +		rtlpriv->scan_list.num++; + +		RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, +			 "Add BSSID=%pM to scan list (total=%d)\n", +			 hdr->addr3, rtlpriv->scan_list.num); +	} + +	entry->age = jiffies; + +	list_add_tail(&entry->list, &rtlpriv->scan_list.list); + +label_err: +	spin_unlock_irqrestore(&rtlpriv->locks.scan_list_lock, flags); +} +EXPORT_SYMBOL(rtl_collect_scan_list); +  void rtl_watchdog_wq_callback(void *data)  {  	struct rtl_works *rtlworks = container_of_dwork_rtl(data, @@ -1662,12 +1901,20 @@ void rtl_watchdog_wq_callback(void *data)  									false;  		} +		/* PS is controlled by coex. */ +		if (rtlpriv->cfg->ops->get_btc_status() && +		    rtlpriv->btcoexist.btc_ops->btc_is_bt_ctrl_lps(rtlpriv)) +			goto label_lps_done; +  		if (((rtlpriv->link_info.num_rx_inperiod +  		      rtlpriv->link_info.num_tx_inperiod) > 8) ||  		    (rtlpriv->link_info.num_rx_inperiod > 2))  			rtl_lps_leave(hw);  		else  			rtl_lps_enter(hw); + +label_lps_done: +		;  	}  	rtlpriv->link_info.num_rx_inperiod = 0; @@ -1713,6 +1960,9 @@ void rtl_watchdog_wq_callback(void *data)  		rtlpriv->btcoexist.btc_ops->btc_periodical(rtlpriv);  	rtlpriv->link_info.bcn_rx_inperiod = 0; + +	/* <6> scan list */ +	rtl_scan_list_expire(hw);  }  void rtl_watch_dog_timer_callback(unsigned long data) @@ -1875,8 +2125,7 @@ static struct sk_buff *rtl_make_smps_action(struct ieee80211_hw *hw,  		return NULL;  	skb_reserve(skb, hw->extra_tx_headroom); -	action_frame = (void *)skb_put(skb, 27); -	memset(action_frame, 0, 27); +	action_frame = skb_put_zero(skb, 27);  	memcpy(action_frame->da, da, ETH_ALEN);  	memcpy(action_frame->sa, rtlefuse->dev_addr, ETH_ALEN);  	memcpy(action_frame->bssid, bssid, ETH_ALEN); @@ -2005,8 +2254,7 @@ struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw,  		return NULL;  	skb_reserve(skb, hw->extra_tx_headroom); -	action_frame = (void *)skb_put(skb, 34); -	memset(action_frame, 0, 34); +	action_frame = skb_put_zero(skb, 34);  	memcpy(action_frame->sa, sa, ETH_ALEN);  	memcpy(action_frame->da, rtlefuse->dev_addr, ETH_ALEN);  	memcpy(action_frame->bssid, bssid, ETH_ALEN);  |