diff options
Diffstat (limited to 'drivers/net/wireless/rtlwifi/base.c')
| -rw-r--r-- | drivers/net/wireless/rtlwifi/base.c | 613 |
1 files changed, 471 insertions, 142 deletions
diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 8d6eb0f56c0..93bb384eb00 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2009-2010 Realtek Corporation. + * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -27,10 +27,6 @@ * *****************************************************************************/ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/ip.h> -#include <linux/module.h> #include "wifi.h" #include "rc.h" #include "base.h" @@ -39,11 +35,15 @@ #include "ps.h" #include "regd.h" +#include <linux/ip.h> +#include <linux/module.h> +#include <linux/udp.h> + /* - *NOTICE!!!: This file will be very big, we hsould - *keep it clear under follwing roles: + *NOTICE!!!: This file will be very big, we should + *keep it clear under following roles: * - *This file include follwing part, so, if you add new + *This file include following parts, so, if you add new *functions into this file, please check which part it *should includes. or check if you should add new part *for this file: @@ -55,7 +55,8 @@ *5) frame process functions *6) IOT functions *7) sysfs functions - *8) ... + *8) vif functions + *9) ... */ /********************************************************* @@ -168,10 +169,11 @@ static const u8 tid_to_ac[] = { 0, /* IEEE80211_AC_VO */ }; -u8 rtl_tid_to_ac(struct ieee80211_hw *hw, u8 tid) +u8 rtl_tid_to_ac(u8 tid) { return tid_to_ac[tid]; } +EXPORT_SYMBOL_GPL(rtl_tid_to_ac); static void _rtl_init_hw_ht_capab(struct ieee80211_hw *hw, struct ieee80211_sta_ht_cap *ht_cap) @@ -199,34 +201,46 @@ static void _rtl_init_hw_ht_capab(struct ieee80211_hw *hw, ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - /* - *hw->wiphy->bands[IEEE80211_BAND_2GHZ] + /*hw->wiphy->bands[IEEE80211_BAND_2GHZ] *base on ant_num *rx_mask: RX mask - *if rx_ant =1 rx_mask[0]=0xff;==>MCS0-MCS7 - *if rx_ant =2 rx_mask[1]=0xff;==>MCS8-MCS15 - *if rx_ant >=3 rx_mask[2]=0xff; - *if BW_40 rx_mask[4]=0x01; + *if rx_ant = 1 rx_mask[0]= 0xff;==>MCS0-MCS7 + *if rx_ant = 2 rx_mask[1]= 0xff;==>MCS8-MCS15 + *if rx_ant >= 3 rx_mask[2]= 0xff; + *if BW_40 rx_mask[4]= 0x01; *highest supported RX rate */ - if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_2T2R) { + if (rtlpriv->dm.supp_phymode_switch) { - RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, ("1T2R or 2T2R\n")); + RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, + "Support phy mode switch\n"); ht_cap->mcs.rx_mask[0] = 0xFF; ht_cap->mcs.rx_mask[1] = 0xFF; ht_cap->mcs.rx_mask[4] = 0x01; ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS15); - } else if (get_rf_type(rtlphy) == RF_1T1R) { - - RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, ("1T1R\n")); - - ht_cap->mcs.rx_mask[0] = 0xFF; - ht_cap->mcs.rx_mask[1] = 0x00; - ht_cap->mcs.rx_mask[4] = 0x01; - - ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS7); + } else { + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_2T2R) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "1T2R or 2T2R\n"); + ht_cap->mcs.rx_mask[0] = 0xFF; + ht_cap->mcs.rx_mask[1] = 0xFF; + ht_cap->mcs.rx_mask[4] = 0x01; + + ht_cap->mcs.rx_highest = + cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS15); + } else if (get_rf_type(rtlphy) == RF_1T1R) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "1T1R\n"); + + ht_cap->mcs.rx_mask[0] = 0xFF; + ht_cap->mcs.rx_mask[1] = 0x00; + ht_cap->mcs.rx_mask[4] = 0x01; + + ht_cap->mcs.rx_highest = + cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS7); + } } } @@ -302,18 +316,18 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) /* <4> set mac->sband to wiphy->sband */ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; } else { - RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, - ("Err BAND %d\n", - rtlhal->current_bandtype)); + RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Err BAND %d\n", + rtlhal->current_bandtype); } } /* <5> set hw caps */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_CONNECTION_MONITOR | /* IEEE80211_HW_SUPPORTS_CQM_RSSI | */ + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_REPORTS_TX_ACK_STATUS | 0; /* swlps or hwlps has been set in diff chip in init_sw_vars */ @@ -326,8 +340,12 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->rts_threshold = 2347; hw->queues = AC_MAX; @@ -335,7 +353,6 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) /* TODO: Correct this value for our hw */ /* TODO: define these hard code value */ - hw->channel_change_time = 100; hw->max_listen_interval = 10; hw->max_rate_tries = 4; /* hw->max_rates = 1; */ @@ -357,13 +374,14 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); /* <1> timer */ - init_timer(&rtlpriv->works.watchdog_timer); setup_timer(&rtlpriv->works.watchdog_timer, rtl_watch_dog_timer_callback, (unsigned long)hw); + setup_timer(&rtlpriv->works.dualmac_easyconcurrent_retrytimer, + rtl_easy_concurrent_retrytimer_callback, (unsigned long)hw); /* <2> work queue */ rtlpriv->works.hw = hw; - rtlpriv->works.rtl_wq = alloc_workqueue(rtlpriv->cfg->name, 0, 0); + rtlpriv->works.rtl_wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name); INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq, (void *)rtl_watchdog_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq, @@ -372,6 +390,8 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw) (void *)rtl_swlps_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ps_rfon_wq, (void *)rtl_swlps_rfon_wq_callback); + INIT_DELAYED_WORK(&rtlpriv->works.fwevt_wq, + (void *)rtl_fwevt_wq_callback); } @@ -385,7 +405,9 @@ void rtl_deinit_deferred_work(struct ieee80211_hw *hw) cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); cancel_delayed_work(&rtlpriv->works.ps_work); cancel_delayed_work(&rtlpriv->works.ps_rfon_wq); + cancel_delayed_work(&rtlpriv->works.fwevt_wq); } +EXPORT_SYMBOL_GPL(rtl_deinit_deferred_work); void rtl_init_rfkill(struct ieee80211_hw *hw) { @@ -413,11 +435,13 @@ void rtl_init_rfkill(struct ieee80211_hw *hw) wiphy_rfkill_start_polling(hw->wiphy); } +EXPORT_SYMBOL(rtl_init_rfkill); void rtl_deinit_rfkill(struct ieee80211_hw *hw) { wiphy_rfkill_stop_polling(hw->wiphy); } +EXPORT_SYMBOL_GPL(rtl_deinit_rfkill); int rtl_init_core(struct ieee80211_hw *hw) { @@ -436,14 +460,8 @@ int rtl_init_core(struct ieee80211_hw *hw) * mac80211 hw in _rtl_init_mac80211. */ if (rtl_regd_init(hw, rtl_reg_notifier)) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("REGD init failed\n")); + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "REGD init failed\n"); return 1; - } else { - /* CRDA regd hint must after init CRDA */ - if (regulatory_hint(hw->wiphy, rtlpriv->regd.alpha2)) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, - ("regulatory_hint fail\n")); - } } /* <4> locks */ @@ -451,23 +469,35 @@ int rtl_init_core(struct ieee80211_hw *hw) mutex_init(&rtlpriv->locks.ps_mutex); spin_lock_init(&rtlpriv->locks.ips_lock); spin_lock_init(&rtlpriv->locks.irq_th_lock); + spin_lock_init(&rtlpriv->locks.irq_pci_lock); + spin_lock_init(&rtlpriv->locks.tx_lock); spin_lock_init(&rtlpriv->locks.h2c_lock); spin_lock_init(&rtlpriv->locks.rf_ps_lock); spin_lock_init(&rtlpriv->locks.rf_lock); spin_lock_init(&rtlpriv->locks.waitq_lock); + spin_lock_init(&rtlpriv->locks.entry_list_lock); + spin_lock_init(&rtlpriv->locks.fw_ps_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); + spin_lock_init(&rtlpriv->locks.lps_lock); + + /* <5> init list */ + INIT_LIST_HEAD(&rtlpriv->entry_list); rtlmac->link_state = MAC80211_NOLINK; - /* <5> init deferred work */ + /* <6> init deferred work */ _rtl_init_deferred_work(hw); return 0; } +EXPORT_SYMBOL_GPL(rtl_init_core); void rtl_deinit_core(struct ieee80211_hw *hw) { } +EXPORT_SYMBOL_GPL(rtl_deinit_core); void rtl_init_rx_config(struct ieee80211_hw *hw) { @@ -476,6 +506,7 @@ void rtl_init_rx_config(struct ieee80211_hw *hw) rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *) (&mac->rx_conf)); } +EXPORT_SYMBOL_GPL(rtl_init_rx_config); /********************************************************* * @@ -525,8 +556,9 @@ static void _rtl_query_shortgi(struct ieee80211_hw *hw, if (mac->opmode == NL80211_IFTYPE_STATION) bw_40 = mac->bw_40; else if (mac->opmode == NL80211_IFTYPE_AP || - mac->opmode == NL80211_IFTYPE_ADHOC) - bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; + mac->opmode == NL80211_IFTYPE_ADHOC || + mac->opmode == NL80211_IFTYPE_MESH_POINT) + bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; if (bw_40 && sgi_40) tcb_desc->use_shortgi = true; @@ -580,23 +612,26 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw, if (!tcb_desc->disable_ratefallback || !tcb_desc->use_driver_rate) { if (mac->opmode == NL80211_IFTYPE_STATION) { tcb_desc->ratr_index = 0; - } else if (mac->opmode == NL80211_IFTYPE_ADHOC) { + } else if (mac->opmode == NL80211_IFTYPE_ADHOC || + mac->opmode == NL80211_IFTYPE_MESH_POINT) { if (tcb_desc->multicast || tcb_desc->broadcast) { tcb_desc->hw_rate = rtlpriv->cfg->maps[RTL_RC_CCK_RATE2M]; tcb_desc->use_driver_rate = 1; + tcb_desc->ratr_index = RATR_INX_WIRELESS_MC; } else { - /* TODO */ + tcb_desc->ratr_index = ratr_index; } - tcb_desc->ratr_index = ratr_index; } else if (mac->opmode == NL80211_IFTYPE_AP) { tcb_desc->ratr_index = ratr_index; } } if (rtlpriv->dm.useramask) { - /* TODO we will differentiate adhoc and station futrue */ - if (mac->opmode == NL80211_IFTYPE_STATION) { + tcb_desc->ratr_index = ratr_index; + /* TODO we will differentiate adhoc and station future */ + if (mac->opmode == NL80211_IFTYPE_STATION || + mac->opmode == NL80211_IFTYPE_MESH_POINT) { tcb_desc->mac_id = 0; if (mac->mode == WIRELESS_MODE_N_24G) @@ -610,7 +645,7 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw, else if (mac->mode & WIRELESS_MODE_A) tcb_desc->ratr_index = RATR_INX_WIRELESS_G; } else if (mac->opmode == NL80211_IFTYPE_AP || - mac->opmode == NL80211_IFTYPE_ADHOC) { + mac->opmode == NL80211_IFTYPE_ADHOC) { if (NULL != sta) { if (sta->aid > 0) tcb_desc->mac_id = sta->aid + 1; @@ -621,7 +656,6 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw, } } } - } static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw, @@ -635,9 +669,9 @@ static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw, if (!sta) return; if (mac->opmode == NL80211_IFTYPE_AP || - mac->opmode == NL80211_IFTYPE_ADHOC) { - if (!(sta->ht_cap.ht_supported) || - !(sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + mac->opmode == NL80211_IFTYPE_ADHOC || + mac->opmode == NL80211_IFTYPE_MESH_POINT) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_20) return; } else if (mac->opmode == NL80211_IFTYPE_STATION) { if (!mac->bw_40 || !(sta->ht_cap.ht_supported)) @@ -694,7 +728,7 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, int rate_idx; if (false == isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.channel->band) { + if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { switch (desc_rate) { case DESC92_RATE1M: rate_idx = 0; @@ -828,6 +862,31 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, } EXPORT_SYMBOL(rtlwifi_rate_mapping); +bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); + __le16 fc = rtl_get_fc(skb); + + if (rtlpriv->dm.supp_phymode_switch && + mac->link_state < MAC80211_LINKED && + (ieee80211_is_auth(fc) || ieee80211_is_probe_req(fc))) { + if (rtlpriv->cfg->ops->chk_switch_dmdp) + rtlpriv->cfg->ops->chk_switch_dmdp(hw); + } + if (ieee80211_is_auth(fc)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "MAC80211_LINKING\n"); + rtl_ips_nic_on(hw); + + mac->link_state = MAC80211_LINKING; + /* Dual mac */ + rtlpriv->phy.need_iqk = true; + } + + return true; +} +EXPORT_SYMBOL_GPL(rtl_tx_mgmt_proc); + void rtl_get_tcb_desc(struct ieee80211_hw *hw, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, @@ -840,7 +899,10 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw, __le16 fc = hdr->frame_control; txrate = ieee80211_get_tx_rate(hw, info); - tcb_desc->hw_rate = txrate->hw_value; + if (txrate) + tcb_desc->hw_rate = txrate->hw_value; + else + tcb_desc->hw_rate = 0; if (ieee80211_is_data(fc)) { /* @@ -900,13 +962,63 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw, } EXPORT_SYMBOL(rtl_get_tcb_desc); +static bool addbareq_rx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ieee80211_sta *sta = NULL; + struct ieee80211_hdr *hdr = rtl_get_hdr(skb); + struct rtl_sta_info *sta_entry = NULL; + struct ieee80211_mgmt *mgmt = (void *)skb->data; + u16 capab = 0, tid = 0; + struct rtl_tid_data *tid_data; + struct sk_buff *skb_delba = NULL; + struct ieee80211_rx_status rx_status = { 0 }; + + rcu_read_lock(); + sta = rtl_find_sta(hw, hdr->addr3); + if (sta == NULL) { + RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_EMERG, + "sta is NULL\n"); + rcu_read_unlock(); + return true; + } + + sta_entry = (struct rtl_sta_info *)sta->drv_priv; + if (!sta_entry) { + rcu_read_unlock(); + return true; + } + capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); + tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; + tid_data = &sta_entry->tids[tid]; + if (tid_data->agg.rx_agg_state == RTL_RX_AGG_START) { + skb_delba = rtl_make_del_ba(hw, hdr->addr2, hdr->addr3, tid); + if (skb_delba) { + rx_status.freq = hw->conf.chandef.chan->center_freq; + rx_status.band = hw->conf.chandef.chan->band; + rx_status.flag |= RX_FLAG_DECRYPTED; + rx_status.flag |= RX_FLAG_MACTIME_END; + rx_status.rate_idx = 0; + rx_status.signal = 50 + 10; + memcpy(IEEE80211_SKB_RXCB(skb_delba), &rx_status, + sizeof(rx_status)); + RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, + "fake del\n", skb_delba->data, + skb_delba->len); + ieee80211_rx_irqsafe(hw, skb_delba); + } + } + rcu_read_unlock(); + return false; +} + bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); struct rtl_priv *rtlpriv = rtl_priv(hw); __le16 fc = hdr->frame_control; - u8 *act = (u8 *) (((u8 *) skb->data + MAC80211_3ADDR_LEN)); + u8 *act = (u8 *)skb->data + MAC80211_3ADDR_LEN; u8 category; if (!ieee80211_is_action(fc)) @@ -922,17 +1034,22 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) return false; RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, - ("%s ACT_ADDBAREQ From :%pM\n", - is_tx ? "Tx" : "Rx", hdr->addr2)); + "%s ACT_ADDBAREQ From :%pM\n", + is_tx ? "Tx" : "Rx", hdr->addr2); + RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, "req\n", + skb->data, skb->len); + if (!is_tx) + if (addbareq_rx(hw, skb)) + return true; break; case ACT_ADDBARSP: RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, - ("%s ACT_ADDBARSP From :%pM\n", - is_tx ? "Tx" : "Rx", hdr->addr2)); + "%s ACT_ADDBARSP From :%pM\n", + is_tx ? "Tx" : "Rx", hdr->addr2); break; case ACT_DELBA: RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, - ("ACT_ADDBADEL From :%pM\n", hdr->addr2)); + "ACT_ADDBADEL From :%pM\n", hdr->addr2); break; } break; @@ -942,6 +1059,7 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) return true; } +EXPORT_SYMBOL_GPL(rtl_action_proc); /*should call before software enc*/ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) @@ -956,61 +1074,54 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) if (!ieee80211_is_data(fc)) return false; + ip = (const struct iphdr *)(skb->data + mac_hdr_len + + SNAP_SIZE + PROTOC_TYPE_SIZE); + ether_type = be16_to_cpup((__be16 *) + (skb->data + mac_hdr_len + SNAP_SIZE)); - ip = (struct iphdr *)((u8 *) skb->data + mac_hdr_len + - SNAP_SIZE + PROTOC_TYPE_SIZE); - ether_type = *(u16 *) ((u8 *) skb->data + mac_hdr_len + SNAP_SIZE); - /* ether_type = ntohs(ether_type); */ - - if (ETH_P_IP == ether_type) { - if (IPPROTO_UDP == ip->protocol) { - struct udphdr *udp = (struct udphdr *)((u8 *) ip + - (ip->ihl << 2)); - if (((((u8 *) udp)[1] == 68) && - (((u8 *) udp)[3] == 67)) || - ((((u8 *) udp)[1] == 67) && - (((u8 *) udp)[3] == 68))) { - /* - * 68 : UDP BOOTP client - * 67 : UDP BOOTP server - */ - RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), - DBG_DMESG, ("dhcp %s !!\n", - (is_tx) ? "Tx" : "Rx")); - - if (is_tx) { - rtl_lps_leave(hw); - ppsc->last_delaylps_stamp_jiffies = - jiffies; - } - - return true; - } - } - } else if (ETH_P_ARP == ether_type) { - if (is_tx) { - rtl_lps_leave(hw); - ppsc->last_delaylps_stamp_jiffies = jiffies; - } + switch (ether_type) { + case ETH_P_IP: { + struct udphdr *udp; + u16 src; + u16 dst; - return true; - } else if (ETH_P_PAE == ether_type) { - RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, - ("802.1X %s EAPOL pkt!!\n", (is_tx) ? "Tx" : "Rx")); + if (ip->protocol != IPPROTO_UDP) + return false; + udp = (struct udphdr *)((u8 *)ip + (ip->ihl << 2)); + src = be16_to_cpu(udp->source); + dst = be16_to_cpu(udp->dest); - if (is_tx) { - rtl_lps_leave(hw); - ppsc->last_delaylps_stamp_jiffies = jiffies; - } + /* If this case involves port 68 (UDP BOOTP client) connecting + * with port 67 (UDP BOOTP server), then return true so that + * the lowest speed is used. + */ + if (!((src == 68 && dst == 67) || (src == 67 && dst == 68))) + return false; - return true; - } else if (ETH_P_IPV6 == ether_type) { - /* IPv6 */ - return true; + RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, + "dhcp %s !!\n", is_tx ? "Tx" : "Rx"); + break; } - - return false; + case ETH_P_ARP: + break; + case ETH_P_PAE: + RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, + "802.1X %s EAPOL pkt!!\n", is_tx ? "Tx" : "Rx"); + break; + case ETH_P_IPV6: + /* TODO: Is this right? */ + return false; + default: + return false; + } + if (is_tx) { + rtlpriv->enter_ps = false; + schedule_work(&rtlpriv->works.lps_change_work); + ppsc->last_delaylps_stamp_jiffies = jiffies; + } + return true; } +EXPORT_SYMBOL_GPL(rtl_is_special_data); /********************************************************* * @@ -1036,9 +1147,8 @@ int rtl_tx_agg_start(struct ieee80211_hw *hw, return -ENXIO; tid_data = &sta_entry->tids[tid]; - RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, - ("on ra = %pM tid = %d seq:%d\n", sta->addr, tid, - tid_data->seq_number)); + RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "on ra = %pM tid = %d seq:%d\n", + sta->addr, tid, tid_data->seq_number); *ssn = tid_data->seq_number; tid_data->agg.agg_state = RTL_AGG_START; @@ -1059,12 +1169,12 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw, return -EINVAL; if (!sta->addr) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("ra = NULL\n")); + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ra = NULL\n"); return -EINVAL; } - RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, - ("on ra = %pM tid = %d\n", sta->addr, tid)); + RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "on ra = %pM tid = %d\n", + sta->addr, tid); if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; @@ -1077,6 +1187,58 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw, return 0; } +int rtl_rx_agg_start(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u16 tid) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_tid_data *tid_data; + struct rtl_sta_info *sta_entry = NULL; + + if (sta == NULL) + return -EINVAL; + + if (unlikely(tid >= MAX_TID_COUNT)) + return -EINVAL; + + sta_entry = (struct rtl_sta_info *)sta->drv_priv; + if (!sta_entry) + return -ENXIO; + tid_data = &sta_entry->tids[tid]; + + RT_TRACE(rtlpriv, COMP_RECV, DBG_DMESG, + "on ra = %pM tid = %d seq:%d\n", sta->addr, tid, + tid_data->seq_number); + + tid_data->agg.rx_agg_state = RTL_RX_AGG_START; + return 0; +} + +int rtl_rx_agg_stop(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u16 tid) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_sta_info *sta_entry = NULL; + + if (sta == NULL) + return -EINVAL; + + if (!sta->addr) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ra = NULL\n"); + return -EINVAL; + } + + RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, + "on ra = %pM tid = %d\n", sta->addr, tid); + + if (unlikely(tid >= MAX_TID_COUNT)) + return -EINVAL; + + sta_entry = (struct rtl_sta_info *)sta->drv_priv; + sta_entry->tids[tid].agg.rx_agg_state = RTL_RX_AGG_STOP; + + return 0; +} + int rtl_tx_agg_oper(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid) { @@ -1087,12 +1249,12 @@ int rtl_tx_agg_oper(struct ieee80211_hw *hw, return -EINVAL; if (!sta->addr) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("ra = NULL\n")); + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ra = NULL\n"); return -EINVAL; } - RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, - ("on ra = %pM tid = %d\n", sta->addr, tid)); + RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "on ra = %pM tid = %d\n", + sta->addr, tid); if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; @@ -1108,6 +1270,35 @@ int rtl_tx_agg_oper(struct ieee80211_hw *hw, * wq & timer callback functions * *********************************************************/ +/* this function is used for roaming */ +void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION) + return; + + if (rtlpriv->mac80211.link_state < MAC80211_LINKED) + return; + + /* check if this really is a beacon */ + if (!ieee80211_is_beacon(hdr->frame_control) && + !ieee80211_is_probe_resp(hdr->frame_control)) + return; + + /* min. beacon length + FCS_LEN */ + if (skb->len <= 40 + FCS_LEN) + return; + + /* and only beacons from the associated BSSID, please */ + if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) + return; + + rtlpriv->link_info.bcn_rx_inperiod++; +} +EXPORT_SYMBOL_GPL(rtl_beacon_statistic); + void rtl_watchdog_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, @@ -1118,6 +1309,8 @@ void rtl_watchdog_wq_callback(void *data) struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); bool busytraffic = false; + bool tx_busy_traffic = false; + bool rx_busy_traffic = false; bool higher_busytraffic = false; bool higher_busyrxtraffic = false; u8 idx, tid; @@ -1127,7 +1320,6 @@ void rtl_watchdog_wq_callback(void *data) u32 aver_tx_cnt_inperiod = 0; u32 aver_tidtx_inperiod[MAX_TID_COUNT] = {0}; u32 tidtx_inp4eriod[MAX_TID_COUNT] = {0}; - bool enter_ps = false; if (is_hal_stop(rtlhal)) return; @@ -1167,8 +1359,13 @@ void rtl_watchdog_wq_callback(void *data) aver_tx_cnt_inperiod = tx_cnt_inp4eriod / 4; /* (2) check traffic busy */ - if (aver_rx_cnt_inperiod > 100 || aver_tx_cnt_inperiod > 100) + if (aver_rx_cnt_inperiod > 100 || aver_tx_cnt_inperiod > 100) { busytraffic = true; + if (aver_rx_cnt_inperiod > aver_tx_cnt_inperiod) + rx_busy_traffic = true; + else + tx_busy_traffic = false; + } /* Higher Tx/Rx data. */ if (aver_rx_cnt_inperiod > 4000 || @@ -1204,15 +1401,12 @@ void rtl_watchdog_wq_callback(void *data) if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || (rtlpriv->link_info.num_rx_inperiod > 2)) - enter_ps = false; + rtlpriv->enter_ps = true; else - enter_ps = true; + rtlpriv->enter_ps = false; /* LeisurePS only work in infra mode. */ - if (enter_ps) - rtl_lps_enter(hw); - else - rtl_lps_leave(hw); + schedule_work(&rtlpriv->works.lps_change_work); } rtlpriv->link_info.num_rx_inperiod = 0; @@ -1222,10 +1416,38 @@ void rtl_watchdog_wq_callback(void *data) rtlpriv->link_info.busytraffic = busytraffic; rtlpriv->link_info.higher_busytraffic = higher_busytraffic; + rtlpriv->link_info.rx_busy_traffic = rx_busy_traffic; + rtlpriv->link_info.tx_busy_traffic = tx_busy_traffic; rtlpriv->link_info.higher_busyrxtraffic = higher_busyrxtraffic; /* <3> DM */ rtlpriv->cfg->ops->dm_watchdog(hw); + + /* <4> roaming */ + if (mac->link_state == MAC80211_LINKED && + mac->opmode == NL80211_IFTYPE_STATION) { + if ((rtlpriv->link_info.bcn_rx_inperiod + + rtlpriv->link_info.num_rx_inperiod) == 0) { + rtlpriv->link_info.roam_times++; + RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG, + "AP off for %d s\n", + (rtlpriv->link_info.roam_times * 2)); + + /* if we can't recv beacon for 6s, we should + * reconnect this AP + */ + if ((rtlpriv->link_info.roam_times >= 3) && + !is_zero_ether_addr(rtlpriv->mac80211.bssid)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "AP off, try to reconnect now\n"); + rtlpriv->link_info.roam_times = 0; + ieee80211_connection_loss(rtlpriv->mac80211.vif); + } + } else { + rtlpriv->link_info.roam_times = 0; + } + } + rtlpriv->link_info.bcn_rx_inperiod = 0; } void rtl_watch_dog_timer_callback(unsigned long data) @@ -1240,6 +1462,28 @@ void rtl_watch_dog_timer_callback(unsigned long data) jiffies + MSECS(RTL_WATCH_DOG_TIME)); } +void rtl_fwevt_wq_callback(void *data) +{ + struct rtl_works *rtlworks = + container_of_dwork_rtl(data, struct rtl_works, fwevt_wq); + struct ieee80211_hw *hw = rtlworks->hw; + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->cfg->ops->c2h_command_handle(hw); +} + +void rtl_easy_concurrent_retrytimer_callback(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_priv *buddy_priv = rtlpriv->buddy_priv; + + if (buddy_priv == NULL) + return; + + rtlpriv->cfg->ops->dualmac_easy_concurrent(hw); +} + /********************************************************* * * frame process functions @@ -1310,14 +1554,16 @@ static struct sk_buff *rtl_make_smps_action(struct ieee80211_hw *hw, } int rtl_send_smps_action(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, u8 *da, u8 *bssid, + struct ieee80211_sta *sta, enum ieee80211_smps_mode smps) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); - struct sk_buff *skb = rtl_make_smps_action(hw, smps, da, bssid); + struct sk_buff *skb = NULL; struct rtl_tcb_desc tcb_desc; + u8 bssid[ETH_ALEN] = {0}; + memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); if (rtlpriv->mac80211.act_scanning) @@ -1332,22 +1578,96 @@ int rtl_send_smps_action(struct ieee80211_hw *hw, if (!test_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status)) goto err_free; + if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP) + memcpy(bssid, rtlpriv->efuse.dev_addr, ETH_ALEN); + else + memcpy(bssid, rtlpriv->mac80211.bssid, ETH_ALEN); + + skb = rtl_make_smps_action(hw, smps, sta->addr, bssid); /* this is a type = mgmt * stype = action frame */ if (skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rtl_sta_info *sta_entry = (struct rtl_sta_info *) sta->drv_priv; sta_entry->mimo_ps = smps; - rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0); info->control.rates[0].idx = 0; - info->control.sta = sta; - info->band = hw->conf.channel->band; - rtlpriv->intf_ops->adapter_tx(hw, skb, &tcb_desc); + info->band = hw->conf.chandef.chan->band; + rtlpriv->intf_ops->adapter_tx(hw, sta, skb, &tcb_desc); } + return 1; + err_free: return 0; } +EXPORT_SYMBOL(rtl_send_smps_action); + +void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + enum io_type iotype; + + if (!is_hal_stop(rtlhal)) { + switch (operation) { + case SCAN_OPT_BACKUP: + iotype = IO_CMD_PAUSE_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + case SCAN_OPT_RESTORE: + iotype = IO_CMD_RESUME_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Unknown Scan Backup operation.\n"); + break; + } + } +} +EXPORT_SYMBOL(rtl_phy_scan_operation_backup); + +/* There seem to be issues in mac80211 regarding when del ba frames can be + * received. As a work around, we make a fake del_ba if we receive a ba_req; + * however, rx_agg was opened to let mac80211 release some ba related + * resources. This del_ba is for tx only. + */ +struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw, + u8 *sa, u8 *bssid, u16 tid) +{ + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct sk_buff *skb; + struct ieee80211_mgmt *action_frame; + u16 params; + + /* 27 = header + category + action + smps mode */ + skb = dev_alloc_skb(34 + hw->extra_tx_headroom); + if (!skb) + return NULL; + + skb_reserve(skb, hw->extra_tx_headroom); + action_frame = (void *)skb_put(skb, 34); + memset(action_frame, 0, 34); + memcpy(action_frame->sa, sa, ETH_ALEN); + memcpy(action_frame->da, rtlefuse->dev_addr, ETH_ALEN); + memcpy(action_frame->bssid, bssid, ETH_ALEN); + action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + action_frame->u.action.category = WLAN_CATEGORY_BACK; + action_frame->u.action.u.delba.action_code = WLAN_ACTION_DELBA; + params = (u16)(1 << 11); /* bit 11 initiator */ + params |= (u16)(tid << 12); /* bit 15:12 TID number */ + + action_frame->u.action.u.delba.params = cpu_to_le16(params); + action_frame->u.action.u.delba.reason_code = + cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); + + return skb; +} /********************************************************* * @@ -1460,7 +1780,7 @@ void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len) return; /* and only beacons from the associated BSSID, please */ - if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid)) + if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) return; if (rtl_find_221_ie(hw, data, len)) @@ -1474,33 +1794,34 @@ void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len) (memcmp(mac->bssid, ap5_6, 3) == 0) || vendor == PEER_ATH) { vendor = PEER_ATH; - RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, ("=>ath find\n")); + RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>ath find\n"); } else if ((memcmp(mac->bssid, ap4_4, 3) == 0) || (memcmp(mac->bssid, ap4_5, 3) == 0) || (memcmp(mac->bssid, ap4_1, 3) == 0) || (memcmp(mac->bssid, ap4_2, 3) == 0) || (memcmp(mac->bssid, ap4_3, 3) == 0) || vendor == PEER_RAL) { - RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, ("=>ral findn\n")); + RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>ral find\n"); vendor = PEER_RAL; } else if (memcmp(mac->bssid, ap6_1, 3) == 0 || vendor == PEER_CISCO) { vendor = PEER_CISCO; - RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, ("=>cisco find\n")); + RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>cisco find\n"); } else if ((memcmp(mac->bssid, ap3_1, 3) == 0) || (memcmp(mac->bssid, ap3_2, 3) == 0) || (memcmp(mac->bssid, ap3_3, 3) == 0) || vendor == PEER_BROAD) { - RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, ("=>broad find\n")); + RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>broad find\n"); vendor = PEER_BROAD; } else if (memcmp(mac->bssid, ap7_1, 3) == 0 || vendor == PEER_MARV) { vendor = PEER_MARV; - RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, ("=>marv find\n")); + RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>marv find\n"); } mac->vendor = vendor; } +EXPORT_SYMBOL_GPL(rtl_recognize_peer); /********************************************************* * @@ -1525,7 +1846,7 @@ static ssize_t rtl_store_debug_level(struct device *d, unsigned long val; int ret; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret) { printk(KERN_DEBUG "%s is not in hex or decimal form.\n", buf); } else { @@ -1557,6 +1878,7 @@ struct attribute_group rtl_attribute_group = { .name = "rtlsysfs", .attrs = rtl_sysfs_entries, }; +EXPORT_SYMBOL_GPL(rtl_attribute_group); MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>"); MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); @@ -1564,11 +1886,18 @@ MODULE_AUTHOR("Larry Finger <Larry.FInger@lwfinger.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core"); +struct rtl_global_var rtl_global_var = {}; +EXPORT_SYMBOL_GPL(rtl_global_var); + static int __init rtl_core_module_init(void) { if (rtl_rate_control_register()) pr_err("Unable to register rtl_rc, use default RC !!\n"); + /* init some global vars */ + INIT_LIST_HEAD(&rtl_global_var.glb_priv_list); + spin_lock_init(&rtl_global_var.glb_list_lock); + return 0; } |
