INIT_LIST_HEAD(&rtlpriv->entry_list);
INIT_LIST_HEAD(&rtlpriv->c2hcmd_list);
INIT_LIST_HEAD(&rtlpriv->scan_list.list);
+ skb_queue_head_init(&rtlpriv->tx_report.queue);
rtlmac->link_state = MAC80211_NOLINK;
EXPORT_SYMBOL_GPL(rtl_init_core);
static void rtl_free_entries_from_scan_list(struct ieee80211_hw *hw);
+static void rtl_free_entries_from_ack_queue(struct ieee80211_hw *hw,
+ bool timeout);
void rtl_deinit_core(struct ieee80211_hw *hw)
{
rtl_c2hcmd_launcher(hw, 0);
rtl_free_entries_from_scan_list(hw);
+ rtl_free_entries_from_ack_queue(hw, false);
}
EXPORT_SYMBOL_GPL(rtl_deinit_core);
}
EXPORT_SYMBOL_GPL(rtl_is_special_data);
+void rtl_tx_ackqueue(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_tx_report *tx_report = &rtlpriv->tx_report;
+
+ __skb_queue_tail(&tx_report->queue, skb);
+}
+EXPORT_SYMBOL_GPL(rtl_tx_ackqueue);
+
+static void rtl_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
+ bool ack)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct ieee80211_tx_info *info;
+
+ info = IEEE80211_SKB_CB(skb);
+ ieee80211_tx_info_clear_status(info);
+ if (ack) {
+ RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_LOUD,
+ "tx report: ack\n");
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ } else {
+ RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_LOUD,
+ "tx report: not ack\n");
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
+ }
+ ieee80211_tx_status_irqsafe(hw, skb);
+}
+
bool rtl_is_tx_report_skb(struct ieee80211_hw *hw, struct sk_buff *skb)
{
u16 ether_type;
const u8 *ether_type_ptr;
+ __le16 fc = rtl_get_fc(skb);
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)
+ if (ether_type == ETH_P_PAE || ieee80211_is_nullfunc(fc))
return true;
return false;
}
-static u16 rtl_get_tx_report_sn(struct ieee80211_hw *hw)
+static u16 rtl_get_tx_report_sn(struct ieee80211_hw *hw,
+ struct rtlwifi_tx_info *tx_info)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_tx_report *tx_report = &rtlpriv->tx_report;
tx_report->last_sent_sn = sn;
tx_report->last_sent_time = jiffies;
-
+ tx_info->sn = sn;
+ tx_info->send_time = tx_report->last_sent_time;
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)
+void rtl_set_tx_report(struct rtl_tcb_desc *ptcb_desc, u8 *pdesc,
+ struct ieee80211_hw *hw, struct rtlwifi_tx_info *tx_info)
{
if (ptcb_desc->use_spe_rpt) {
- u16 sn = rtl_get_tx_report_sn(hw);
+ u16 sn = rtl_get_tx_report_sn(hw, tx_info);
SET_TX_DESC_SPE_RPT(pdesc, 1);
SET_TX_DESC_SW_DEFINE(pdesc, sn);
}
}
-EXPORT_SYMBOL_GPL(rtl_get_tx_report);
+EXPORT_SYMBOL_GPL(rtl_set_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;
+ struct rtlwifi_tx_info *tx_info;
+ struct sk_buff_head *queue = &tx_report->queue;
+ struct sk_buff *skb;
u16 sn;
u8 st, retry;
tx_report->last_recv_sn = sn;
+ skb_queue_walk(queue, skb) {
+ tx_info = rtl_tx_skb_cb_info(skb);
+ if (tx_info->sn == sn) {
+ skb_unlink(skb, queue);
+ rtl_tx_status(hw, skb, st == 0);
+ break;
+ }
+ }
RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_DMESG,
"Recv TX-Report st=0x%02X sn=0x%X retry=0x%X\n",
st, sn, retry);
}
}
+static void rtl_free_entries_from_ack_queue(struct ieee80211_hw *hw,
+ bool chk_timeout)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_tx_report *tx_report = &rtlpriv->tx_report;
+ struct sk_buff_head *queue = &tx_report->queue;
+ struct sk_buff *skb, *tmp;
+ struct rtlwifi_tx_info *tx_info;
+
+ skb_queue_walk_safe(queue, skb, tmp) {
+ tx_info = rtl_tx_skb_cb_info(skb);
+ if (chk_timeout &&
+ time_after(tx_info->send_time + HZ, jiffies))
+ continue;
+ skb_unlink(skb, queue);
+ rtl_tx_status(hw, skb, false);
+ }
+}
+
void rtl_scan_list_expire(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
/* <6> scan list */
rtl_scan_list_expire(hw);
+
+ /* <7> check ack queue */
+ rtl_free_entries_from_ack_queue(hw, true);
}
void rtl_watch_dog_timer_callback(struct timer_list *t)