diff options
Diffstat (limited to 'drivers/net/wireless/realtek/rtlwifi/base.c')
-rw-r--r-- | drivers/net/wireless/realtek/rtlwifi/base.c | 282 |
1 files changed, 266 insertions, 16 deletions
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 710e5b447cff..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) |