18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2004-2011 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
68c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
78c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "core.h"
218c2ecf20Sopenharmony_ci#include "debug.h"
228c2ecf20Sopenharmony_ci#include "htc-ops.h"
238c2ecf20Sopenharmony_ci#include "trace.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * tid - tid_mux0..tid_mux3
278c2ecf20Sopenharmony_ci * aid - tid_mux4..tid_mux7
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#define ATH6KL_TID_MASK 0xf
308c2ecf20Sopenharmony_ci#define ATH6KL_AID_SHIFT 4
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic inline u8 ath6kl_get_tid(u8 tid_mux)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return tid_mux & ATH6KL_TID_MASK;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic inline u8 ath6kl_get_aid(u8 tid_mux)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	return tid_mux >> ATH6KL_AID_SHIFT;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev,
438c2ecf20Sopenharmony_ci			       u32 *map_no)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct ath6kl *ar = ath6kl_priv(dev);
468c2ecf20Sopenharmony_ci	struct ethhdr *eth_hdr;
478c2ecf20Sopenharmony_ci	u32 i, ep_map = -1;
488c2ecf20Sopenharmony_ci	u8 *datap;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	*map_no = 0;
518c2ecf20Sopenharmony_ci	datap = skb->data;
528c2ecf20Sopenharmony_ci	eth_hdr = (struct ethhdr *) (datap + sizeof(struct wmi_data_hdr));
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(eth_hdr->h_dest))
558c2ecf20Sopenharmony_ci		return ENDPOINT_2;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	for (i = 0; i < ar->node_num; i++) {
588c2ecf20Sopenharmony_ci		if (memcmp(eth_hdr->h_dest, ar->node_map[i].mac_addr,
598c2ecf20Sopenharmony_ci			   ETH_ALEN) == 0) {
608c2ecf20Sopenharmony_ci			*map_no = i + 1;
618c2ecf20Sopenharmony_ci			ar->node_map[i].tx_pend++;
628c2ecf20Sopenharmony_ci			return ar->node_map[i].ep_id;
638c2ecf20Sopenharmony_ci		}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		if ((ep_map == -1) && !ar->node_map[i].tx_pend)
668c2ecf20Sopenharmony_ci			ep_map = i;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (ep_map == -1) {
708c2ecf20Sopenharmony_ci		ep_map = ar->node_num;
718c2ecf20Sopenharmony_ci		ar->node_num++;
728c2ecf20Sopenharmony_ci		if (ar->node_num > MAX_NODE_NUM)
738c2ecf20Sopenharmony_ci			return ENDPOINT_UNUSED;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	memcpy(ar->node_map[ep_map].mac_addr, eth_hdr->h_dest, ETH_ALEN);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	for (i = ENDPOINT_2; i <= ENDPOINT_5; i++) {
798c2ecf20Sopenharmony_ci		if (!ar->tx_pending[i]) {
808c2ecf20Sopenharmony_ci			ar->node_map[ep_map].ep_id = i;
818c2ecf20Sopenharmony_ci			break;
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		/*
858c2ecf20Sopenharmony_ci		 * No free endpoint is available, start redistribution on
868c2ecf20Sopenharmony_ci		 * the inuse endpoints.
878c2ecf20Sopenharmony_ci		 */
888c2ecf20Sopenharmony_ci		if (i == ENDPOINT_5) {
898c2ecf20Sopenharmony_ci			ar->node_map[ep_map].ep_id = ar->next_ep_id;
908c2ecf20Sopenharmony_ci			ar->next_ep_id++;
918c2ecf20Sopenharmony_ci			if (ar->next_ep_id > ENDPOINT_5)
928c2ecf20Sopenharmony_ci				ar->next_ep_id = ENDPOINT_2;
938c2ecf20Sopenharmony_ci		}
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	*map_no = ep_map + 1;
978c2ecf20Sopenharmony_ci	ar->node_map[ep_map].tx_pend++;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return ar->node_map[ep_map].ep_id;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic bool ath6kl_process_uapsdq(struct ath6kl_sta *conn,
1038c2ecf20Sopenharmony_ci				struct ath6kl_vif *vif,
1048c2ecf20Sopenharmony_ci				struct sk_buff *skb,
1058c2ecf20Sopenharmony_ci				u32 *flags)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
1088c2ecf20Sopenharmony_ci	bool is_apsdq_empty = false;
1098c2ecf20Sopenharmony_ci	struct ethhdr *datap = (struct ethhdr *) skb->data;
1108c2ecf20Sopenharmony_ci	u8 up = 0, traffic_class, *ip_hdr;
1118c2ecf20Sopenharmony_ci	u16 ether_type;
1128c2ecf20Sopenharmony_ci	struct ath6kl_llc_snap_hdr *llc_hdr;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (conn->sta_flags & STA_PS_APSD_TRIGGER) {
1158c2ecf20Sopenharmony_ci		/*
1168c2ecf20Sopenharmony_ci		 * This tx is because of a uAPSD trigger, determine
1178c2ecf20Sopenharmony_ci		 * more and EOSP bit. Set EOSP if queue is empty
1188c2ecf20Sopenharmony_ci		 * or sufficient frames are delivered for this trigger.
1198c2ecf20Sopenharmony_ci		 */
1208c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->psq_lock);
1218c2ecf20Sopenharmony_ci		if (!skb_queue_empty(&conn->apsdq))
1228c2ecf20Sopenharmony_ci			*flags |= WMI_DATA_HDR_FLAGS_MORE;
1238c2ecf20Sopenharmony_ci		else if (conn->sta_flags & STA_PS_APSD_EOSP)
1248c2ecf20Sopenharmony_ci			*flags |= WMI_DATA_HDR_FLAGS_EOSP;
1258c2ecf20Sopenharmony_ci		*flags |= WMI_DATA_HDR_FLAGS_UAPSD;
1268c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->psq_lock);
1278c2ecf20Sopenharmony_ci		return false;
1288c2ecf20Sopenharmony_ci	} else if (!conn->apsd_info) {
1298c2ecf20Sopenharmony_ci		return false;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (test_bit(WMM_ENABLED, &vif->flags)) {
1338c2ecf20Sopenharmony_ci		ether_type = be16_to_cpu(datap->h_proto);
1348c2ecf20Sopenharmony_ci		if (is_ethertype(ether_type)) {
1358c2ecf20Sopenharmony_ci			/* packet is in DIX format  */
1368c2ecf20Sopenharmony_ci			ip_hdr = (u8 *)(datap + 1);
1378c2ecf20Sopenharmony_ci		} else {
1388c2ecf20Sopenharmony_ci			/* packet is in 802.3 format */
1398c2ecf20Sopenharmony_ci			llc_hdr = (struct ath6kl_llc_snap_hdr *)
1408c2ecf20Sopenharmony_ci							(datap + 1);
1418c2ecf20Sopenharmony_ci			ether_type = be16_to_cpu(llc_hdr->eth_type);
1428c2ecf20Sopenharmony_ci			ip_hdr = (u8 *)(llc_hdr + 1);
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		if (ether_type == IP_ETHERTYPE)
1468c2ecf20Sopenharmony_ci			up = ath6kl_wmi_determine_user_priority(
1478c2ecf20Sopenharmony_ci							ip_hdr, 0);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	traffic_class = ath6kl_wmi_get_traffic_class(up);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if ((conn->apsd_info & (1 << traffic_class)) == 0)
1538c2ecf20Sopenharmony_ci		return false;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Queue the frames if the STA is sleeping */
1568c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->psq_lock);
1578c2ecf20Sopenharmony_ci	is_apsdq_empty = skb_queue_empty(&conn->apsdq);
1588c2ecf20Sopenharmony_ci	skb_queue_tail(&conn->apsdq, skb);
1598c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->psq_lock);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/*
1628c2ecf20Sopenharmony_ci	 * If this is the first pkt getting queued
1638c2ecf20Sopenharmony_ci	 * for this STA, update the PVB for this STA
1648c2ecf20Sopenharmony_ci	 */
1658c2ecf20Sopenharmony_ci	if (is_apsdq_empty) {
1668c2ecf20Sopenharmony_ci		ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi,
1678c2ecf20Sopenharmony_ci					      vif->fw_vif_idx,
1688c2ecf20Sopenharmony_ci					      conn->aid, 1, 0);
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci	*flags |= WMI_DATA_HDR_FLAGS_UAPSD;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return true;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic bool ath6kl_process_psq(struct ath6kl_sta *conn,
1768c2ecf20Sopenharmony_ci				struct ath6kl_vif *vif,
1778c2ecf20Sopenharmony_ci				struct sk_buff *skb,
1788c2ecf20Sopenharmony_ci				u32 *flags)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	bool is_psq_empty = false;
1818c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (conn->sta_flags & STA_PS_POLLED) {
1848c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->psq_lock);
1858c2ecf20Sopenharmony_ci		if (!skb_queue_empty(&conn->psq))
1868c2ecf20Sopenharmony_ci			*flags |= WMI_DATA_HDR_FLAGS_MORE;
1878c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->psq_lock);
1888c2ecf20Sopenharmony_ci		return false;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* Queue the frames if the STA is sleeping */
1928c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->psq_lock);
1938c2ecf20Sopenharmony_ci	is_psq_empty = skb_queue_empty(&conn->psq);
1948c2ecf20Sopenharmony_ci	skb_queue_tail(&conn->psq, skb);
1958c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->psq_lock);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/*
1988c2ecf20Sopenharmony_ci	 * If this is the first pkt getting queued
1998c2ecf20Sopenharmony_ci	 * for this STA, update the PVB for this
2008c2ecf20Sopenharmony_ci	 * STA.
2018c2ecf20Sopenharmony_ci	 */
2028c2ecf20Sopenharmony_ci	if (is_psq_empty)
2038c2ecf20Sopenharmony_ci		ath6kl_wmi_set_pvb_cmd(ar->wmi,
2048c2ecf20Sopenharmony_ci				       vif->fw_vif_idx,
2058c2ecf20Sopenharmony_ci				       conn->aid, 1);
2068c2ecf20Sopenharmony_ci	return true;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic bool ath6kl_powersave_ap(struct ath6kl_vif *vif, struct sk_buff *skb,
2108c2ecf20Sopenharmony_ci				u32 *flags)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct ethhdr *datap = (struct ethhdr *) skb->data;
2138c2ecf20Sopenharmony_ci	struct ath6kl_sta *conn = NULL;
2148c2ecf20Sopenharmony_ci	bool ps_queued = false;
2158c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(datap->h_dest)) {
2188c2ecf20Sopenharmony_ci		u8 ctr = 0;
2198c2ecf20Sopenharmony_ci		bool q_mcast = false;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
2228c2ecf20Sopenharmony_ci			if (ar->sta_list[ctr].sta_flags & STA_PS_SLEEP) {
2238c2ecf20Sopenharmony_ci				q_mcast = true;
2248c2ecf20Sopenharmony_ci				break;
2258c2ecf20Sopenharmony_ci			}
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		if (q_mcast) {
2298c2ecf20Sopenharmony_ci			/*
2308c2ecf20Sopenharmony_ci			 * If this transmit is not because of a Dtim Expiry
2318c2ecf20Sopenharmony_ci			 * q it.
2328c2ecf20Sopenharmony_ci			 */
2338c2ecf20Sopenharmony_ci			if (!test_bit(DTIM_EXPIRED, &vif->flags)) {
2348c2ecf20Sopenharmony_ci				bool is_mcastq_empty = false;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci				spin_lock_bh(&ar->mcastpsq_lock);
2378c2ecf20Sopenharmony_ci				is_mcastq_empty =
2388c2ecf20Sopenharmony_ci					skb_queue_empty(&ar->mcastpsq);
2398c2ecf20Sopenharmony_ci				skb_queue_tail(&ar->mcastpsq, skb);
2408c2ecf20Sopenharmony_ci				spin_unlock_bh(&ar->mcastpsq_lock);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci				/*
2438c2ecf20Sopenharmony_ci				 * If this is the first Mcast pkt getting
2448c2ecf20Sopenharmony_ci				 * queued indicate to the target to set the
2458c2ecf20Sopenharmony_ci				 * BitmapControl LSB of the TIM IE.
2468c2ecf20Sopenharmony_ci				 */
2478c2ecf20Sopenharmony_ci				if (is_mcastq_empty)
2488c2ecf20Sopenharmony_ci					ath6kl_wmi_set_pvb_cmd(ar->wmi,
2498c2ecf20Sopenharmony_ci							       vif->fw_vif_idx,
2508c2ecf20Sopenharmony_ci							       MCAST_AID, 1);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci				ps_queued = true;
2538c2ecf20Sopenharmony_ci			} else {
2548c2ecf20Sopenharmony_ci				/*
2558c2ecf20Sopenharmony_ci				 * This transmit is because of Dtim expiry.
2568c2ecf20Sopenharmony_ci				 * Determine if MoreData bit has to be set.
2578c2ecf20Sopenharmony_ci				 */
2588c2ecf20Sopenharmony_ci				spin_lock_bh(&ar->mcastpsq_lock);
2598c2ecf20Sopenharmony_ci				if (!skb_queue_empty(&ar->mcastpsq))
2608c2ecf20Sopenharmony_ci					*flags |= WMI_DATA_HDR_FLAGS_MORE;
2618c2ecf20Sopenharmony_ci				spin_unlock_bh(&ar->mcastpsq_lock);
2628c2ecf20Sopenharmony_ci			}
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci	} else {
2658c2ecf20Sopenharmony_ci		conn = ath6kl_find_sta(vif, datap->h_dest);
2668c2ecf20Sopenharmony_ci		if (!conn) {
2678c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci			/* Inform the caller that the skb is consumed */
2708c2ecf20Sopenharmony_ci			return true;
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		if (conn->sta_flags & STA_PS_SLEEP) {
2748c2ecf20Sopenharmony_ci			ps_queued = ath6kl_process_uapsdq(conn,
2758c2ecf20Sopenharmony_ci						vif, skb, flags);
2768c2ecf20Sopenharmony_ci			if (!(*flags & WMI_DATA_HDR_FLAGS_UAPSD))
2778c2ecf20Sopenharmony_ci				ps_queued = ath6kl_process_psq(conn,
2788c2ecf20Sopenharmony_ci						vif, skb, flags);
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci	return ps_queued;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/* Tx functions */
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ciint ath6kl_control_tx(void *devt, struct sk_buff *skb,
2878c2ecf20Sopenharmony_ci		      enum htc_endpoint_id eid)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct ath6kl *ar = devt;
2908c2ecf20Sopenharmony_ci	int status = 0;
2918c2ecf20Sopenharmony_ci	struct ath6kl_cookie *cookie = NULL;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	trace_ath6kl_wmi_cmd(skb->data, skb->len);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) {
2968c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
2978c2ecf20Sopenharmony_ci		return -EACCES;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(eid == ENDPOINT_UNUSED ||
3018c2ecf20Sopenharmony_ci			 eid >= ENDPOINT_MAX)) {
3028c2ecf20Sopenharmony_ci		status = -EINVAL;
3038c2ecf20Sopenharmony_ci		goto fail_ctrl_tx;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->lock);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
3098c2ecf20Sopenharmony_ci		   "%s: skb=0x%p, len=0x%x eid =%d\n", __func__,
3108c2ecf20Sopenharmony_ci		   skb, skb->len, eid);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (test_bit(WMI_CTRL_EP_FULL, &ar->flag) && (eid == ar->ctrl_ep)) {
3138c2ecf20Sopenharmony_ci		/*
3148c2ecf20Sopenharmony_ci		 * Control endpoint is full, don't allocate resources, we
3158c2ecf20Sopenharmony_ci		 * are just going to drop this packet.
3168c2ecf20Sopenharmony_ci		 */
3178c2ecf20Sopenharmony_ci		cookie = NULL;
3188c2ecf20Sopenharmony_ci		ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n",
3198c2ecf20Sopenharmony_ci			   skb, skb->len);
3208c2ecf20Sopenharmony_ci	} else {
3218c2ecf20Sopenharmony_ci		cookie = ath6kl_alloc_cookie(ar);
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (cookie == NULL) {
3258c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->lock);
3268c2ecf20Sopenharmony_ci		status = -ENOMEM;
3278c2ecf20Sopenharmony_ci		goto fail_ctrl_tx;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ar->tx_pending[eid]++;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (eid != ar->ctrl_ep)
3338c2ecf20Sopenharmony_ci		ar->total_tx_data_pend++;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->lock);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	cookie->skb = skb;
3388c2ecf20Sopenharmony_ci	cookie->map_no = 0;
3398c2ecf20Sopenharmony_ci	set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
3408c2ecf20Sopenharmony_ci			 eid, ATH6KL_CONTROL_PKT_TAG);
3418c2ecf20Sopenharmony_ci	cookie->htc_pkt.skb = skb;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/*
3448c2ecf20Sopenharmony_ci	 * This interface is asynchronous, if there is an error, cleanup
3458c2ecf20Sopenharmony_ci	 * will happen in the TX completion callback.
3468c2ecf20Sopenharmony_ci	 */
3478c2ecf20Sopenharmony_ci	ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cifail_ctrl_tx:
3528c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
3538c2ecf20Sopenharmony_ci	return status;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cinetdev_tx_t ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct ath6kl *ar = ath6kl_priv(dev);
3598c2ecf20Sopenharmony_ci	struct ath6kl_cookie *cookie = NULL;
3608c2ecf20Sopenharmony_ci	enum htc_endpoint_id eid = ENDPOINT_UNUSED;
3618c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif = netdev_priv(dev);
3628c2ecf20Sopenharmony_ci	u32 map_no = 0;
3638c2ecf20Sopenharmony_ci	u16 htc_tag = ATH6KL_DATA_PKT_TAG;
3648c2ecf20Sopenharmony_ci	u8 ac = 99; /* initialize to unmapped ac */
3658c2ecf20Sopenharmony_ci	bool chk_adhoc_ps_mapping = false;
3668c2ecf20Sopenharmony_ci	int ret;
3678c2ecf20Sopenharmony_ci	struct wmi_tx_meta_v2 meta_v2;
3688c2ecf20Sopenharmony_ci	void *meta;
3698c2ecf20Sopenharmony_ci	u8 csum_start = 0, csum_dest = 0, csum = skb->ip_summed;
3708c2ecf20Sopenharmony_ci	u8 meta_ver = 0;
3718c2ecf20Sopenharmony_ci	u32 flags = 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
3748c2ecf20Sopenharmony_ci		   "%s: skb=0x%p, data=0x%p, len=0x%x\n", __func__,
3758c2ecf20Sopenharmony_ci		   skb, skb->data, skb->len);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* If target is not associated */
3788c2ecf20Sopenharmony_ci	if (!test_bit(CONNECTED, &vif->flags))
3798c2ecf20Sopenharmony_ci		goto fail_tx;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(ar->state != ATH6KL_STATE_ON))
3828c2ecf20Sopenharmony_ci		goto fail_tx;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (!test_bit(WMI_READY, &ar->flag))
3858c2ecf20Sopenharmony_ci		goto fail_tx;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* AP mode Power saving processing */
3888c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
3898c2ecf20Sopenharmony_ci		if (ath6kl_powersave_ap(vif, skb, &flags))
3908c2ecf20Sopenharmony_ci			return 0;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (test_bit(WMI_ENABLED, &ar->flag)) {
3948c2ecf20Sopenharmony_ci		if ((dev->features & NETIF_F_IP_CSUM) &&
3958c2ecf20Sopenharmony_ci		    (csum == CHECKSUM_PARTIAL)) {
3968c2ecf20Sopenharmony_ci			csum_start = skb->csum_start -
3978c2ecf20Sopenharmony_ci					(skb_network_header(skb) - skb->head) +
3988c2ecf20Sopenharmony_ci					sizeof(struct ath6kl_llc_snap_hdr);
3998c2ecf20Sopenharmony_ci			csum_dest = skb->csum_offset + csum_start;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		if (skb_cow_head(skb, dev->needed_headroom)) {
4038c2ecf20Sopenharmony_ci			dev->stats.tx_dropped++;
4048c2ecf20Sopenharmony_ci			kfree_skb(skb);
4058c2ecf20Sopenharmony_ci			return 0;
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		if (ath6kl_wmi_dix_2_dot3(ar->wmi, skb)) {
4098c2ecf20Sopenharmony_ci			ath6kl_err("ath6kl_wmi_dix_2_dot3 failed\n");
4108c2ecf20Sopenharmony_ci			goto fail_tx;
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		if ((dev->features & NETIF_F_IP_CSUM) &&
4148c2ecf20Sopenharmony_ci		    (csum == CHECKSUM_PARTIAL)) {
4158c2ecf20Sopenharmony_ci			meta_v2.csum_start = csum_start;
4168c2ecf20Sopenharmony_ci			meta_v2.csum_dest = csum_dest;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci			/* instruct target to calculate checksum */
4198c2ecf20Sopenharmony_ci			meta_v2.csum_flags = WMI_META_V2_FLAG_CSUM_OFFLOAD;
4208c2ecf20Sopenharmony_ci			meta_ver = WMI_META_VERSION_2;
4218c2ecf20Sopenharmony_ci			meta = &meta_v2;
4228c2ecf20Sopenharmony_ci		} else {
4238c2ecf20Sopenharmony_ci			meta_ver = 0;
4248c2ecf20Sopenharmony_ci			meta = NULL;
4258c2ecf20Sopenharmony_ci		}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		ret = ath6kl_wmi_data_hdr_add(ar->wmi, skb,
4288c2ecf20Sopenharmony_ci				DATA_MSGTYPE, flags, 0,
4298c2ecf20Sopenharmony_ci				meta_ver,
4308c2ecf20Sopenharmony_ci				meta, vif->fw_vif_idx);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		if (ret) {
4338c2ecf20Sopenharmony_ci			ath6kl_warn("failed to add wmi data header:%d\n"
4348c2ecf20Sopenharmony_ci				, ret);
4358c2ecf20Sopenharmony_ci			goto fail_tx;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		if ((vif->nw_type == ADHOC_NETWORK) &&
4398c2ecf20Sopenharmony_ci		    ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags))
4408c2ecf20Sopenharmony_ci			chk_adhoc_ps_mapping = true;
4418c2ecf20Sopenharmony_ci		else {
4428c2ecf20Sopenharmony_ci			/* get the stream mapping */
4438c2ecf20Sopenharmony_ci			ret = ath6kl_wmi_implicit_create_pstream(ar->wmi,
4448c2ecf20Sopenharmony_ci				    vif->fw_vif_idx, skb,
4458c2ecf20Sopenharmony_ci				    0, test_bit(WMM_ENABLED, &vif->flags), &ac);
4468c2ecf20Sopenharmony_ci			if (ret)
4478c2ecf20Sopenharmony_ci				goto fail_tx;
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci	} else {
4508c2ecf20Sopenharmony_ci		goto fail_tx;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->lock);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (chk_adhoc_ps_mapping)
4568c2ecf20Sopenharmony_ci		eid = ath6kl_ibss_map_epid(skb, dev, &map_no);
4578c2ecf20Sopenharmony_ci	else
4588c2ecf20Sopenharmony_ci		eid = ar->ac2ep_map[ac];
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (eid == 0 || eid == ENDPOINT_UNUSED) {
4618c2ecf20Sopenharmony_ci		ath6kl_err("eid %d is not mapped!\n", eid);
4628c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->lock);
4638c2ecf20Sopenharmony_ci		goto fail_tx;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* allocate resource for this packet */
4678c2ecf20Sopenharmony_ci	cookie = ath6kl_alloc_cookie(ar);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (!cookie) {
4708c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->lock);
4718c2ecf20Sopenharmony_ci		goto fail_tx;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* update counts while the lock is held */
4758c2ecf20Sopenharmony_ci	ar->tx_pending[eid]++;
4768c2ecf20Sopenharmony_ci	ar->total_tx_data_pend++;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->lock);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) &&
4818c2ecf20Sopenharmony_ci	    skb_cloned(skb)) {
4828c2ecf20Sopenharmony_ci		/*
4838c2ecf20Sopenharmony_ci		 * We will touch (move the buffer data to align it. Since the
4848c2ecf20Sopenharmony_ci		 * skb buffer is cloned and not only the header is changed, we
4858c2ecf20Sopenharmony_ci		 * have to copy it to allow the changes. Since we are copying
4868c2ecf20Sopenharmony_ci		 * the data here, we may as well align it by reserving suitable
4878c2ecf20Sopenharmony_ci		 * headroom to avoid the memmove in ath6kl_htc_tx_buf_align().
4888c2ecf20Sopenharmony_ci		 */
4898c2ecf20Sopenharmony_ci		struct sk_buff *nskb;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC);
4928c2ecf20Sopenharmony_ci		if (nskb == NULL)
4938c2ecf20Sopenharmony_ci			goto fail_tx;
4948c2ecf20Sopenharmony_ci		kfree_skb(skb);
4958c2ecf20Sopenharmony_ci		skb = nskb;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	cookie->skb = skb;
4998c2ecf20Sopenharmony_ci	cookie->map_no = map_no;
5008c2ecf20Sopenharmony_ci	set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
5018c2ecf20Sopenharmony_ci			 eid, htc_tag);
5028c2ecf20Sopenharmony_ci	cookie->htc_pkt.skb = skb;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
5058c2ecf20Sopenharmony_ci			skb->data, skb->len);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/*
5088c2ecf20Sopenharmony_ci	 * HTC interface is asynchronous, if this fails, cleanup will
5098c2ecf20Sopenharmony_ci	 * happen in the ath6kl_tx_complete callback.
5108c2ecf20Sopenharmony_ci	 */
5118c2ecf20Sopenharmony_ci	ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return 0;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cifail_tx:
5168c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	dev->stats.tx_dropped++;
5198c2ecf20Sopenharmony_ci	dev->stats.tx_aborted_errors++;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return 0;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/* indicate tx activity or inactivity on a WMI stream */
5258c2ecf20Sopenharmony_civoid ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct ath6kl *ar = devt;
5288c2ecf20Sopenharmony_ci	enum htc_endpoint_id eid;
5298c2ecf20Sopenharmony_ci	int i;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	eid = ar->ac2ep_map[traffic_class];
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (!test_bit(WMI_ENABLED, &ar->flag))
5348c2ecf20Sopenharmony_ci		goto notify_htc;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->lock);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	ar->ac_stream_active[traffic_class] = active;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (active) {
5418c2ecf20Sopenharmony_ci		/*
5428c2ecf20Sopenharmony_ci		 * Keep track of the active stream with the highest
5438c2ecf20Sopenharmony_ci		 * priority.
5448c2ecf20Sopenharmony_ci		 */
5458c2ecf20Sopenharmony_ci		if (ar->ac_stream_pri_map[traffic_class] >
5468c2ecf20Sopenharmony_ci		    ar->hiac_stream_active_pri)
5478c2ecf20Sopenharmony_ci			/* set the new highest active priority */
5488c2ecf20Sopenharmony_ci			ar->hiac_stream_active_pri =
5498c2ecf20Sopenharmony_ci					ar->ac_stream_pri_map[traffic_class];
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	} else {
5528c2ecf20Sopenharmony_ci		/*
5538c2ecf20Sopenharmony_ci		 * We may have to search for the next active stream
5548c2ecf20Sopenharmony_ci		 * that is the highest priority.
5558c2ecf20Sopenharmony_ci		 */
5568c2ecf20Sopenharmony_ci		if (ar->hiac_stream_active_pri ==
5578c2ecf20Sopenharmony_ci			ar->ac_stream_pri_map[traffic_class]) {
5588c2ecf20Sopenharmony_ci			/*
5598c2ecf20Sopenharmony_ci			 * The highest priority stream just went inactive
5608c2ecf20Sopenharmony_ci			 * reset and search for the "next" highest "active"
5618c2ecf20Sopenharmony_ci			 * priority stream.
5628c2ecf20Sopenharmony_ci			 */
5638c2ecf20Sopenharmony_ci			ar->hiac_stream_active_pri = 0;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci			for (i = 0; i < WMM_NUM_AC; i++) {
5668c2ecf20Sopenharmony_ci				if (ar->ac_stream_active[i] &&
5678c2ecf20Sopenharmony_ci				    (ar->ac_stream_pri_map[i] >
5688c2ecf20Sopenharmony_ci				     ar->hiac_stream_active_pri))
5698c2ecf20Sopenharmony_ci					/*
5708c2ecf20Sopenharmony_ci					 * Set the new highest active
5718c2ecf20Sopenharmony_ci					 * priority.
5728c2ecf20Sopenharmony_ci					 */
5738c2ecf20Sopenharmony_ci					ar->hiac_stream_active_pri =
5748c2ecf20Sopenharmony_ci						ar->ac_stream_pri_map[i];
5758c2ecf20Sopenharmony_ci			}
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->lock);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cinotify_htc:
5828c2ecf20Sopenharmony_ci	/* notify HTC, this may cause credit distribution changes */
5838c2ecf20Sopenharmony_ci	ath6kl_htc_activity_changed(ar->htc_target, eid, active);
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cienum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
5878c2ecf20Sopenharmony_ci					       struct htc_packet *packet)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct ath6kl *ar = target->dev->ar;
5908c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif;
5918c2ecf20Sopenharmony_ci	enum htc_endpoint_id endpoint = packet->endpoint;
5928c2ecf20Sopenharmony_ci	enum htc_send_full_action action = HTC_SEND_FULL_KEEP;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (endpoint == ar->ctrl_ep) {
5958c2ecf20Sopenharmony_ci		/*
5968c2ecf20Sopenharmony_ci		 * Under normal WMI if this is getting full, then something
5978c2ecf20Sopenharmony_ci		 * is running rampant the host should not be exhausting the
5988c2ecf20Sopenharmony_ci		 * WMI queue with too many commands the only exception to
5998c2ecf20Sopenharmony_ci		 * this is during testing using endpointping.
6008c2ecf20Sopenharmony_ci		 */
6018c2ecf20Sopenharmony_ci		set_bit(WMI_CTRL_EP_FULL, &ar->flag);
6028c2ecf20Sopenharmony_ci		ath6kl_err("wmi ctrl ep is full\n");
6038c2ecf20Sopenharmony_ci		ath6kl_recovery_err_notify(ar, ATH6KL_FW_EP_FULL);
6048c2ecf20Sopenharmony_ci		return action;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if (packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG)
6088c2ecf20Sopenharmony_ci		return action;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	/*
6118c2ecf20Sopenharmony_ci	 * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for
6128c2ecf20Sopenharmony_ci	 * the highest active stream.
6138c2ecf20Sopenharmony_ci	 */
6148c2ecf20Sopenharmony_ci	if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] <
6158c2ecf20Sopenharmony_ci	    ar->hiac_stream_active_pri &&
6168c2ecf20Sopenharmony_ci	    ar->cookie_count <=
6178c2ecf20Sopenharmony_ci			target->endpoint[endpoint].tx_drop_packet_threshold)
6188c2ecf20Sopenharmony_ci		/*
6198c2ecf20Sopenharmony_ci		 * Give preference to the highest priority stream by
6208c2ecf20Sopenharmony_ci		 * dropping the packets which overflowed.
6218c2ecf20Sopenharmony_ci		 */
6228c2ecf20Sopenharmony_ci		action = HTC_SEND_FULL_DROP;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	/* FIXME: Locking */
6258c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->list_lock);
6268c2ecf20Sopenharmony_ci	list_for_each_entry(vif, &ar->vif_list, list) {
6278c2ecf20Sopenharmony_ci		if (vif->nw_type == ADHOC_NETWORK ||
6288c2ecf20Sopenharmony_ci		    action != HTC_SEND_FULL_DROP) {
6298c2ecf20Sopenharmony_ci			spin_unlock_bh(&ar->list_lock);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci			set_bit(NETQ_STOPPED, &vif->flags);
6328c2ecf20Sopenharmony_ci			netif_stop_queue(vif->ndev);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci			return action;
6358c2ecf20Sopenharmony_ci		}
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->list_lock);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	return action;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci/* TODO this needs to be looked at */
6438c2ecf20Sopenharmony_cistatic void ath6kl_tx_clear_node_map(struct ath6kl_vif *vif,
6448c2ecf20Sopenharmony_ci				     enum htc_endpoint_id eid, u32 map_no)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
6478c2ecf20Sopenharmony_ci	u32 i;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (vif->nw_type != ADHOC_NETWORK)
6508c2ecf20Sopenharmony_ci		return;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (!ar->ibss_ps_enable)
6538c2ecf20Sopenharmony_ci		return;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (eid == ar->ctrl_ep)
6568c2ecf20Sopenharmony_ci		return;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (map_no == 0)
6598c2ecf20Sopenharmony_ci		return;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	map_no--;
6628c2ecf20Sopenharmony_ci	ar->node_map[map_no].tx_pend--;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (ar->node_map[map_no].tx_pend)
6658c2ecf20Sopenharmony_ci		return;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (map_no != (ar->node_num - 1))
6688c2ecf20Sopenharmony_ci		return;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	for (i = ar->node_num; i > 0; i--) {
6718c2ecf20Sopenharmony_ci		if (ar->node_map[i - 1].tx_pend)
6728c2ecf20Sopenharmony_ci			break;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci		memset(&ar->node_map[i - 1], 0,
6758c2ecf20Sopenharmony_ci		       sizeof(struct ath6kl_node_mapping));
6768c2ecf20Sopenharmony_ci		ar->node_num--;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_civoid ath6kl_tx_complete(struct htc_target *target,
6818c2ecf20Sopenharmony_ci			struct list_head *packet_queue)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	struct ath6kl *ar = target->dev->ar;
6848c2ecf20Sopenharmony_ci	struct sk_buff_head skb_queue;
6858c2ecf20Sopenharmony_ci	struct htc_packet *packet;
6868c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6878c2ecf20Sopenharmony_ci	struct ath6kl_cookie *ath6kl_cookie;
6888c2ecf20Sopenharmony_ci	u32 map_no = 0;
6898c2ecf20Sopenharmony_ci	int status;
6908c2ecf20Sopenharmony_ci	enum htc_endpoint_id eid;
6918c2ecf20Sopenharmony_ci	bool wake_event = false;
6928c2ecf20Sopenharmony_ci	bool flushing[ATH6KL_VIF_MAX] = {false};
6938c2ecf20Sopenharmony_ci	u8 if_idx;
6948c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	skb_queue_head_init(&skb_queue);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/* lock the driver as we update internal state */
6998c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->lock);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* reap completed packets */
7028c2ecf20Sopenharmony_ci	while (!list_empty(packet_queue)) {
7038c2ecf20Sopenharmony_ci		packet = list_first_entry(packet_queue, struct htc_packet,
7048c2ecf20Sopenharmony_ci					  list);
7058c2ecf20Sopenharmony_ci		list_del(&packet->list);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(packet->endpoint == ENDPOINT_UNUSED ||
7088c2ecf20Sopenharmony_ci				 packet->endpoint >= ENDPOINT_MAX))
7098c2ecf20Sopenharmony_ci			continue;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci		ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt;
7128c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!ath6kl_cookie))
7138c2ecf20Sopenharmony_ci			continue;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci		status = packet->status;
7168c2ecf20Sopenharmony_ci		skb = ath6kl_cookie->skb;
7178c2ecf20Sopenharmony_ci		eid = packet->endpoint;
7188c2ecf20Sopenharmony_ci		map_no = ath6kl_cookie->map_no;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!skb || !skb->data)) {
7218c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
7228c2ecf20Sopenharmony_ci			ath6kl_free_cookie(ar, ath6kl_cookie);
7238c2ecf20Sopenharmony_ci			continue;
7248c2ecf20Sopenharmony_ci		}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci		__skb_queue_tail(&skb_queue, skb);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!status && (packet->act_len != skb->len))) {
7298c2ecf20Sopenharmony_ci			ath6kl_free_cookie(ar, ath6kl_cookie);
7308c2ecf20Sopenharmony_ci			continue;
7318c2ecf20Sopenharmony_ci		}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		ar->tx_pending[eid]--;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		if (eid != ar->ctrl_ep)
7368c2ecf20Sopenharmony_ci			ar->total_tx_data_pend--;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		if (eid == ar->ctrl_ep) {
7398c2ecf20Sopenharmony_ci			if (test_bit(WMI_CTRL_EP_FULL, &ar->flag))
7408c2ecf20Sopenharmony_ci				clear_bit(WMI_CTRL_EP_FULL, &ar->flag);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci			if (ar->tx_pending[eid] == 0)
7438c2ecf20Sopenharmony_ci				wake_event = true;
7448c2ecf20Sopenharmony_ci		}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci		if (eid == ar->ctrl_ep) {
7478c2ecf20Sopenharmony_ci			if_idx = wmi_cmd_hdr_get_if_idx(
7488c2ecf20Sopenharmony_ci				(struct wmi_cmd_hdr *) packet->buf);
7498c2ecf20Sopenharmony_ci		} else {
7508c2ecf20Sopenharmony_ci			if_idx = wmi_data_hdr_get_if_idx(
7518c2ecf20Sopenharmony_ci				(struct wmi_data_hdr *) packet->buf);
7528c2ecf20Sopenharmony_ci		}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		vif = ath6kl_get_vif_by_index(ar, if_idx);
7558c2ecf20Sopenharmony_ci		if (!vif) {
7568c2ecf20Sopenharmony_ci			ath6kl_free_cookie(ar, ath6kl_cookie);
7578c2ecf20Sopenharmony_ci			continue;
7588c2ecf20Sopenharmony_ci		}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci		if (status) {
7618c2ecf20Sopenharmony_ci			if (status == -ECANCELED)
7628c2ecf20Sopenharmony_ci				/* a packet was flushed  */
7638c2ecf20Sopenharmony_ci				flushing[if_idx] = true;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci			vif->ndev->stats.tx_errors++;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci			if (status != -ENOSPC && status != -ECANCELED)
7688c2ecf20Sopenharmony_ci				ath6kl_warn("tx complete error: %d\n", status);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci			ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
7718c2ecf20Sopenharmony_ci				   "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n",
7728c2ecf20Sopenharmony_ci				   __func__, skb, packet->buf, packet->act_len,
7738c2ecf20Sopenharmony_ci				   eid, "error!");
7748c2ecf20Sopenharmony_ci		} else {
7758c2ecf20Sopenharmony_ci			ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
7768c2ecf20Sopenharmony_ci				   "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n",
7778c2ecf20Sopenharmony_ci				   __func__, skb, packet->buf, packet->act_len,
7788c2ecf20Sopenharmony_ci				   eid, "OK");
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci			flushing[if_idx] = false;
7818c2ecf20Sopenharmony_ci			vif->ndev->stats.tx_packets++;
7828c2ecf20Sopenharmony_ci			vif->ndev->stats.tx_bytes += skb->len;
7838c2ecf20Sopenharmony_ci		}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		ath6kl_tx_clear_node_map(vif, eid, map_no);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		ath6kl_free_cookie(ar, ath6kl_cookie);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		if (test_bit(NETQ_STOPPED, &vif->flags))
7908c2ecf20Sopenharmony_ci			clear_bit(NETQ_STOPPED, &vif->flags);
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->lock);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	__skb_queue_purge(&skb_queue);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	/* FIXME: Locking */
7988c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->list_lock);
7998c2ecf20Sopenharmony_ci	list_for_each_entry(vif, &ar->vif_list, list) {
8008c2ecf20Sopenharmony_ci		if (test_bit(CONNECTED, &vif->flags) &&
8018c2ecf20Sopenharmony_ci		    !flushing[vif->fw_vif_idx]) {
8028c2ecf20Sopenharmony_ci			spin_unlock_bh(&ar->list_lock);
8038c2ecf20Sopenharmony_ci			netif_wake_queue(vif->ndev);
8048c2ecf20Sopenharmony_ci			spin_lock_bh(&ar->list_lock);
8058c2ecf20Sopenharmony_ci		}
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->list_lock);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	if (wake_event)
8108c2ecf20Sopenharmony_ci		wake_up(&ar->event_wq);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_civoid ath6kl_tx_data_cleanup(struct ath6kl *ar)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	int i;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	/* flush all the data (non-control) streams */
8208c2ecf20Sopenharmony_ci	for (i = 0; i < WMM_NUM_AC; i++)
8218c2ecf20Sopenharmony_ci		ath6kl_htc_flush_txep(ar->htc_target, ar->ac2ep_map[i],
8228c2ecf20Sopenharmony_ci				      ATH6KL_DATA_PKT_TAG);
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/* Rx functions */
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_cistatic void ath6kl_deliver_frames_to_nw_stack(struct net_device *dev,
8288c2ecf20Sopenharmony_ci					      struct sk_buff *skb)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	if (!skb)
8318c2ecf20Sopenharmony_ci		return;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	skb->dev = dev;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (!(skb->dev->flags & IFF_UP)) {
8368c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
8378c2ecf20Sopenharmony_ci		return;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, skb->dev);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	netif_rx_ni(skb);
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic void ath6kl_alloc_netbufs(struct sk_buff_head *q, u16 num)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	while (num) {
8508c2ecf20Sopenharmony_ci		skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE);
8518c2ecf20Sopenharmony_ci		if (!skb) {
8528c2ecf20Sopenharmony_ci			ath6kl_err("netbuf allocation failed\n");
8538c2ecf20Sopenharmony_ci			return;
8548c2ecf20Sopenharmony_ci		}
8558c2ecf20Sopenharmony_ci		skb_queue_tail(q, skb);
8568c2ecf20Sopenharmony_ci		num--;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic struct sk_buff *aggr_get_free_skb(struct aggr_info *p_aggr)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (skb_queue_len(&p_aggr->rx_amsdu_freeq) <
8658c2ecf20Sopenharmony_ci	    (AGGR_NUM_OF_FREE_NETBUFS >> 2))
8668c2ecf20Sopenharmony_ci		ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq,
8678c2ecf20Sopenharmony_ci				     AGGR_NUM_OF_FREE_NETBUFS);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	skb = skb_dequeue(&p_aggr->rx_amsdu_freeq);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return skb;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_civoid ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	struct ath6kl *ar = target->dev->ar;
8778c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8788c2ecf20Sopenharmony_ci	int rx_buf;
8798c2ecf20Sopenharmony_ci	int n_buf_refill;
8808c2ecf20Sopenharmony_ci	struct htc_packet *packet;
8818c2ecf20Sopenharmony_ci	struct list_head queue;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	n_buf_refill = ATH6KL_MAX_RX_BUFFERS -
8848c2ecf20Sopenharmony_ci			  ath6kl_htc_get_rxbuf_num(ar->htc_target, endpoint);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	if (n_buf_refill <= 0)
8878c2ecf20Sopenharmony_ci		return;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&queue);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_RX,
8928c2ecf20Sopenharmony_ci		   "%s: providing htc with %d buffers at eid=%d\n",
8938c2ecf20Sopenharmony_ci		   __func__, n_buf_refill, endpoint);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	for (rx_buf = 0; rx_buf < n_buf_refill; rx_buf++) {
8968c2ecf20Sopenharmony_ci		skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE);
8978c2ecf20Sopenharmony_ci		if (!skb)
8988c2ecf20Sopenharmony_ci			break;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci		packet = (struct htc_packet *) skb->head;
9018c2ecf20Sopenharmony_ci		if (!IS_ALIGNED((unsigned long) skb->data, 4)) {
9028c2ecf20Sopenharmony_ci			size_t len = skb_headlen(skb);
9038c2ecf20Sopenharmony_ci			skb->data = PTR_ALIGN(skb->data - 4, 4);
9048c2ecf20Sopenharmony_ci			skb_set_tail_pointer(skb, len);
9058c2ecf20Sopenharmony_ci		}
9068c2ecf20Sopenharmony_ci		set_htc_rxpkt_info(packet, skb, skb->data,
9078c2ecf20Sopenharmony_ci				   ATH6KL_BUFFER_SIZE, endpoint);
9088c2ecf20Sopenharmony_ci		packet->skb = skb;
9098c2ecf20Sopenharmony_ci		list_add_tail(&packet->list, &queue);
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if (!list_empty(&queue))
9138c2ecf20Sopenharmony_ci		ath6kl_htc_add_rxbuf_multiple(ar->htc_target, &queue);
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_civoid ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct htc_packet *packet;
9198c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	while (count) {
9228c2ecf20Sopenharmony_ci		skb = ath6kl_buf_alloc(ATH6KL_AMSDU_BUFFER_SIZE);
9238c2ecf20Sopenharmony_ci		if (!skb)
9248c2ecf20Sopenharmony_ci			return;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		packet = (struct htc_packet *) skb->head;
9278c2ecf20Sopenharmony_ci		if (!IS_ALIGNED((unsigned long) skb->data, 4)) {
9288c2ecf20Sopenharmony_ci			size_t len = skb_headlen(skb);
9298c2ecf20Sopenharmony_ci			skb->data = PTR_ALIGN(skb->data - 4, 4);
9308c2ecf20Sopenharmony_ci			skb_set_tail_pointer(skb, len);
9318c2ecf20Sopenharmony_ci		}
9328c2ecf20Sopenharmony_ci		set_htc_rxpkt_info(packet, skb, skb->data,
9338c2ecf20Sopenharmony_ci				   ATH6KL_AMSDU_BUFFER_SIZE, 0);
9348c2ecf20Sopenharmony_ci		packet->skb = skb;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		spin_lock_bh(&ar->lock);
9378c2ecf20Sopenharmony_ci		list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue);
9388c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->lock);
9398c2ecf20Sopenharmony_ci		count--;
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci/*
9448c2ecf20Sopenharmony_ci * Callback to allocate a receive buffer for a pending packet. We use a
9458c2ecf20Sopenharmony_ci * pre-allocated list of buffers of maximum AMSDU size (4K).
9468c2ecf20Sopenharmony_ci */
9478c2ecf20Sopenharmony_cistruct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target,
9488c2ecf20Sopenharmony_ci					    enum htc_endpoint_id endpoint,
9498c2ecf20Sopenharmony_ci					    int len)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	struct ath6kl *ar = target->dev->ar;
9528c2ecf20Sopenharmony_ci	struct htc_packet *packet = NULL;
9538c2ecf20Sopenharmony_ci	struct list_head *pkt_pos;
9548c2ecf20Sopenharmony_ci	int refill_cnt = 0, depth = 0;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: eid=%d, len:%d\n",
9578c2ecf20Sopenharmony_ci		   __func__, endpoint, len);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if ((len <= ATH6KL_BUFFER_SIZE) ||
9608c2ecf20Sopenharmony_ci	    (len > ATH6KL_AMSDU_BUFFER_SIZE))
9618c2ecf20Sopenharmony_ci		return NULL;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->lock);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	if (list_empty(&ar->amsdu_rx_buffer_queue)) {
9668c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->lock);
9678c2ecf20Sopenharmony_ci		refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS;
9688c2ecf20Sopenharmony_ci		goto refill_buf;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	packet = list_first_entry(&ar->amsdu_rx_buffer_queue,
9728c2ecf20Sopenharmony_ci				  struct htc_packet, list);
9738c2ecf20Sopenharmony_ci	list_del(&packet->list);
9748c2ecf20Sopenharmony_ci	list_for_each(pkt_pos, &ar->amsdu_rx_buffer_queue)
9758c2ecf20Sopenharmony_ci		depth++;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS - depth;
9788c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->lock);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	/* set actual endpoint ID */
9818c2ecf20Sopenharmony_ci	packet->endpoint = endpoint;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cirefill_buf:
9848c2ecf20Sopenharmony_ci	if (refill_cnt >= ATH6KL_AMSDU_REFILL_THRESHOLD)
9858c2ecf20Sopenharmony_ci		ath6kl_refill_amsdu_rxbufs(ar, refill_cnt);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	return packet;
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic void aggr_slice_amsdu(struct aggr_info *p_aggr,
9918c2ecf20Sopenharmony_ci			     struct rxtid *rxtid, struct sk_buff *skb)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	struct sk_buff *new_skb;
9948c2ecf20Sopenharmony_ci	struct ethhdr *hdr;
9958c2ecf20Sopenharmony_ci	u16 frame_8023_len, payload_8023_len, mac_hdr_len, amsdu_len;
9968c2ecf20Sopenharmony_ci	u8 *framep;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	mac_hdr_len = sizeof(struct ethhdr);
9998c2ecf20Sopenharmony_ci	framep = skb->data + mac_hdr_len;
10008c2ecf20Sopenharmony_ci	amsdu_len = skb->len - mac_hdr_len;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	while (amsdu_len > mac_hdr_len) {
10038c2ecf20Sopenharmony_ci		hdr = (struct ethhdr *) framep;
10048c2ecf20Sopenharmony_ci		payload_8023_len = be16_to_cpu(hdr->h_proto);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci		if (payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN ||
10078c2ecf20Sopenharmony_ci		    payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) {
10088c2ecf20Sopenharmony_ci			ath6kl_err("802.3 AMSDU frame bound check failed. len %d\n",
10098c2ecf20Sopenharmony_ci				   payload_8023_len);
10108c2ecf20Sopenharmony_ci			break;
10118c2ecf20Sopenharmony_ci		}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci		frame_8023_len = payload_8023_len + mac_hdr_len;
10148c2ecf20Sopenharmony_ci		new_skb = aggr_get_free_skb(p_aggr);
10158c2ecf20Sopenharmony_ci		if (!new_skb) {
10168c2ecf20Sopenharmony_ci			ath6kl_err("no buffer available\n");
10178c2ecf20Sopenharmony_ci			break;
10188c2ecf20Sopenharmony_ci		}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		memcpy(new_skb->data, framep, frame_8023_len);
10218c2ecf20Sopenharmony_ci		skb_put(new_skb, frame_8023_len);
10228c2ecf20Sopenharmony_ci		if (ath6kl_wmi_dot3_2_dix(new_skb)) {
10238c2ecf20Sopenharmony_ci			ath6kl_err("dot3_2_dix error\n");
10248c2ecf20Sopenharmony_ci			dev_kfree_skb(new_skb);
10258c2ecf20Sopenharmony_ci			break;
10268c2ecf20Sopenharmony_ci		}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		skb_queue_tail(&rxtid->q, new_skb);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci		/* Is this the last subframe within this aggregate ? */
10318c2ecf20Sopenharmony_ci		if ((amsdu_len - frame_8023_len) == 0)
10328c2ecf20Sopenharmony_ci			break;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		/* Add the length of A-MSDU subframe padding bytes -
10358c2ecf20Sopenharmony_ci		 * Round to nearest word.
10368c2ecf20Sopenharmony_ci		 */
10378c2ecf20Sopenharmony_ci		frame_8023_len = ALIGN(frame_8023_len, 4);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci		framep += frame_8023_len;
10408c2ecf20Sopenharmony_ci		amsdu_len -= frame_8023_len;
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_cistatic void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid,
10478c2ecf20Sopenharmony_ci			    u16 seq_no, u8 order)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	struct sk_buff *skb;
10508c2ecf20Sopenharmony_ci	struct rxtid *rxtid;
10518c2ecf20Sopenharmony_ci	struct skb_hold_q *node;
10528c2ecf20Sopenharmony_ci	u16 idx, idx_end, seq_end;
10538c2ecf20Sopenharmony_ci	struct rxtid_stats *stats;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	rxtid = &agg_conn->rx_tid[tid];
10568c2ecf20Sopenharmony_ci	stats = &agg_conn->stat[tid];
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	spin_lock_bh(&rxtid->lock);
10598c2ecf20Sopenharmony_ci	idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	/*
10628c2ecf20Sopenharmony_ci	 * idx_end is typically the last possible frame in the window,
10638c2ecf20Sopenharmony_ci	 * but changes to 'the' seq_no, when BAR comes. If seq_no
10648c2ecf20Sopenharmony_ci	 * is non-zero, we will go up to that and stop.
10658c2ecf20Sopenharmony_ci	 * Note: last seq no in current window will occupy the same
10668c2ecf20Sopenharmony_ci	 * index position as index that is just previous to start.
10678c2ecf20Sopenharmony_ci	 * An imp point : if win_sz is 7, for seq_no space of 4095,
10688c2ecf20Sopenharmony_ci	 * then, there would be holes when sequence wrap around occurs.
10698c2ecf20Sopenharmony_ci	 * Target should judiciously choose the win_sz, based on
10708c2ecf20Sopenharmony_ci	 * this condition. For 4095, (TID_WINDOW_SZ = 2 x win_sz
10718c2ecf20Sopenharmony_ci	 * 2, 4, 8, 16 win_sz works fine).
10728c2ecf20Sopenharmony_ci	 * We must deque from "idx" to "idx_end", including both.
10738c2ecf20Sopenharmony_ci	 */
10748c2ecf20Sopenharmony_ci	seq_end = seq_no ? seq_no : rxtid->seq_next;
10758c2ecf20Sopenharmony_ci	idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	do {
10788c2ecf20Sopenharmony_ci		node = &rxtid->hold_q[idx];
10798c2ecf20Sopenharmony_ci		if ((order == 1) && (!node->skb))
10808c2ecf20Sopenharmony_ci			break;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		if (node->skb) {
10838c2ecf20Sopenharmony_ci			if (node->is_amsdu)
10848c2ecf20Sopenharmony_ci				aggr_slice_amsdu(agg_conn->aggr_info, rxtid,
10858c2ecf20Sopenharmony_ci						 node->skb);
10868c2ecf20Sopenharmony_ci			else
10878c2ecf20Sopenharmony_ci				skb_queue_tail(&rxtid->q, node->skb);
10888c2ecf20Sopenharmony_ci			node->skb = NULL;
10898c2ecf20Sopenharmony_ci		} else {
10908c2ecf20Sopenharmony_ci			stats->num_hole++;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next);
10948c2ecf20Sopenharmony_ci		idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
10958c2ecf20Sopenharmony_ci	} while (idx != idx_end);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	spin_unlock_bh(&rxtid->lock);
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	stats->num_delivered += skb_queue_len(&rxtid->q);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&rxtid->q)))
11028c2ecf20Sopenharmony_ci		ath6kl_deliver_frames_to_nw_stack(agg_conn->dev, skb);
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_cistatic bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid,
11068c2ecf20Sopenharmony_ci				  u16 seq_no,
11078c2ecf20Sopenharmony_ci				  bool is_amsdu, struct sk_buff *frame)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	struct rxtid *rxtid;
11108c2ecf20Sopenharmony_ci	struct rxtid_stats *stats;
11118c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11128c2ecf20Sopenharmony_ci	struct skb_hold_q *node;
11138c2ecf20Sopenharmony_ci	u16 idx, st, cur, end;
11148c2ecf20Sopenharmony_ci	bool is_queued = false;
11158c2ecf20Sopenharmony_ci	u16 extended_end;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	rxtid = &agg_conn->rx_tid[tid];
11188c2ecf20Sopenharmony_ci	stats = &agg_conn->stat[tid];
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	stats->num_into_aggr++;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (!rxtid->aggr) {
11238c2ecf20Sopenharmony_ci		if (is_amsdu) {
11248c2ecf20Sopenharmony_ci			aggr_slice_amsdu(agg_conn->aggr_info, rxtid, frame);
11258c2ecf20Sopenharmony_ci			is_queued = true;
11268c2ecf20Sopenharmony_ci			stats->num_amsdu++;
11278c2ecf20Sopenharmony_ci			while ((skb = skb_dequeue(&rxtid->q)))
11288c2ecf20Sopenharmony_ci				ath6kl_deliver_frames_to_nw_stack(agg_conn->dev,
11298c2ecf20Sopenharmony_ci								  skb);
11308c2ecf20Sopenharmony_ci		}
11318c2ecf20Sopenharmony_ci		return is_queued;
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	/* Check the incoming sequence no, if it's in the window */
11358c2ecf20Sopenharmony_ci	st = rxtid->seq_next;
11368c2ecf20Sopenharmony_ci	cur = seq_no;
11378c2ecf20Sopenharmony_ci	end = (st + rxtid->hold_q_sz-1) & ATH6KL_MAX_SEQ_NO;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	if (((st < end) && (cur < st || cur > end)) ||
11408c2ecf20Sopenharmony_ci	    ((st > end) && (cur > end) && (cur < st))) {
11418c2ecf20Sopenharmony_ci		extended_end = (end + rxtid->hold_q_sz - 1) &
11428c2ecf20Sopenharmony_ci			ATH6KL_MAX_SEQ_NO;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci		if (((end < extended_end) &&
11458c2ecf20Sopenharmony_ci		     (cur < end || cur > extended_end)) ||
11468c2ecf20Sopenharmony_ci		    ((end > extended_end) && (cur > extended_end) &&
11478c2ecf20Sopenharmony_ci		     (cur < end))) {
11488c2ecf20Sopenharmony_ci			aggr_deque_frms(agg_conn, tid, 0, 0);
11498c2ecf20Sopenharmony_ci			spin_lock_bh(&rxtid->lock);
11508c2ecf20Sopenharmony_ci			if (cur >= rxtid->hold_q_sz - 1)
11518c2ecf20Sopenharmony_ci				rxtid->seq_next = cur - (rxtid->hold_q_sz - 1);
11528c2ecf20Sopenharmony_ci			else
11538c2ecf20Sopenharmony_ci				rxtid->seq_next = ATH6KL_MAX_SEQ_NO -
11548c2ecf20Sopenharmony_ci						  (rxtid->hold_q_sz - 2 - cur);
11558c2ecf20Sopenharmony_ci			spin_unlock_bh(&rxtid->lock);
11568c2ecf20Sopenharmony_ci		} else {
11578c2ecf20Sopenharmony_ci			/*
11588c2ecf20Sopenharmony_ci			 * Dequeue only those frames that are outside the
11598c2ecf20Sopenharmony_ci			 * new shifted window.
11608c2ecf20Sopenharmony_ci			 */
11618c2ecf20Sopenharmony_ci			if (cur >= rxtid->hold_q_sz - 1)
11628c2ecf20Sopenharmony_ci				st = cur - (rxtid->hold_q_sz - 1);
11638c2ecf20Sopenharmony_ci			else
11648c2ecf20Sopenharmony_ci				st = ATH6KL_MAX_SEQ_NO -
11658c2ecf20Sopenharmony_ci					(rxtid->hold_q_sz - 2 - cur);
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci			aggr_deque_frms(agg_conn, tid, st, 0);
11688c2ecf20Sopenharmony_ci		}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci		stats->num_oow++;
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	idx = AGGR_WIN_IDX(seq_no, rxtid->hold_q_sz);
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	node = &rxtid->hold_q[idx];
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	spin_lock_bh(&rxtid->lock);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	/*
11808c2ecf20Sopenharmony_ci	 * Is the cur frame duplicate or something beyond our window(hold_q
11818c2ecf20Sopenharmony_ci	 * -> which is 2x, already)?
11828c2ecf20Sopenharmony_ci	 *
11838c2ecf20Sopenharmony_ci	 * 1. Duplicate is easy - drop incoming frame.
11848c2ecf20Sopenharmony_ci	 * 2. Not falling in current sliding window.
11858c2ecf20Sopenharmony_ci	 *  2a. is the frame_seq_no preceding current tid_seq_no?
11868c2ecf20Sopenharmony_ci	 *      -> drop the frame. perhaps sender did not get our ACK.
11878c2ecf20Sopenharmony_ci	 *         this is taken care of above.
11888c2ecf20Sopenharmony_ci	 *  2b. is the frame_seq_no beyond window(st, TID_WINDOW_SZ);
11898c2ecf20Sopenharmony_ci	 *      -> Taken care of it above, by moving window forward.
11908c2ecf20Sopenharmony_ci	 */
11918c2ecf20Sopenharmony_ci	dev_kfree_skb(node->skb);
11928c2ecf20Sopenharmony_ci	stats->num_dups++;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	node->skb = frame;
11958c2ecf20Sopenharmony_ci	is_queued = true;
11968c2ecf20Sopenharmony_ci	node->is_amsdu = is_amsdu;
11978c2ecf20Sopenharmony_ci	node->seq_no = seq_no;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	if (node->is_amsdu)
12008c2ecf20Sopenharmony_ci		stats->num_amsdu++;
12018c2ecf20Sopenharmony_ci	else
12028c2ecf20Sopenharmony_ci		stats->num_mpdu++;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	spin_unlock_bh(&rxtid->lock);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	aggr_deque_frms(agg_conn, tid, 0, 1);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	if (agg_conn->timer_scheduled)
12098c2ecf20Sopenharmony_ci		return is_queued;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	spin_lock_bh(&rxtid->lock);
12128c2ecf20Sopenharmony_ci	for (idx = 0; idx < rxtid->hold_q_sz; idx++) {
12138c2ecf20Sopenharmony_ci		if (rxtid->hold_q[idx].skb) {
12148c2ecf20Sopenharmony_ci			/*
12158c2ecf20Sopenharmony_ci			 * There is a frame in the queue and no
12168c2ecf20Sopenharmony_ci			 * timer so start a timer to ensure that
12178c2ecf20Sopenharmony_ci			 * the frame doesn't remain stuck
12188c2ecf20Sopenharmony_ci			 * forever.
12198c2ecf20Sopenharmony_ci			 */
12208c2ecf20Sopenharmony_ci			agg_conn->timer_scheduled = true;
12218c2ecf20Sopenharmony_ci			mod_timer(&agg_conn->timer,
12228c2ecf20Sopenharmony_ci				  (jiffies + (HZ * AGGR_RX_TIMEOUT) / 1000));
12238c2ecf20Sopenharmony_ci			rxtid->timer_mon = true;
12248c2ecf20Sopenharmony_ci			break;
12258c2ecf20Sopenharmony_ci		}
12268c2ecf20Sopenharmony_ci	}
12278c2ecf20Sopenharmony_ci	spin_unlock_bh(&rxtid->lock);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	return is_queued;
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_cistatic void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif,
12338c2ecf20Sopenharmony_ci						 struct ath6kl_sta *conn)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
12368c2ecf20Sopenharmony_ci	bool is_apsdq_empty, is_apsdq_empty_at_start;
12378c2ecf20Sopenharmony_ci	u32 num_frames_to_deliver, flags;
12388c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	/*
12418c2ecf20Sopenharmony_ci	 * If the APSD q for this STA is not empty, dequeue and
12428c2ecf20Sopenharmony_ci	 * send a pkt from the head of the q. Also update the
12438c2ecf20Sopenharmony_ci	 * More data bit in the WMI_DATA_HDR if there are
12448c2ecf20Sopenharmony_ci	 * more pkts for this STA in the APSD q.
12458c2ecf20Sopenharmony_ci	 * If there are no more pkts for this STA,
12468c2ecf20Sopenharmony_ci	 * update the APSD bitmap for this STA.
12478c2ecf20Sopenharmony_ci	 */
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	num_frames_to_deliver = (conn->apsd_info >> ATH6KL_APSD_NUM_OF_AC) &
12508c2ecf20Sopenharmony_ci						    ATH6KL_APSD_FRAME_MASK;
12518c2ecf20Sopenharmony_ci	/*
12528c2ecf20Sopenharmony_ci	 * Number of frames to send in a service period is
12538c2ecf20Sopenharmony_ci	 * indicated by the station
12548c2ecf20Sopenharmony_ci	 * in the QOS_INFO of the association request
12558c2ecf20Sopenharmony_ci	 * If it is zero, send all frames
12568c2ecf20Sopenharmony_ci	 */
12578c2ecf20Sopenharmony_ci	if (!num_frames_to_deliver)
12588c2ecf20Sopenharmony_ci		num_frames_to_deliver = ATH6KL_APSD_ALL_FRAME;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->psq_lock);
12618c2ecf20Sopenharmony_ci	is_apsdq_empty = skb_queue_empty(&conn->apsdq);
12628c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->psq_lock);
12638c2ecf20Sopenharmony_ci	is_apsdq_empty_at_start = is_apsdq_empty;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	while ((!is_apsdq_empty) && (num_frames_to_deliver)) {
12668c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->psq_lock);
12678c2ecf20Sopenharmony_ci		skb = skb_dequeue(&conn->apsdq);
12688c2ecf20Sopenharmony_ci		is_apsdq_empty = skb_queue_empty(&conn->apsdq);
12698c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->psq_lock);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci		/*
12728c2ecf20Sopenharmony_ci		 * Set the STA flag to Trigger delivery,
12738c2ecf20Sopenharmony_ci		 * so that the frame will go out
12748c2ecf20Sopenharmony_ci		 */
12758c2ecf20Sopenharmony_ci		conn->sta_flags |= STA_PS_APSD_TRIGGER;
12768c2ecf20Sopenharmony_ci		num_frames_to_deliver--;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		/* Last frame in the service period, set EOSP or queue empty */
12798c2ecf20Sopenharmony_ci		if ((is_apsdq_empty) || (!num_frames_to_deliver))
12808c2ecf20Sopenharmony_ci			conn->sta_flags |= STA_PS_APSD_EOSP;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci		ath6kl_data_tx(skb, vif->ndev);
12838c2ecf20Sopenharmony_ci		conn->sta_flags &= ~(STA_PS_APSD_TRIGGER);
12848c2ecf20Sopenharmony_ci		conn->sta_flags &= ~(STA_PS_APSD_EOSP);
12858c2ecf20Sopenharmony_ci	}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	if (is_apsdq_empty) {
12888c2ecf20Sopenharmony_ci		if (is_apsdq_empty_at_start)
12898c2ecf20Sopenharmony_ci			flags = WMI_AP_APSD_NO_DELIVERY_FRAMES;
12908c2ecf20Sopenharmony_ci		else
12918c2ecf20Sopenharmony_ci			flags = 0;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci		ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi,
12948c2ecf20Sopenharmony_ci					      vif->fw_vif_idx,
12958c2ecf20Sopenharmony_ci					      conn->aid, 0, flags);
12968c2ecf20Sopenharmony_ci	}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	return;
12998c2ecf20Sopenharmony_ci}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_civoid ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	struct ath6kl *ar = target->dev->ar;
13048c2ecf20Sopenharmony_ci	struct sk_buff *skb = packet->pkt_cntxt;
13058c2ecf20Sopenharmony_ci	struct wmi_rx_meta_v2 *meta;
13068c2ecf20Sopenharmony_ci	struct wmi_data_hdr *dhdr;
13078c2ecf20Sopenharmony_ci	int min_hdr_len;
13088c2ecf20Sopenharmony_ci	u8 meta_type, dot11_hdr = 0;
13098c2ecf20Sopenharmony_ci	u8 pad_before_data_start;
13108c2ecf20Sopenharmony_ci	int status = packet->status;
13118c2ecf20Sopenharmony_ci	enum htc_endpoint_id ept = packet->endpoint;
13128c2ecf20Sopenharmony_ci	bool is_amsdu, prev_ps, ps_state = false;
13138c2ecf20Sopenharmony_ci	bool trig_state = false;
13148c2ecf20Sopenharmony_ci	struct ath6kl_sta *conn = NULL;
13158c2ecf20Sopenharmony_ci	struct sk_buff *skb1 = NULL;
13168c2ecf20Sopenharmony_ci	struct ethhdr *datap = NULL;
13178c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif;
13188c2ecf20Sopenharmony_ci	struct aggr_info_conn *aggr_conn;
13198c2ecf20Sopenharmony_ci	u16 seq_no, offset;
13208c2ecf20Sopenharmony_ci	u8 tid, if_idx;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_RX,
13238c2ecf20Sopenharmony_ci		   "%s: ar=0x%p eid=%d, skb=0x%p, data=0x%p, len=0x%x status:%d",
13248c2ecf20Sopenharmony_ci		   __func__, ar, ept, skb, packet->buf,
13258c2ecf20Sopenharmony_ci		   packet->act_len, status);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	if (status || packet->act_len < HTC_HDR_LENGTH) {
13288c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
13298c2ecf20Sopenharmony_ci		return;
13308c2ecf20Sopenharmony_ci	}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
13338c2ecf20Sopenharmony_ci	skb_pull(skb, HTC_HDR_LENGTH);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
13368c2ecf20Sopenharmony_ci			skb->data, skb->len);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	if (ept == ar->ctrl_ep) {
13398c2ecf20Sopenharmony_ci		if (test_bit(WMI_ENABLED, &ar->flag)) {
13408c2ecf20Sopenharmony_ci			ath6kl_check_wow_status(ar);
13418c2ecf20Sopenharmony_ci			ath6kl_wmi_control_rx(ar->wmi, skb);
13428c2ecf20Sopenharmony_ci			return;
13438c2ecf20Sopenharmony_ci		}
13448c2ecf20Sopenharmony_ci		if_idx =
13458c2ecf20Sopenharmony_ci		wmi_cmd_hdr_get_if_idx((struct wmi_cmd_hdr *) skb->data);
13468c2ecf20Sopenharmony_ci	} else {
13478c2ecf20Sopenharmony_ci		if_idx =
13488c2ecf20Sopenharmony_ci		wmi_data_hdr_get_if_idx((struct wmi_data_hdr *) skb->data);
13498c2ecf20Sopenharmony_ci	}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	vif = ath6kl_get_vif_by_index(ar, if_idx);
13528c2ecf20Sopenharmony_ci	if (!vif) {
13538c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
13548c2ecf20Sopenharmony_ci		return;
13558c2ecf20Sopenharmony_ci	}
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	/*
13588c2ecf20Sopenharmony_ci	 * Take lock to protect buffer counts and adaptive power throughput
13598c2ecf20Sopenharmony_ci	 * state.
13608c2ecf20Sopenharmony_ci	 */
13618c2ecf20Sopenharmony_ci	spin_lock_bh(&vif->if_lock);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	vif->ndev->stats.rx_packets++;
13648c2ecf20Sopenharmony_ci	vif->ndev->stats.rx_bytes += packet->act_len;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	spin_unlock_bh(&vif->if_lock);
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	skb->dev = vif->ndev;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	if (!test_bit(WMI_ENABLED, &ar->flag)) {
13718c2ecf20Sopenharmony_ci		if (EPPING_ALIGNMENT_PAD > 0)
13728c2ecf20Sopenharmony_ci			skb_pull(skb, EPPING_ALIGNMENT_PAD);
13738c2ecf20Sopenharmony_ci		ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb);
13748c2ecf20Sopenharmony_ci		return;
13758c2ecf20Sopenharmony_ci	}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	ath6kl_check_wow_status(ar);
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) +
13808c2ecf20Sopenharmony_ci		      sizeof(struct ath6kl_llc_snap_hdr);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	dhdr = (struct wmi_data_hdr *) skb->data;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	/*
13858c2ecf20Sopenharmony_ci	 * In the case of AP mode we may receive NULL data frames
13868c2ecf20Sopenharmony_ci	 * that do not have LLC hdr. They are 16 bytes in size.
13878c2ecf20Sopenharmony_ci	 * Allow these frames in the AP mode.
13888c2ecf20Sopenharmony_ci	 */
13898c2ecf20Sopenharmony_ci	if (vif->nw_type != AP_NETWORK &&
13908c2ecf20Sopenharmony_ci	    ((packet->act_len < min_hdr_len) ||
13918c2ecf20Sopenharmony_ci	     (packet->act_len > WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH))) {
13928c2ecf20Sopenharmony_ci		ath6kl_info("frame len is too short or too long\n");
13938c2ecf20Sopenharmony_ci		vif->ndev->stats.rx_errors++;
13948c2ecf20Sopenharmony_ci		vif->ndev->stats.rx_length_errors++;
13958c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
13968c2ecf20Sopenharmony_ci		return;
13978c2ecf20Sopenharmony_ci	}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	pad_before_data_start =
14008c2ecf20Sopenharmony_ci		(le16_to_cpu(dhdr->info3) >> WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT)
14018c2ecf20Sopenharmony_ci			& WMI_DATA_HDR_PAD_BEFORE_DATA_MASK;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	/* Get the Power save state of the STA */
14048c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
14058c2ecf20Sopenharmony_ci		meta_type = wmi_data_hdr_get_meta(dhdr);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci		ps_state = !!((dhdr->info >> WMI_DATA_HDR_PS_SHIFT) &
14088c2ecf20Sopenharmony_ci			      WMI_DATA_HDR_PS_MASK);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci		offset = sizeof(struct wmi_data_hdr) + pad_before_data_start;
14118c2ecf20Sopenharmony_ci		trig_state = !!(le16_to_cpu(dhdr->info3) & WMI_DATA_HDR_TRIG);
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci		switch (meta_type) {
14148c2ecf20Sopenharmony_ci		case 0:
14158c2ecf20Sopenharmony_ci			break;
14168c2ecf20Sopenharmony_ci		case WMI_META_VERSION_1:
14178c2ecf20Sopenharmony_ci			offset += sizeof(struct wmi_rx_meta_v1);
14188c2ecf20Sopenharmony_ci			break;
14198c2ecf20Sopenharmony_ci		case WMI_META_VERSION_2:
14208c2ecf20Sopenharmony_ci			offset += sizeof(struct wmi_rx_meta_v2);
14218c2ecf20Sopenharmony_ci			break;
14228c2ecf20Sopenharmony_ci		default:
14238c2ecf20Sopenharmony_ci			break;
14248c2ecf20Sopenharmony_ci		}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci		datap = (struct ethhdr *) (skb->data + offset);
14278c2ecf20Sopenharmony_ci		conn = ath6kl_find_sta(vif, datap->h_source);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci		if (!conn) {
14308c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
14318c2ecf20Sopenharmony_ci			return;
14328c2ecf20Sopenharmony_ci		}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci		/*
14358c2ecf20Sopenharmony_ci		 * If there is a change in PS state of the STA,
14368c2ecf20Sopenharmony_ci		 * take appropriate steps:
14378c2ecf20Sopenharmony_ci		 *
14388c2ecf20Sopenharmony_ci		 * 1. If Sleep-->Awake, flush the psq for the STA
14398c2ecf20Sopenharmony_ci		 *    Clear the PVB for the STA.
14408c2ecf20Sopenharmony_ci		 * 2. If Awake-->Sleep, Starting queueing frames
14418c2ecf20Sopenharmony_ci		 *    the STA.
14428c2ecf20Sopenharmony_ci		 */
14438c2ecf20Sopenharmony_ci		prev_ps = !!(conn->sta_flags & STA_PS_SLEEP);
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci		if (ps_state)
14468c2ecf20Sopenharmony_ci			conn->sta_flags |= STA_PS_SLEEP;
14478c2ecf20Sopenharmony_ci		else
14488c2ecf20Sopenharmony_ci			conn->sta_flags &= ~STA_PS_SLEEP;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci		/* Accept trigger only when the station is in sleep */
14518c2ecf20Sopenharmony_ci		if ((conn->sta_flags & STA_PS_SLEEP) && trig_state)
14528c2ecf20Sopenharmony_ci			ath6kl_uapsd_trigger_frame_rx(vif, conn);
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci		if (prev_ps ^ !!(conn->sta_flags & STA_PS_SLEEP)) {
14558c2ecf20Sopenharmony_ci			if (!(conn->sta_flags & STA_PS_SLEEP)) {
14568c2ecf20Sopenharmony_ci				struct sk_buff *skbuff = NULL;
14578c2ecf20Sopenharmony_ci				bool is_apsdq_empty;
14588c2ecf20Sopenharmony_ci				struct ath6kl_mgmt_buff *mgmt;
14598c2ecf20Sopenharmony_ci				u8 idx;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci				spin_lock_bh(&conn->psq_lock);
14628c2ecf20Sopenharmony_ci				while (conn->mgmt_psq_len > 0) {
14638c2ecf20Sopenharmony_ci					mgmt = list_first_entry(
14648c2ecf20Sopenharmony_ci							&conn->mgmt_psq,
14658c2ecf20Sopenharmony_ci							struct ath6kl_mgmt_buff,
14668c2ecf20Sopenharmony_ci							list);
14678c2ecf20Sopenharmony_ci					list_del(&mgmt->list);
14688c2ecf20Sopenharmony_ci					conn->mgmt_psq_len--;
14698c2ecf20Sopenharmony_ci					spin_unlock_bh(&conn->psq_lock);
14708c2ecf20Sopenharmony_ci					idx = vif->fw_vif_idx;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci					ath6kl_wmi_send_mgmt_cmd(ar->wmi,
14738c2ecf20Sopenharmony_ci								 idx,
14748c2ecf20Sopenharmony_ci								 mgmt->id,
14758c2ecf20Sopenharmony_ci								 mgmt->freq,
14768c2ecf20Sopenharmony_ci								 mgmt->wait,
14778c2ecf20Sopenharmony_ci								 mgmt->buf,
14788c2ecf20Sopenharmony_ci								 mgmt->len,
14798c2ecf20Sopenharmony_ci								 mgmt->no_cck);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci					kfree(mgmt);
14828c2ecf20Sopenharmony_ci					spin_lock_bh(&conn->psq_lock);
14838c2ecf20Sopenharmony_ci				}
14848c2ecf20Sopenharmony_ci				conn->mgmt_psq_len = 0;
14858c2ecf20Sopenharmony_ci				while ((skbuff = skb_dequeue(&conn->psq))) {
14868c2ecf20Sopenharmony_ci					spin_unlock_bh(&conn->psq_lock);
14878c2ecf20Sopenharmony_ci					ath6kl_data_tx(skbuff, vif->ndev);
14888c2ecf20Sopenharmony_ci					spin_lock_bh(&conn->psq_lock);
14898c2ecf20Sopenharmony_ci				}
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci				is_apsdq_empty = skb_queue_empty(&conn->apsdq);
14928c2ecf20Sopenharmony_ci				while ((skbuff = skb_dequeue(&conn->apsdq))) {
14938c2ecf20Sopenharmony_ci					spin_unlock_bh(&conn->psq_lock);
14948c2ecf20Sopenharmony_ci					ath6kl_data_tx(skbuff, vif->ndev);
14958c2ecf20Sopenharmony_ci					spin_lock_bh(&conn->psq_lock);
14968c2ecf20Sopenharmony_ci				}
14978c2ecf20Sopenharmony_ci				spin_unlock_bh(&conn->psq_lock);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci				if (!is_apsdq_empty)
15008c2ecf20Sopenharmony_ci					ath6kl_wmi_set_apsd_bfrd_traf(
15018c2ecf20Sopenharmony_ci							ar->wmi,
15028c2ecf20Sopenharmony_ci							vif->fw_vif_idx,
15038c2ecf20Sopenharmony_ci							conn->aid, 0, 0);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci				/* Clear the PVB for this STA */
15068c2ecf20Sopenharmony_ci				ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
15078c2ecf20Sopenharmony_ci						       conn->aid, 0);
15088c2ecf20Sopenharmony_ci			}
15098c2ecf20Sopenharmony_ci		}
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci		/* drop NULL data frames here */
15128c2ecf20Sopenharmony_ci		if ((packet->act_len < min_hdr_len) ||
15138c2ecf20Sopenharmony_ci		    (packet->act_len >
15148c2ecf20Sopenharmony_ci		     WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH)) {
15158c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
15168c2ecf20Sopenharmony_ci			return;
15178c2ecf20Sopenharmony_ci		}
15188c2ecf20Sopenharmony_ci	}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	is_amsdu = wmi_data_hdr_is_amsdu(dhdr) ? true : false;
15218c2ecf20Sopenharmony_ci	tid = wmi_data_hdr_get_up(dhdr);
15228c2ecf20Sopenharmony_ci	seq_no = wmi_data_hdr_get_seqno(dhdr);
15238c2ecf20Sopenharmony_ci	meta_type = wmi_data_hdr_get_meta(dhdr);
15248c2ecf20Sopenharmony_ci	dot11_hdr = wmi_data_hdr_get_dot11(dhdr);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(struct wmi_data_hdr));
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	switch (meta_type) {
15298c2ecf20Sopenharmony_ci	case WMI_META_VERSION_1:
15308c2ecf20Sopenharmony_ci		skb_pull(skb, sizeof(struct wmi_rx_meta_v1));
15318c2ecf20Sopenharmony_ci		break;
15328c2ecf20Sopenharmony_ci	case WMI_META_VERSION_2:
15338c2ecf20Sopenharmony_ci		meta = (struct wmi_rx_meta_v2 *) skb->data;
15348c2ecf20Sopenharmony_ci		if (meta->csum_flags & 0x1) {
15358c2ecf20Sopenharmony_ci			skb->ip_summed = CHECKSUM_COMPLETE;
15368c2ecf20Sopenharmony_ci			skb->csum = (__force __wsum) meta->csum;
15378c2ecf20Sopenharmony_ci		}
15388c2ecf20Sopenharmony_ci		skb_pull(skb, sizeof(struct wmi_rx_meta_v2));
15398c2ecf20Sopenharmony_ci		break;
15408c2ecf20Sopenharmony_ci	default:
15418c2ecf20Sopenharmony_ci		break;
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	skb_pull(skb, pad_before_data_start);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	if (dot11_hdr)
15478c2ecf20Sopenharmony_ci		status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb);
15488c2ecf20Sopenharmony_ci	else if (!is_amsdu)
15498c2ecf20Sopenharmony_ci		status = ath6kl_wmi_dot3_2_dix(skb);
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	if (status) {
15528c2ecf20Sopenharmony_ci		/*
15538c2ecf20Sopenharmony_ci		 * Drop frames that could not be processed (lack of
15548c2ecf20Sopenharmony_ci		 * memory, etc.)
15558c2ecf20Sopenharmony_ci		 */
15568c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
15578c2ecf20Sopenharmony_ci		return;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	if (!(vif->ndev->flags & IFF_UP)) {
15618c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
15628c2ecf20Sopenharmony_ci		return;
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
15668c2ecf20Sopenharmony_ci		datap = (struct ethhdr *) skb->data;
15678c2ecf20Sopenharmony_ci		if (is_multicast_ether_addr(datap->h_dest))
15688c2ecf20Sopenharmony_ci			/*
15698c2ecf20Sopenharmony_ci			 * Bcast/Mcast frames should be sent to the
15708c2ecf20Sopenharmony_ci			 * OS stack as well as on the air.
15718c2ecf20Sopenharmony_ci			 */
15728c2ecf20Sopenharmony_ci			skb1 = skb_copy(skb, GFP_ATOMIC);
15738c2ecf20Sopenharmony_ci		else {
15748c2ecf20Sopenharmony_ci			/*
15758c2ecf20Sopenharmony_ci			 * Search for a connected STA with dstMac
15768c2ecf20Sopenharmony_ci			 * as the Mac address. If found send the
15778c2ecf20Sopenharmony_ci			 * frame to it on the air else send the
15788c2ecf20Sopenharmony_ci			 * frame up the stack.
15798c2ecf20Sopenharmony_ci			 */
15808c2ecf20Sopenharmony_ci			conn = ath6kl_find_sta(vif, datap->h_dest);
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci			if (conn && ar->intra_bss) {
15838c2ecf20Sopenharmony_ci				skb1 = skb;
15848c2ecf20Sopenharmony_ci				skb = NULL;
15858c2ecf20Sopenharmony_ci			} else if (conn && !ar->intra_bss) {
15868c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
15878c2ecf20Sopenharmony_ci				skb = NULL;
15888c2ecf20Sopenharmony_ci			}
15898c2ecf20Sopenharmony_ci		}
15908c2ecf20Sopenharmony_ci		if (skb1)
15918c2ecf20Sopenharmony_ci			ath6kl_data_tx(skb1, vif->ndev);
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci		if (skb == NULL) {
15948c2ecf20Sopenharmony_ci			/* nothing to deliver up the stack */
15958c2ecf20Sopenharmony_ci			return;
15968c2ecf20Sopenharmony_ci		}
15978c2ecf20Sopenharmony_ci	}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	datap = (struct ethhdr *) skb->data;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	if (is_unicast_ether_addr(datap->h_dest)) {
16028c2ecf20Sopenharmony_ci		if (vif->nw_type == AP_NETWORK) {
16038c2ecf20Sopenharmony_ci			conn = ath6kl_find_sta(vif, datap->h_source);
16048c2ecf20Sopenharmony_ci			if (!conn)
16058c2ecf20Sopenharmony_ci				return;
16068c2ecf20Sopenharmony_ci			aggr_conn = conn->aggr_conn;
16078c2ecf20Sopenharmony_ci		} else {
16088c2ecf20Sopenharmony_ci			aggr_conn = vif->aggr_cntxt->aggr_conn;
16098c2ecf20Sopenharmony_ci		}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci		if (aggr_process_recv_frm(aggr_conn, tid, seq_no,
16128c2ecf20Sopenharmony_ci					  is_amsdu, skb)) {
16138c2ecf20Sopenharmony_ci			/* aggregation code will handle the skb */
16148c2ecf20Sopenharmony_ci			return;
16158c2ecf20Sopenharmony_ci		}
16168c2ecf20Sopenharmony_ci	} else if (!is_broadcast_ether_addr(datap->h_dest)) {
16178c2ecf20Sopenharmony_ci		vif->ndev->stats.multicast++;
16188c2ecf20Sopenharmony_ci	}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb);
16218c2ecf20Sopenharmony_ci}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_cistatic void aggr_timeout(struct timer_list *t)
16248c2ecf20Sopenharmony_ci{
16258c2ecf20Sopenharmony_ci	u8 i, j;
16268c2ecf20Sopenharmony_ci	struct aggr_info_conn *aggr_conn = from_timer(aggr_conn, t, timer);
16278c2ecf20Sopenharmony_ci	struct rxtid *rxtid;
16288c2ecf20Sopenharmony_ci	struct rxtid_stats *stats;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_OF_TIDS; i++) {
16318c2ecf20Sopenharmony_ci		rxtid = &aggr_conn->rx_tid[i];
16328c2ecf20Sopenharmony_ci		stats = &aggr_conn->stat[i];
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci		if (!rxtid->aggr || !rxtid->timer_mon)
16358c2ecf20Sopenharmony_ci			continue;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci		stats->num_timeouts++;
16388c2ecf20Sopenharmony_ci		ath6kl_dbg(ATH6KL_DBG_AGGR,
16398c2ecf20Sopenharmony_ci			   "aggr timeout (st %d end %d)\n",
16408c2ecf20Sopenharmony_ci			   rxtid->seq_next,
16418c2ecf20Sopenharmony_ci			   ((rxtid->seq_next + rxtid->hold_q_sz-1) &
16428c2ecf20Sopenharmony_ci			    ATH6KL_MAX_SEQ_NO));
16438c2ecf20Sopenharmony_ci		aggr_deque_frms(aggr_conn, i, 0, 0);
16448c2ecf20Sopenharmony_ci	}
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	aggr_conn->timer_scheduled = false;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_OF_TIDS; i++) {
16498c2ecf20Sopenharmony_ci		rxtid = &aggr_conn->rx_tid[i];
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci		if (rxtid->aggr && rxtid->hold_q) {
16528c2ecf20Sopenharmony_ci			spin_lock_bh(&rxtid->lock);
16538c2ecf20Sopenharmony_ci			for (j = 0; j < rxtid->hold_q_sz; j++) {
16548c2ecf20Sopenharmony_ci				if (rxtid->hold_q[j].skb) {
16558c2ecf20Sopenharmony_ci					aggr_conn->timer_scheduled = true;
16568c2ecf20Sopenharmony_ci					rxtid->timer_mon = true;
16578c2ecf20Sopenharmony_ci					break;
16588c2ecf20Sopenharmony_ci				}
16598c2ecf20Sopenharmony_ci			}
16608c2ecf20Sopenharmony_ci			spin_unlock_bh(&rxtid->lock);
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci			if (j >= rxtid->hold_q_sz)
16638c2ecf20Sopenharmony_ci				rxtid->timer_mon = false;
16648c2ecf20Sopenharmony_ci		}
16658c2ecf20Sopenharmony_ci	}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	if (aggr_conn->timer_scheduled)
16688c2ecf20Sopenharmony_ci		mod_timer(&aggr_conn->timer,
16698c2ecf20Sopenharmony_ci			  jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT));
16708c2ecf20Sopenharmony_ci}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_cistatic void aggr_delete_tid_state(struct aggr_info_conn *aggr_conn, u8 tid)
16738c2ecf20Sopenharmony_ci{
16748c2ecf20Sopenharmony_ci	struct rxtid *rxtid;
16758c2ecf20Sopenharmony_ci	struct rxtid_stats *stats;
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	if (!aggr_conn || tid >= NUM_OF_TIDS)
16788c2ecf20Sopenharmony_ci		return;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	rxtid = &aggr_conn->rx_tid[tid];
16818c2ecf20Sopenharmony_ci	stats = &aggr_conn->stat[tid];
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	if (rxtid->aggr)
16848c2ecf20Sopenharmony_ci		aggr_deque_frms(aggr_conn, tid, 0, 0);
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	rxtid->aggr = false;
16878c2ecf20Sopenharmony_ci	rxtid->timer_mon = false;
16888c2ecf20Sopenharmony_ci	rxtid->win_sz = 0;
16898c2ecf20Sopenharmony_ci	rxtid->seq_next = 0;
16908c2ecf20Sopenharmony_ci	rxtid->hold_q_sz = 0;
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	kfree(rxtid->hold_q);
16938c2ecf20Sopenharmony_ci	rxtid->hold_q = NULL;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	memset(stats, 0, sizeof(struct rxtid_stats));
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_civoid aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no,
16998c2ecf20Sopenharmony_ci			     u8 win_sz)
17008c2ecf20Sopenharmony_ci{
17018c2ecf20Sopenharmony_ci	struct ath6kl_sta *sta;
17028c2ecf20Sopenharmony_ci	struct aggr_info_conn *aggr_conn = NULL;
17038c2ecf20Sopenharmony_ci	struct rxtid *rxtid;
17048c2ecf20Sopenharmony_ci	u16 hold_q_size;
17058c2ecf20Sopenharmony_ci	u8 tid, aid;
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
17088c2ecf20Sopenharmony_ci		aid = ath6kl_get_aid(tid_mux);
17098c2ecf20Sopenharmony_ci		sta = ath6kl_find_sta_by_aid(vif->ar, aid);
17108c2ecf20Sopenharmony_ci		if (sta)
17118c2ecf20Sopenharmony_ci			aggr_conn = sta->aggr_conn;
17128c2ecf20Sopenharmony_ci	} else {
17138c2ecf20Sopenharmony_ci		aggr_conn = vif->aggr_cntxt->aggr_conn;
17148c2ecf20Sopenharmony_ci	}
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	if (!aggr_conn)
17178c2ecf20Sopenharmony_ci		return;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	tid = ath6kl_get_tid(tid_mux);
17208c2ecf20Sopenharmony_ci	if (tid >= NUM_OF_TIDS)
17218c2ecf20Sopenharmony_ci		return;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	rxtid = &aggr_conn->rx_tid[tid];
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX)
17268c2ecf20Sopenharmony_ci		ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
17278c2ecf20Sopenharmony_ci			   __func__, win_sz, tid);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	if (rxtid->aggr)
17308c2ecf20Sopenharmony_ci		aggr_delete_tid_state(aggr_conn, tid);
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	rxtid->seq_next = seq_no;
17338c2ecf20Sopenharmony_ci	hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q);
17348c2ecf20Sopenharmony_ci	rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL);
17358c2ecf20Sopenharmony_ci	if (!rxtid->hold_q)
17368c2ecf20Sopenharmony_ci		return;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	rxtid->win_sz = win_sz;
17398c2ecf20Sopenharmony_ci	rxtid->hold_q_sz = TID_WINDOW_SZ(win_sz);
17408c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&rxtid->q))
17418c2ecf20Sopenharmony_ci		return;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	rxtid->aggr = true;
17448c2ecf20Sopenharmony_ci}
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_civoid aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info,
17478c2ecf20Sopenharmony_ci		    struct aggr_info_conn *aggr_conn)
17488c2ecf20Sopenharmony_ci{
17498c2ecf20Sopenharmony_ci	struct rxtid *rxtid;
17508c2ecf20Sopenharmony_ci	u8 i;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	aggr_conn->aggr_sz = AGGR_SZ_DEFAULT;
17538c2ecf20Sopenharmony_ci	aggr_conn->dev = vif->ndev;
17548c2ecf20Sopenharmony_ci	timer_setup(&aggr_conn->timer, aggr_timeout, 0);
17558c2ecf20Sopenharmony_ci	aggr_conn->aggr_info = aggr_info;
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	aggr_conn->timer_scheduled = false;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_OF_TIDS; i++) {
17608c2ecf20Sopenharmony_ci		rxtid = &aggr_conn->rx_tid[i];
17618c2ecf20Sopenharmony_ci		rxtid->aggr = false;
17628c2ecf20Sopenharmony_ci		rxtid->timer_mon = false;
17638c2ecf20Sopenharmony_ci		skb_queue_head_init(&rxtid->q);
17648c2ecf20Sopenharmony_ci		spin_lock_init(&rxtid->lock);
17658c2ecf20Sopenharmony_ci	}
17668c2ecf20Sopenharmony_ci}
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_cistruct aggr_info *aggr_init(struct ath6kl_vif *vif)
17698c2ecf20Sopenharmony_ci{
17708c2ecf20Sopenharmony_ci	struct aggr_info *p_aggr = NULL;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	p_aggr = kzalloc(sizeof(struct aggr_info), GFP_KERNEL);
17738c2ecf20Sopenharmony_ci	if (!p_aggr) {
17748c2ecf20Sopenharmony_ci		ath6kl_err("failed to alloc memory for aggr_node\n");
17758c2ecf20Sopenharmony_ci		return NULL;
17768c2ecf20Sopenharmony_ci	}
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	p_aggr->aggr_conn = kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL);
17798c2ecf20Sopenharmony_ci	if (!p_aggr->aggr_conn) {
17808c2ecf20Sopenharmony_ci		ath6kl_err("failed to alloc memory for connection specific aggr info\n");
17818c2ecf20Sopenharmony_ci		kfree(p_aggr);
17828c2ecf20Sopenharmony_ci		return NULL;
17838c2ecf20Sopenharmony_ci	}
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	aggr_conn_init(vif, p_aggr, p_aggr->aggr_conn);
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	skb_queue_head_init(&p_aggr->rx_amsdu_freeq);
17888c2ecf20Sopenharmony_ci	ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq, AGGR_NUM_OF_FREE_NETBUFS);
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	return p_aggr;
17918c2ecf20Sopenharmony_ci}
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_civoid aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid_mux)
17948c2ecf20Sopenharmony_ci{
17958c2ecf20Sopenharmony_ci	struct ath6kl_sta *sta;
17968c2ecf20Sopenharmony_ci	struct rxtid *rxtid;
17978c2ecf20Sopenharmony_ci	struct aggr_info_conn *aggr_conn = NULL;
17988c2ecf20Sopenharmony_ci	u8 tid, aid;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
18018c2ecf20Sopenharmony_ci		aid = ath6kl_get_aid(tid_mux);
18028c2ecf20Sopenharmony_ci		sta = ath6kl_find_sta_by_aid(vif->ar, aid);
18038c2ecf20Sopenharmony_ci		if (sta)
18048c2ecf20Sopenharmony_ci			aggr_conn = sta->aggr_conn;
18058c2ecf20Sopenharmony_ci	} else {
18068c2ecf20Sopenharmony_ci		aggr_conn = vif->aggr_cntxt->aggr_conn;
18078c2ecf20Sopenharmony_ci	}
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	if (!aggr_conn)
18108c2ecf20Sopenharmony_ci		return;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	tid = ath6kl_get_tid(tid_mux);
18138c2ecf20Sopenharmony_ci	if (tid >= NUM_OF_TIDS)
18148c2ecf20Sopenharmony_ci		return;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	rxtid = &aggr_conn->rx_tid[tid];
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (rxtid->aggr)
18198c2ecf20Sopenharmony_ci		aggr_delete_tid_state(aggr_conn, tid);
18208c2ecf20Sopenharmony_ci}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_civoid aggr_reset_state(struct aggr_info_conn *aggr_conn)
18238c2ecf20Sopenharmony_ci{
18248c2ecf20Sopenharmony_ci	u8 tid;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	if (!aggr_conn)
18278c2ecf20Sopenharmony_ci		return;
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	if (aggr_conn->timer_scheduled) {
18308c2ecf20Sopenharmony_ci		del_timer(&aggr_conn->timer);
18318c2ecf20Sopenharmony_ci		aggr_conn->timer_scheduled = false;
18328c2ecf20Sopenharmony_ci	}
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	for (tid = 0; tid < NUM_OF_TIDS; tid++)
18358c2ecf20Sopenharmony_ci		aggr_delete_tid_state(aggr_conn, tid);
18368c2ecf20Sopenharmony_ci}
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci/* clean up our amsdu buffer list */
18398c2ecf20Sopenharmony_civoid ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar)
18408c2ecf20Sopenharmony_ci{
18418c2ecf20Sopenharmony_ci	struct htc_packet *packet, *tmp_pkt;
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->lock);
18448c2ecf20Sopenharmony_ci	if (list_empty(&ar->amsdu_rx_buffer_queue)) {
18458c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->lock);
18468c2ecf20Sopenharmony_ci		return;
18478c2ecf20Sopenharmony_ci	}
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	list_for_each_entry_safe(packet, tmp_pkt, &ar->amsdu_rx_buffer_queue,
18508c2ecf20Sopenharmony_ci				 list) {
18518c2ecf20Sopenharmony_ci		list_del(&packet->list);
18528c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->lock);
18538c2ecf20Sopenharmony_ci		dev_kfree_skb(packet->pkt_cntxt);
18548c2ecf20Sopenharmony_ci		spin_lock_bh(&ar->lock);
18558c2ecf20Sopenharmony_ci	}
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->lock);
18588c2ecf20Sopenharmony_ci}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_civoid aggr_module_destroy(struct aggr_info *aggr_info)
18618c2ecf20Sopenharmony_ci{
18628c2ecf20Sopenharmony_ci	if (!aggr_info)
18638c2ecf20Sopenharmony_ci		return;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	aggr_reset_state(aggr_info->aggr_conn);
18668c2ecf20Sopenharmony_ci	skb_queue_purge(&aggr_info->rx_amsdu_freeq);
18678c2ecf20Sopenharmony_ci	kfree(aggr_info->aggr_conn);
18688c2ecf20Sopenharmony_ci	kfree(aggr_info);
18698c2ecf20Sopenharmony_ci}
1870