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 "hif-ops.h"
228c2ecf20Sopenharmony_ci#include "cfg80211.h"
238c2ecf20Sopenharmony_ci#include "target.h"
248c2ecf20Sopenharmony_ci#include "debug.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
298c2ecf20Sopenharmony_ci	struct ath6kl_sta *conn = NULL;
308c2ecf20Sopenharmony_ci	u8 i, max_conn;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(node_addr))
338c2ecf20Sopenharmony_ci		return NULL;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	max_conn = (vif->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	for (i = 0; i < max_conn; i++) {
388c2ecf20Sopenharmony_ci		if (memcmp(node_addr, ar->sta_list[i].mac, ETH_ALEN) == 0) {
398c2ecf20Sopenharmony_ci			conn = &ar->sta_list[i];
408c2ecf20Sopenharmony_ci			break;
418c2ecf20Sopenharmony_ci		}
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return conn;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct ath6kl_sta *conn = NULL;
508c2ecf20Sopenharmony_ci	u8 ctr;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
538c2ecf20Sopenharmony_ci		if (ar->sta_list[ctr].aid == aid) {
548c2ecf20Sopenharmony_ci			conn = &ar->sta_list[ctr];
558c2ecf20Sopenharmony_ci			break;
568c2ecf20Sopenharmony_ci		}
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	return conn;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid,
628c2ecf20Sopenharmony_ci			       u8 *wpaie, size_t ielen, u8 keymgmt,
638c2ecf20Sopenharmony_ci			       u8 ucipher, u8 auth, u8 apsd_info)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
668c2ecf20Sopenharmony_ci	struct ath6kl_sta *sta;
678c2ecf20Sopenharmony_ci	u8 free_slot;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	free_slot = aid - 1;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	sta = &ar->sta_list[free_slot];
728c2ecf20Sopenharmony_ci	memcpy(sta->mac, mac, ETH_ALEN);
738c2ecf20Sopenharmony_ci	if (ielen <= ATH6KL_MAX_IE)
748c2ecf20Sopenharmony_ci		memcpy(sta->wpa_ie, wpaie, ielen);
758c2ecf20Sopenharmony_ci	sta->aid = aid;
768c2ecf20Sopenharmony_ci	sta->keymgmt = keymgmt;
778c2ecf20Sopenharmony_ci	sta->ucipher = ucipher;
788c2ecf20Sopenharmony_ci	sta->auth = auth;
798c2ecf20Sopenharmony_ci	sta->apsd_info = apsd_info;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ar->sta_list_index = ar->sta_list_index | (1 << free_slot);
828c2ecf20Sopenharmony_ci	ar->ap_stats.sta[free_slot].aid = cpu_to_le32(aid);
838c2ecf20Sopenharmony_ci	aggr_conn_init(vif, vif->aggr_cntxt, sta->aggr_conn);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct ath6kl_sta *sta = &ar->sta_list[i];
898c2ecf20Sopenharmony_ci	struct ath6kl_mgmt_buff *entry, *tmp;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* empty the queued pkts in the PS queue if any */
928c2ecf20Sopenharmony_ci	spin_lock_bh(&sta->psq_lock);
938c2ecf20Sopenharmony_ci	skb_queue_purge(&sta->psq);
948c2ecf20Sopenharmony_ci	skb_queue_purge(&sta->apsdq);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (sta->mgmt_psq_len != 0) {
978c2ecf20Sopenharmony_ci		list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) {
988c2ecf20Sopenharmony_ci			kfree(entry);
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&sta->mgmt_psq);
1018c2ecf20Sopenharmony_ci		sta->mgmt_psq_len = 0;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	spin_unlock_bh(&sta->psq_lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	memset(&ar->ap_stats.sta[sta->aid - 1], 0,
1078c2ecf20Sopenharmony_ci	       sizeof(struct wmi_per_sta_stat));
1088c2ecf20Sopenharmony_ci	eth_zero_addr(sta->mac);
1098c2ecf20Sopenharmony_ci	memset(sta->wpa_ie, 0, ATH6KL_MAX_IE);
1108c2ecf20Sopenharmony_ci	sta->aid = 0;
1118c2ecf20Sopenharmony_ci	sta->sta_flags = 0;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	ar->sta_list_index = ar->sta_list_index & ~(1 << i);
1148c2ecf20Sopenharmony_ci	aggr_reset_state(sta->aggr_conn);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic u8 ath6kl_remove_sta(struct ath6kl *ar, u8 *mac, u16 reason)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	u8 i, removed = 0;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(mac))
1228c2ecf20Sopenharmony_ci		return removed;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (is_broadcast_ether_addr(mac)) {
1258c2ecf20Sopenharmony_ci		ath6kl_dbg(ATH6KL_DBG_TRC, "deleting all station\n");
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		for (i = 0; i < AP_MAX_NUM_STA; i++) {
1288c2ecf20Sopenharmony_ci			if (!is_zero_ether_addr(ar->sta_list[i].mac)) {
1298c2ecf20Sopenharmony_ci				ath6kl_sta_cleanup(ar, i);
1308c2ecf20Sopenharmony_ci				removed = 1;
1318c2ecf20Sopenharmony_ci			}
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci	} else {
1348c2ecf20Sopenharmony_ci		for (i = 0; i < AP_MAX_NUM_STA; i++) {
1358c2ecf20Sopenharmony_ci			if (memcmp(ar->sta_list[i].mac, mac, ETH_ALEN) == 0) {
1368c2ecf20Sopenharmony_ci				ath6kl_dbg(ATH6KL_DBG_TRC,
1378c2ecf20Sopenharmony_ci					   "deleting station %pM aid=%d reason=%d\n",
1388c2ecf20Sopenharmony_ci					   mac, ar->sta_list[i].aid, reason);
1398c2ecf20Sopenharmony_ci				ath6kl_sta_cleanup(ar, i);
1408c2ecf20Sopenharmony_ci				removed = 1;
1418c2ecf20Sopenharmony_ci				break;
1428c2ecf20Sopenharmony_ci			}
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return removed;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cienum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct ath6kl *ar = devt;
1528c2ecf20Sopenharmony_ci	return ar->ac2ep_map[ac];
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistruct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct ath6kl_cookie *cookie;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	cookie = ar->cookie_list;
1608c2ecf20Sopenharmony_ci	if (cookie != NULL) {
1618c2ecf20Sopenharmony_ci		ar->cookie_list = cookie->arc_list_next;
1628c2ecf20Sopenharmony_ci		ar->cookie_count--;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return cookie;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_civoid ath6kl_cookie_init(struct ath6kl *ar)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	u32 i;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	ar->cookie_list = NULL;
1738c2ecf20Sopenharmony_ci	ar->cookie_count = 0;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	memset(ar->cookie_mem, 0, sizeof(ar->cookie_mem));
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_COOKIE_NUM; i++)
1788c2ecf20Sopenharmony_ci		ath6kl_free_cookie(ar, &ar->cookie_mem[i]);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_civoid ath6kl_cookie_cleanup(struct ath6kl *ar)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	ar->cookie_list = NULL;
1848c2ecf20Sopenharmony_ci	ar->cookie_count = 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_civoid ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	/* Insert first */
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (!ar || !cookie)
1928c2ecf20Sopenharmony_ci		return;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	cookie->arc_list_next = ar->cookie_list;
1958c2ecf20Sopenharmony_ci	ar->cookie_list = cookie;
1968c2ecf20Sopenharmony_ci	ar->cookie_count++;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/*
2008c2ecf20Sopenharmony_ci * Read from the hardware through its diagnostic window. No cooperation
2018c2ecf20Sopenharmony_ci * from the firmware is required for this.
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_ciint ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ret = ath6kl_hif_diag_read32(ar, address, value);
2088c2ecf20Sopenharmony_ci	if (ret) {
2098c2ecf20Sopenharmony_ci		ath6kl_warn("failed to read32 through diagnose window: %d\n",
2108c2ecf20Sopenharmony_ci			    ret);
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/*
2188c2ecf20Sopenharmony_ci * Write to the ATH6KL through its diagnostic window. No cooperation from
2198c2ecf20Sopenharmony_ci * the Target is required for this.
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_ciint ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	int ret;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	ret = ath6kl_hif_diag_write32(ar, address, value);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (ret) {
2288c2ecf20Sopenharmony_ci		ath6kl_err("failed to write 0x%x during diagnose window to 0x%x\n",
2298c2ecf20Sopenharmony_ci			   address, value);
2308c2ecf20Sopenharmony_ci		return ret;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ciint ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	u32 count, *buf = data;
2398c2ecf20Sopenharmony_ci	int ret;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (WARN_ON(length % 4))
2428c2ecf20Sopenharmony_ci		return -EINVAL;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for (count = 0; count < length / 4; count++, address += 4) {
2458c2ecf20Sopenharmony_ci		ret = ath6kl_diag_read32(ar, address, &buf[count]);
2468c2ecf20Sopenharmony_ci		if (ret)
2478c2ecf20Sopenharmony_ci			return ret;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ciint ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	u32 count;
2568c2ecf20Sopenharmony_ci	__le32 *buf = data;
2578c2ecf20Sopenharmony_ci	int ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (WARN_ON(length % 4))
2608c2ecf20Sopenharmony_ci		return -EINVAL;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	for (count = 0; count < length / 4; count++, address += 4) {
2638c2ecf20Sopenharmony_ci		ret = ath6kl_diag_write32(ar, address, buf[count]);
2648c2ecf20Sopenharmony_ci		if (ret)
2658c2ecf20Sopenharmony_ci			return ret;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ciint ath6kl_read_fwlogs(struct ath6kl *ar)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct ath6kl_dbglog_hdr debug_hdr;
2748c2ecf20Sopenharmony_ci	struct ath6kl_dbglog_buf debug_buf;
2758c2ecf20Sopenharmony_ci	u32 address, length, firstbuf, debug_hdr_addr;
2768c2ecf20Sopenharmony_ci	int ret, loop;
2778c2ecf20Sopenharmony_ci	u8 *buf;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
2808c2ecf20Sopenharmony_ci	if (!buf)
2818c2ecf20Sopenharmony_ci		return -ENOMEM;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	address = TARG_VTOP(ar->target_type,
2848c2ecf20Sopenharmony_ci			    ath6kl_get_hi_item_addr(ar,
2858c2ecf20Sopenharmony_ci						    HI_ITEM(hi_dbglog_hdr)));
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr);
2888c2ecf20Sopenharmony_ci	if (ret)
2898c2ecf20Sopenharmony_ci		goto out;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* Get the contents of the ring buffer */
2928c2ecf20Sopenharmony_ci	if (debug_hdr_addr == 0) {
2938c2ecf20Sopenharmony_ci		ath6kl_warn("Invalid address for debug_hdr_addr\n");
2948c2ecf20Sopenharmony_ci		ret = -EINVAL;
2958c2ecf20Sopenharmony_ci		goto out;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	address = TARG_VTOP(ar->target_type, debug_hdr_addr);
2998c2ecf20Sopenharmony_ci	ret = ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));
3008c2ecf20Sopenharmony_ci	if (ret)
3018c2ecf20Sopenharmony_ci		goto out;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	address = TARG_VTOP(ar->target_type,
3048c2ecf20Sopenharmony_ci			    le32_to_cpu(debug_hdr.dbuf_addr));
3058c2ecf20Sopenharmony_ci	firstbuf = address;
3068c2ecf20Sopenharmony_ci	ret = ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
3078c2ecf20Sopenharmony_ci	if (ret)
3088c2ecf20Sopenharmony_ci		goto out;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	loop = 100;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	do {
3138c2ecf20Sopenharmony_ci		address = TARG_VTOP(ar->target_type,
3148c2ecf20Sopenharmony_ci				    le32_to_cpu(debug_buf.buffer_addr));
3158c2ecf20Sopenharmony_ci		length = le32_to_cpu(debug_buf.length);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		if (length != 0 && (le32_to_cpu(debug_buf.length) <=
3188c2ecf20Sopenharmony_ci				    le32_to_cpu(debug_buf.bufsize))) {
3198c2ecf20Sopenharmony_ci			length = ALIGN(length, 4);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci			ret = ath6kl_diag_read(ar, address,
3228c2ecf20Sopenharmony_ci					       buf, length);
3238c2ecf20Sopenharmony_ci			if (ret)
3248c2ecf20Sopenharmony_ci				goto out;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci			ath6kl_debug_fwlog_event(ar, buf, length);
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		address = TARG_VTOP(ar->target_type,
3308c2ecf20Sopenharmony_ci				    le32_to_cpu(debug_buf.next));
3318c2ecf20Sopenharmony_ci		ret = ath6kl_diag_read(ar, address, &debug_buf,
3328c2ecf20Sopenharmony_ci				       sizeof(debug_buf));
3338c2ecf20Sopenharmony_ci		if (ret)
3348c2ecf20Sopenharmony_ci			goto out;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		loop--;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		if (WARN_ON(loop == 0)) {
3398c2ecf20Sopenharmony_ci			ret = -ETIMEDOUT;
3408c2ecf20Sopenharmony_ci			goto out;
3418c2ecf20Sopenharmony_ci		}
3428c2ecf20Sopenharmony_ci	} while (address != firstbuf);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ciout:
3458c2ecf20Sopenharmony_ci	kfree(buf);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return ret;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void ath6kl_install_static_wep_keys(struct ath6kl_vif *vif)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	u8 index;
3538c2ecf20Sopenharmony_ci	u8 keyusage;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	for (index = 0; index <= WMI_MAX_KEY_INDEX; index++) {
3568c2ecf20Sopenharmony_ci		if (vif->wep_key_list[index].key_len) {
3578c2ecf20Sopenharmony_ci			keyusage = GROUP_USAGE;
3588c2ecf20Sopenharmony_ci			if (index == vif->def_txkey_index)
3598c2ecf20Sopenharmony_ci				keyusage |= TX_USAGE;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci			ath6kl_wmi_addkey_cmd(vif->ar->wmi, vif->fw_vif_idx,
3628c2ecf20Sopenharmony_ci					      index,
3638c2ecf20Sopenharmony_ci					      WEP_CRYPT,
3648c2ecf20Sopenharmony_ci					      keyusage,
3658c2ecf20Sopenharmony_ci					      vif->wep_key_list[index].key_len,
3668c2ecf20Sopenharmony_ci					      NULL, 0,
3678c2ecf20Sopenharmony_ci					      vif->wep_key_list[index].key,
3688c2ecf20Sopenharmony_ci					      KEY_OP_INIT_VAL, NULL,
3698c2ecf20Sopenharmony_ci					      NO_SYNC_WMIFLAG);
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_civoid ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
3778c2ecf20Sopenharmony_ci	struct ath6kl_req_key *ik;
3788c2ecf20Sopenharmony_ci	int res;
3798c2ecf20Sopenharmony_ci	u8 key_rsc[ATH6KL_KEY_SEQ_LEN];
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ik = &ar->ap_mode_bkey;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	switch (vif->auth_mode) {
3868c2ecf20Sopenharmony_ci	case NONE_AUTH:
3878c2ecf20Sopenharmony_ci		if (vif->prwise_crypto == WEP_CRYPT)
3888c2ecf20Sopenharmony_ci			ath6kl_install_static_wep_keys(vif);
3898c2ecf20Sopenharmony_ci		if (!ik->valid || ik->key_type != WAPI_CRYPT)
3908c2ecf20Sopenharmony_ci			break;
3918c2ecf20Sopenharmony_ci		/* for WAPI, we need to set the delayed group key, continue: */
3928c2ecf20Sopenharmony_ci		fallthrough;
3938c2ecf20Sopenharmony_ci	case WPA_PSK_AUTH:
3948c2ecf20Sopenharmony_ci	case WPA2_PSK_AUTH:
3958c2ecf20Sopenharmony_ci	case (WPA_PSK_AUTH | WPA2_PSK_AUTH):
3968c2ecf20Sopenharmony_ci		if (!ik->valid)
3978c2ecf20Sopenharmony_ci			break;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
4008c2ecf20Sopenharmony_ci			   "Delayed addkey for the initial group key for AP mode\n");
4018c2ecf20Sopenharmony_ci		memset(key_rsc, 0, sizeof(key_rsc));
4028c2ecf20Sopenharmony_ci		res = ath6kl_wmi_addkey_cmd(
4038c2ecf20Sopenharmony_ci			ar->wmi, vif->fw_vif_idx, ik->key_index, ik->key_type,
4048c2ecf20Sopenharmony_ci			GROUP_USAGE, ik->key_len, key_rsc, ATH6KL_KEY_SEQ_LEN,
4058c2ecf20Sopenharmony_ci			ik->key,
4068c2ecf20Sopenharmony_ci			KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG);
4078c2ecf20Sopenharmony_ci		if (res) {
4088c2ecf20Sopenharmony_ci			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
4098c2ecf20Sopenharmony_ci				   "Delayed addkey failed: %d\n", res);
4108c2ecf20Sopenharmony_ci		}
4118c2ecf20Sopenharmony_ci		break;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (ar->last_ch != channel)
4158c2ecf20Sopenharmony_ci		/* we actually don't know the phymode, default to HT20 */
4168c2ecf20Sopenharmony_ci		ath6kl_cfg80211_ch_switch_notify(vif, channel, WMI_11G_HT20);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0);
4198c2ecf20Sopenharmony_ci	set_bit(CONNECTED, &vif->flags);
4208c2ecf20Sopenharmony_ci	netif_carrier_on(vif->ndev);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_civoid ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr,
4248c2ecf20Sopenharmony_ci				u8 keymgmt, u8 ucipher, u8 auth,
4258c2ecf20Sopenharmony_ci				u8 assoc_req_len, u8 *assoc_info, u8 apsd_info)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	u8 *ies = NULL, *wpa_ie = NULL, *pos;
4288c2ecf20Sopenharmony_ci	size_t ies_len = 0;
4298c2ecf20Sopenharmony_ci	struct station_info *sinfo;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (aid < 1 || aid > AP_MAX_NUM_STA)
4348c2ecf20Sopenharmony_ci		return;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) {
4378c2ecf20Sopenharmony_ci		struct ieee80211_mgmt *mgmt =
4388c2ecf20Sopenharmony_ci			(struct ieee80211_mgmt *) assoc_info;
4398c2ecf20Sopenharmony_ci		if (ieee80211_is_assoc_req(mgmt->frame_control) &&
4408c2ecf20Sopenharmony_ci		    assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) +
4418c2ecf20Sopenharmony_ci		    sizeof(mgmt->u.assoc_req)) {
4428c2ecf20Sopenharmony_ci			ies = mgmt->u.assoc_req.variable;
4438c2ecf20Sopenharmony_ci			ies_len = assoc_info + assoc_req_len - ies;
4448c2ecf20Sopenharmony_ci		} else if (ieee80211_is_reassoc_req(mgmt->frame_control) &&
4458c2ecf20Sopenharmony_ci			   assoc_req_len >= sizeof(struct ieee80211_hdr_3addr)
4468c2ecf20Sopenharmony_ci			   + sizeof(mgmt->u.reassoc_req)) {
4478c2ecf20Sopenharmony_ci			ies = mgmt->u.reassoc_req.variable;
4488c2ecf20Sopenharmony_ci			ies_len = assoc_info + assoc_req_len - ies;
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	pos = ies;
4538c2ecf20Sopenharmony_ci	while (pos && pos + 1 < ies + ies_len) {
4548c2ecf20Sopenharmony_ci		if (pos + 2 + pos[1] > ies + ies_len)
4558c2ecf20Sopenharmony_ci			break;
4568c2ecf20Sopenharmony_ci		if (pos[0] == WLAN_EID_RSN)
4578c2ecf20Sopenharmony_ci			wpa_ie = pos; /* RSN IE */
4588c2ecf20Sopenharmony_ci		else if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
4598c2ecf20Sopenharmony_ci			 pos[1] >= 4 &&
4608c2ecf20Sopenharmony_ci			 pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) {
4618c2ecf20Sopenharmony_ci			if (pos[5] == 0x01)
4628c2ecf20Sopenharmony_ci				wpa_ie = pos; /* WPA IE */
4638c2ecf20Sopenharmony_ci			else if (pos[5] == 0x04) {
4648c2ecf20Sopenharmony_ci				wpa_ie = pos; /* WPS IE */
4658c2ecf20Sopenharmony_ci				break; /* overrides WPA/RSN IE */
4668c2ecf20Sopenharmony_ci			}
4678c2ecf20Sopenharmony_ci		} else if (pos[0] == 0x44 && wpa_ie == NULL) {
4688c2ecf20Sopenharmony_ci			/*
4698c2ecf20Sopenharmony_ci			 * Note: WAPI Parameter Set IE re-uses Element ID that
4708c2ecf20Sopenharmony_ci			 * was officially allocated for BSS AC Access Delay. As
4718c2ecf20Sopenharmony_ci			 * such, we need to be a bit more careful on when
4728c2ecf20Sopenharmony_ci			 * parsing the frame. However, BSS AC Access Delay
4738c2ecf20Sopenharmony_ci			 * element is not supposed to be included in
4748c2ecf20Sopenharmony_ci			 * (Re)Association Request frames, so this should not
4758c2ecf20Sopenharmony_ci			 * cause problems.
4768c2ecf20Sopenharmony_ci			 */
4778c2ecf20Sopenharmony_ci			wpa_ie = pos; /* WAPI IE */
4788c2ecf20Sopenharmony_ci			break;
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci		pos += 2 + pos[1];
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	ath6kl_add_new_sta(vif, mac_addr, aid, wpa_ie,
4848c2ecf20Sopenharmony_ci			   wpa_ie ? 2 + wpa_ie[1] : 0,
4858c2ecf20Sopenharmony_ci			   keymgmt, ucipher, auth, apsd_info);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* send event to application */
4888c2ecf20Sopenharmony_ci	sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
4898c2ecf20Sopenharmony_ci	if (!sinfo)
4908c2ecf20Sopenharmony_ci		return;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	/* TODO: sinfo.generation */
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	sinfo->assoc_req_ies = ies;
4958c2ecf20Sopenharmony_ci	sinfo->assoc_req_ies_len = ies_len;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	cfg80211_new_sta(vif->ndev, mac_addr, sinfo, GFP_KERNEL);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	netif_wake_queue(vif->ndev);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	kfree(sinfo);
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_civoid disconnect_timer_handler(struct timer_list *t)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif = from_timer(vif, t, disconnect_timer);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	ath6kl_init_profile_info(vif);
5098c2ecf20Sopenharmony_ci	ath6kl_disconnect(vif);
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_civoid ath6kl_disconnect(struct ath6kl_vif *vif)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	if (test_bit(CONNECTED, &vif->flags) ||
5158c2ecf20Sopenharmony_ci	    test_bit(CONNECT_PEND, &vif->flags)) {
5168c2ecf20Sopenharmony_ci		ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
5178c2ecf20Sopenharmony_ci		/*
5188c2ecf20Sopenharmony_ci		 * Disconnect command is issued, clear the connect pending
5198c2ecf20Sopenharmony_ci		 * flag. The connected flag will be cleared in
5208c2ecf20Sopenharmony_ci		 * disconnect event notification.
5218c2ecf20Sopenharmony_ci		 */
5228c2ecf20Sopenharmony_ci		clear_bit(CONNECT_PEND, &vif->flags);
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci/* WMI Event handlers */
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_civoid ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver,
5298c2ecf20Sopenharmony_ci			enum wmi_phy_cap cap)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct ath6kl *ar = devt;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	memcpy(ar->mac_addr, datap, ETH_ALEN);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BOOT,
5368c2ecf20Sopenharmony_ci		   "ready event mac addr %pM sw_ver 0x%x abi_ver 0x%x cap 0x%x\n",
5378c2ecf20Sopenharmony_ci		   ar->mac_addr, sw_ver, abi_ver, cap);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	ar->version.wlan_ver = sw_ver;
5408c2ecf20Sopenharmony_ci	ar->version.abi_ver = abi_ver;
5418c2ecf20Sopenharmony_ci	ar->hw.cap = cap;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	if (strlen(ar->wiphy->fw_version) == 0) {
5448c2ecf20Sopenharmony_ci		snprintf(ar->wiphy->fw_version,
5458c2ecf20Sopenharmony_ci			 sizeof(ar->wiphy->fw_version),
5468c2ecf20Sopenharmony_ci			 "%u.%u.%u.%u",
5478c2ecf20Sopenharmony_ci			 (ar->version.wlan_ver & 0xf0000000) >> 28,
5488c2ecf20Sopenharmony_ci			 (ar->version.wlan_ver & 0x0f000000) >> 24,
5498c2ecf20Sopenharmony_ci			 (ar->version.wlan_ver & 0x00ff0000) >> 16,
5508c2ecf20Sopenharmony_ci			 (ar->version.wlan_ver & 0x0000ffff));
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	/* indicate to the waiting thread that the ready event was received */
5548c2ecf20Sopenharmony_ci	set_bit(WMI_READY, &ar->flag);
5558c2ecf20Sopenharmony_ci	wake_up(&ar->event_wq);
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_civoid ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
5618c2ecf20Sopenharmony_ci	bool aborted = false;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (status != WMI_SCAN_STATUS_SUCCESS)
5648c2ecf20Sopenharmony_ci		aborted = true;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	ath6kl_cfg80211_scan_complete_event(vif, aborted);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!ar->usr_bss_filter) {
5698c2ecf20Sopenharmony_ci		clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
5708c2ecf20Sopenharmony_ci		ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
5718c2ecf20Sopenharmony_ci					 NONE_BSS_FILTER, 0);
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status);
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	vif->profile.ch = cpu_to_le16(channel);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	switch (vif->nw_type) {
5848c2ecf20Sopenharmony_ci	case AP_NETWORK:
5858c2ecf20Sopenharmony_ci		/*
5868c2ecf20Sopenharmony_ci		 * reconfigure any saved RSN IE capabilites in the beacon /
5878c2ecf20Sopenharmony_ci		 * probe response to stay in sync with the supplicant.
5888c2ecf20Sopenharmony_ci		 */
5898c2ecf20Sopenharmony_ci		if (vif->rsn_capab &&
5908c2ecf20Sopenharmony_ci		    test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
5918c2ecf20Sopenharmony_ci			     ar->fw_capabilities))
5928c2ecf20Sopenharmony_ci			ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
5938c2ecf20Sopenharmony_ci					      WLAN_EID_RSN, WMI_RSN_IE_CAPB,
5948c2ecf20Sopenharmony_ci					      (const u8 *) &vif->rsn_capab,
5958c2ecf20Sopenharmony_ci					      sizeof(vif->rsn_capab));
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci		return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx,
5988c2ecf20Sopenharmony_ci						    &vif->profile);
5998c2ecf20Sopenharmony_ci	default:
6008c2ecf20Sopenharmony_ci		ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type);
6018c2ecf20Sopenharmony_ci		return -ENOTSUPP;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif;
6088c2ecf20Sopenharmony_ci	int res = 0;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	if (!ar->want_ch_switch)
6118c2ecf20Sopenharmony_ci		return;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->list_lock);
6148c2ecf20Sopenharmony_ci	list_for_each_entry(vif, &ar->vif_list, list) {
6158c2ecf20Sopenharmony_ci		if (ar->want_ch_switch & (1 << vif->fw_vif_idx))
6168c2ecf20Sopenharmony_ci			res = ath6kl_commit_ch_switch(vif, channel);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		/* if channel switch failed, oh well we tried */
6198c2ecf20Sopenharmony_ci		ar->want_ch_switch &= ~(1 << vif->fw_vif_idx);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		if (res)
6228c2ecf20Sopenharmony_ci			ath6kl_err("channel switch failed nw_type %d res %d\n",
6238c2ecf20Sopenharmony_ci				   vif->nw_type, res);
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->list_lock);
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_civoid ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid,
6298c2ecf20Sopenharmony_ci			  u16 listen_int, u16 beacon_int,
6308c2ecf20Sopenharmony_ci			  enum network_type net_type, u8 beacon_ie_len,
6318c2ecf20Sopenharmony_ci			  u8 assoc_req_len, u8 assoc_resp_len,
6328c2ecf20Sopenharmony_ci			  u8 *assoc_info)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	ath6kl_cfg80211_connect_event(vif, channel, bssid,
6378c2ecf20Sopenharmony_ci				      listen_int, beacon_int,
6388c2ecf20Sopenharmony_ci				      net_type, beacon_ie_len,
6398c2ecf20Sopenharmony_ci				      assoc_req_len, assoc_resp_len,
6408c2ecf20Sopenharmony_ci				      assoc_info);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	memcpy(vif->bssid, bssid, sizeof(vif->bssid));
6438c2ecf20Sopenharmony_ci	vif->bss_ch = channel;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (vif->nw_type == INFRA_NETWORK) {
6468c2ecf20Sopenharmony_ci		ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
6478c2ecf20Sopenharmony_ci					      vif->listen_intvl_t, 0);
6488c2ecf20Sopenharmony_ci		ath6kl_check_ch_switch(ar, channel);
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	netif_wake_queue(vif->ndev);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/* Update connect & link status atomically */
6548c2ecf20Sopenharmony_ci	spin_lock_bh(&vif->if_lock);
6558c2ecf20Sopenharmony_ci	set_bit(CONNECTED, &vif->flags);
6568c2ecf20Sopenharmony_ci	clear_bit(CONNECT_PEND, &vif->flags);
6578c2ecf20Sopenharmony_ci	netif_carrier_on(vif->ndev);
6588c2ecf20Sopenharmony_ci	spin_unlock_bh(&vif->if_lock);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	aggr_reset_state(vif->aggr_cntxt->aggr_conn);
6618c2ecf20Sopenharmony_ci	vif->reconnect_flag = 0;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if ((vif->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) {
6648c2ecf20Sopenharmony_ci		memset(ar->node_map, 0, sizeof(ar->node_map));
6658c2ecf20Sopenharmony_ci		ar->node_num = 0;
6668c2ecf20Sopenharmony_ci		ar->next_ep_id = ENDPOINT_2;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (!ar->usr_bss_filter) {
6708c2ecf20Sopenharmony_ci		set_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
6718c2ecf20Sopenharmony_ci		ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
6728c2ecf20Sopenharmony_ci					 CURRENT_BSS_FILTER, 0);
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_civoid ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct ath6kl_sta *sta;
6798c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
6808c2ecf20Sopenharmony_ci	u8 tsc[6];
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	/*
6838c2ecf20Sopenharmony_ci	 * For AP case, keyid will have aid of STA which sent pkt with
6848c2ecf20Sopenharmony_ci	 * MIC error. Use this aid to get MAC & send it to hostapd.
6858c2ecf20Sopenharmony_ci	 */
6868c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
6878c2ecf20Sopenharmony_ci		sta = ath6kl_find_sta_by_aid(ar, (keyid >> 2));
6888c2ecf20Sopenharmony_ci		if (!sta)
6898c2ecf20Sopenharmony_ci			return;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci		ath6kl_dbg(ATH6KL_DBG_TRC,
6928c2ecf20Sopenharmony_ci			   "ap tkip mic error received from aid=%d\n", keyid);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci		memset(tsc, 0, sizeof(tsc)); /* FIX: get correct TSC */
6958c2ecf20Sopenharmony_ci		cfg80211_michael_mic_failure(vif->ndev, sta->mac,
6968c2ecf20Sopenharmony_ci					     NL80211_KEYTYPE_PAIRWISE, keyid,
6978c2ecf20Sopenharmony_ci					     tsc, GFP_KERNEL);
6988c2ecf20Sopenharmony_ci	} else {
6998c2ecf20Sopenharmony_ci		ath6kl_cfg80211_tkip_micerr_event(vif, keyid, ismcast);
7008c2ecf20Sopenharmony_ci	}
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_cistatic void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct wmi_target_stats *tgt_stats =
7068c2ecf20Sopenharmony_ci		(struct wmi_target_stats *) ptr;
7078c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
7088c2ecf20Sopenharmony_ci	struct target_stats *stats = &vif->target_stats;
7098c2ecf20Sopenharmony_ci	struct tkip_ccmp_stats *ccmp_stats;
7108c2ecf20Sopenharmony_ci	s32 rate;
7118c2ecf20Sopenharmony_ci	u8 ac;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (len < sizeof(*tgt_stats))
7148c2ecf20Sopenharmony_ci		return;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n");
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt);
7198c2ecf20Sopenharmony_ci	stats->tx_byte += le32_to_cpu(tgt_stats->stats.tx.byte);
7208c2ecf20Sopenharmony_ci	stats->tx_ucast_pkt += le32_to_cpu(tgt_stats->stats.tx.ucast_pkt);
7218c2ecf20Sopenharmony_ci	stats->tx_ucast_byte += le32_to_cpu(tgt_stats->stats.tx.ucast_byte);
7228c2ecf20Sopenharmony_ci	stats->tx_mcast_pkt += le32_to_cpu(tgt_stats->stats.tx.mcast_pkt);
7238c2ecf20Sopenharmony_ci	stats->tx_mcast_byte += le32_to_cpu(tgt_stats->stats.tx.mcast_byte);
7248c2ecf20Sopenharmony_ci	stats->tx_bcast_pkt  += le32_to_cpu(tgt_stats->stats.tx.bcast_pkt);
7258c2ecf20Sopenharmony_ci	stats->tx_bcast_byte += le32_to_cpu(tgt_stats->stats.tx.bcast_byte);
7268c2ecf20Sopenharmony_ci	stats->tx_rts_success_cnt +=
7278c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->stats.tx.rts_success_cnt);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	for (ac = 0; ac < WMM_NUM_AC; ac++)
7308c2ecf20Sopenharmony_ci		stats->tx_pkt_per_ac[ac] +=
7318c2ecf20Sopenharmony_ci			le32_to_cpu(tgt_stats->stats.tx.pkt_per_ac[ac]);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	stats->tx_err += le32_to_cpu(tgt_stats->stats.tx.err);
7348c2ecf20Sopenharmony_ci	stats->tx_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.fail_cnt);
7358c2ecf20Sopenharmony_ci	stats->tx_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.retry_cnt);
7368c2ecf20Sopenharmony_ci	stats->tx_mult_retry_cnt +=
7378c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt);
7388c2ecf20Sopenharmony_ci	stats->tx_rts_fail_cnt +=
7398c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	rate = a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate);
7428c2ecf20Sopenharmony_ci	stats->tx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt);
7458c2ecf20Sopenharmony_ci	stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte);
7468c2ecf20Sopenharmony_ci	stats->rx_ucast_pkt += le32_to_cpu(tgt_stats->stats.rx.ucast_pkt);
7478c2ecf20Sopenharmony_ci	stats->rx_ucast_byte += le32_to_cpu(tgt_stats->stats.rx.ucast_byte);
7488c2ecf20Sopenharmony_ci	stats->rx_mcast_pkt += le32_to_cpu(tgt_stats->stats.rx.mcast_pkt);
7498c2ecf20Sopenharmony_ci	stats->rx_mcast_byte += le32_to_cpu(tgt_stats->stats.rx.mcast_byte);
7508c2ecf20Sopenharmony_ci	stats->rx_bcast_pkt += le32_to_cpu(tgt_stats->stats.rx.bcast_pkt);
7518c2ecf20Sopenharmony_ci	stats->rx_bcast_byte += le32_to_cpu(tgt_stats->stats.rx.bcast_byte);
7528c2ecf20Sopenharmony_ci	stats->rx_frgment_pkt += le32_to_cpu(tgt_stats->stats.rx.frgment_pkt);
7538c2ecf20Sopenharmony_ci	stats->rx_err += le32_to_cpu(tgt_stats->stats.rx.err);
7548c2ecf20Sopenharmony_ci	stats->rx_crc_err += le32_to_cpu(tgt_stats->stats.rx.crc_err);
7558c2ecf20Sopenharmony_ci	stats->rx_key_cache_miss +=
7568c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->stats.rx.key_cache_miss);
7578c2ecf20Sopenharmony_ci	stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err);
7588c2ecf20Sopenharmony_ci	stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	rate = a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate);
7618c2ecf20Sopenharmony_ci	stats->rx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	stats->tkip_local_mic_fail +=
7668c2ecf20Sopenharmony_ci		le32_to_cpu(ccmp_stats->tkip_local_mic_fail);
7678c2ecf20Sopenharmony_ci	stats->tkip_cnter_measures_invoked +=
7688c2ecf20Sopenharmony_ci		le32_to_cpu(ccmp_stats->tkip_cnter_measures_invoked);
7698c2ecf20Sopenharmony_ci	stats->tkip_fmt_err += le32_to_cpu(ccmp_stats->tkip_fmt_err);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	stats->ccmp_fmt_err += le32_to_cpu(ccmp_stats->ccmp_fmt_err);
7728c2ecf20Sopenharmony_ci	stats->ccmp_replays += le32_to_cpu(ccmp_stats->ccmp_replays);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	stats->pwr_save_fail_cnt +=
7758c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->pm_stats.pwr_save_failure_cnt);
7768c2ecf20Sopenharmony_ci	stats->noise_floor_calib =
7778c2ecf20Sopenharmony_ci		a_sle32_to_cpu(tgt_stats->noise_floor_calib);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	stats->cs_bmiss_cnt +=
7808c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->cserv_stats.cs_bmiss_cnt);
7818c2ecf20Sopenharmony_ci	stats->cs_low_rssi_cnt +=
7828c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->cserv_stats.cs_low_rssi_cnt);
7838c2ecf20Sopenharmony_ci	stats->cs_connect_cnt +=
7848c2ecf20Sopenharmony_ci		le16_to_cpu(tgt_stats->cserv_stats.cs_connect_cnt);
7858c2ecf20Sopenharmony_ci	stats->cs_discon_cnt +=
7868c2ecf20Sopenharmony_ci		le16_to_cpu(tgt_stats->cserv_stats.cs_discon_cnt);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	stats->cs_ave_beacon_rssi =
7898c2ecf20Sopenharmony_ci		a_sle16_to_cpu(tgt_stats->cserv_stats.cs_ave_beacon_rssi);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	stats->cs_last_roam_msec =
7928c2ecf20Sopenharmony_ci		tgt_stats->cserv_stats.cs_last_roam_msec;
7938c2ecf20Sopenharmony_ci	stats->cs_snr = tgt_stats->cserv_stats.cs_snr;
7948c2ecf20Sopenharmony_ci	stats->cs_rssi = a_sle16_to_cpu(tgt_stats->cserv_stats.cs_rssi);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	stats->lq_val = le32_to_cpu(tgt_stats->lq_val);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	stats->wow_pkt_dropped +=
7998c2ecf20Sopenharmony_ci		le32_to_cpu(tgt_stats->wow_stats.wow_pkt_dropped);
8008c2ecf20Sopenharmony_ci	stats->wow_host_pkt_wakeups +=
8018c2ecf20Sopenharmony_ci		tgt_stats->wow_stats.wow_host_pkt_wakeups;
8028c2ecf20Sopenharmony_ci	stats->wow_host_evt_wakeups +=
8038c2ecf20Sopenharmony_ci		tgt_stats->wow_stats.wow_host_evt_wakeups;
8048c2ecf20Sopenharmony_ci	stats->wow_evt_discarded +=
8058c2ecf20Sopenharmony_ci		le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	stats->arp_received = le32_to_cpu(tgt_stats->arp_stats.arp_received);
8088c2ecf20Sopenharmony_ci	stats->arp_replied = le32_to_cpu(tgt_stats->arp_stats.arp_replied);
8098c2ecf20Sopenharmony_ci	stats->arp_matched = le32_to_cpu(tgt_stats->arp_stats.arp_matched);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (test_bit(STATS_UPDATE_PEND, &vif->flags)) {
8128c2ecf20Sopenharmony_ci		clear_bit(STATS_UPDATE_PEND, &vif->flags);
8138c2ecf20Sopenharmony_ci		wake_up(&ar->event_wq);
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_cistatic void ath6kl_add_le32(__le32 *var, __le32 val)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	*var = cpu_to_le32(le32_to_cpu(*var) + le32_to_cpu(val));
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_civoid ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct wmi_ap_mode_stat *p = (struct wmi_ap_mode_stat *) ptr;
8258c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
8268c2ecf20Sopenharmony_ci	struct wmi_ap_mode_stat *ap = &ar->ap_stats;
8278c2ecf20Sopenharmony_ci	struct wmi_per_sta_stat *st_ap, *st_p;
8288c2ecf20Sopenharmony_ci	u8 ac;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
8318c2ecf20Sopenharmony_ci		if (len < sizeof(*p))
8328c2ecf20Sopenharmony_ci			return;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci		for (ac = 0; ac < AP_MAX_NUM_STA; ac++) {
8358c2ecf20Sopenharmony_ci			st_ap = &ap->sta[ac];
8368c2ecf20Sopenharmony_ci			st_p = &p->sta[ac];
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->tx_bytes, st_p->tx_bytes);
8398c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->tx_pkts, st_p->tx_pkts);
8408c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->tx_error, st_p->tx_error);
8418c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->tx_discard, st_p->tx_discard);
8428c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->rx_bytes, st_p->rx_bytes);
8438c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->rx_pkts, st_p->rx_pkts);
8448c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->rx_error, st_p->rx_error);
8458c2ecf20Sopenharmony_ci			ath6kl_add_le32(&st_ap->rx_discard, st_p->rx_discard);
8468c2ecf20Sopenharmony_ci		}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	} else {
8498c2ecf20Sopenharmony_ci		ath6kl_update_target_stats(vif, ptr, len);
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_civoid ath6kl_wakeup_event(void *dev)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci	struct ath6kl *ar = (struct ath6kl *) dev;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	wake_up(&ar->event_wq);
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_civoid ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct ath6kl *ar = (struct ath6kl *) devt;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	ar->tx_pwr = tx_pwr;
8658c2ecf20Sopenharmony_ci	wake_up(&ar->event_wq);
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_civoid ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	struct ath6kl_sta *conn;
8718c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8728c2ecf20Sopenharmony_ci	bool psq_empty = false;
8738c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
8748c2ecf20Sopenharmony_ci	struct ath6kl_mgmt_buff *mgmt_buf;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	conn = ath6kl_find_sta_by_aid(ar, aid);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	if (!conn)
8798c2ecf20Sopenharmony_ci		return;
8808c2ecf20Sopenharmony_ci	/*
8818c2ecf20Sopenharmony_ci	 * Send out a packet queued on ps queue. When the ps queue
8828c2ecf20Sopenharmony_ci	 * becomes empty update the PVB for this station.
8838c2ecf20Sopenharmony_ci	 */
8848c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->psq_lock);
8858c2ecf20Sopenharmony_ci	psq_empty  = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
8868c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->psq_lock);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (psq_empty)
8898c2ecf20Sopenharmony_ci		/* TODO: Send out a NULL data frame */
8908c2ecf20Sopenharmony_ci		return;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->psq_lock);
8938c2ecf20Sopenharmony_ci	if (conn->mgmt_psq_len > 0) {
8948c2ecf20Sopenharmony_ci		mgmt_buf = list_first_entry(&conn->mgmt_psq,
8958c2ecf20Sopenharmony_ci					struct ath6kl_mgmt_buff, list);
8968c2ecf20Sopenharmony_ci		list_del(&mgmt_buf->list);
8978c2ecf20Sopenharmony_ci		conn->mgmt_psq_len--;
8988c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->psq_lock);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci		conn->sta_flags |= STA_PS_POLLED;
9018c2ecf20Sopenharmony_ci		ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx,
9028c2ecf20Sopenharmony_ci					 mgmt_buf->id, mgmt_buf->freq,
9038c2ecf20Sopenharmony_ci					 mgmt_buf->wait, mgmt_buf->buf,
9048c2ecf20Sopenharmony_ci					 mgmt_buf->len, mgmt_buf->no_cck);
9058c2ecf20Sopenharmony_ci		conn->sta_flags &= ~STA_PS_POLLED;
9068c2ecf20Sopenharmony_ci		kfree(mgmt_buf);
9078c2ecf20Sopenharmony_ci	} else {
9088c2ecf20Sopenharmony_ci		skb = skb_dequeue(&conn->psq);
9098c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->psq_lock);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci		conn->sta_flags |= STA_PS_POLLED;
9128c2ecf20Sopenharmony_ci		ath6kl_data_tx(skb, vif->ndev);
9138c2ecf20Sopenharmony_ci		conn->sta_flags &= ~STA_PS_POLLED;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->psq_lock);
9178c2ecf20Sopenharmony_ci	psq_empty  = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
9188c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->psq_lock);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	if (psq_empty)
9218c2ecf20Sopenharmony_ci		ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 0);
9228c2ecf20Sopenharmony_ci}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_civoid ath6kl_dtimexpiry_event(struct ath6kl_vif *vif)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	bool mcastq_empty = false;
9278c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9288c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	/*
9318c2ecf20Sopenharmony_ci	 * If there are no associated STAs, ignore the DTIM expiry event.
9328c2ecf20Sopenharmony_ci	 * There can be potential race conditions where the last associated
9338c2ecf20Sopenharmony_ci	 * STA may disconnect & before the host could clear the 'Indicate
9348c2ecf20Sopenharmony_ci	 * DTIM' request to the firmware, the firmware would have just
9358c2ecf20Sopenharmony_ci	 * indicated a DTIM expiry event. The race is between 'clear DTIM
9368c2ecf20Sopenharmony_ci	 * expiry cmd' going from the host to the firmware & the DTIM
9378c2ecf20Sopenharmony_ci	 * expiry event happening from the firmware to the host.
9388c2ecf20Sopenharmony_ci	 */
9398c2ecf20Sopenharmony_ci	if (!ar->sta_list_index)
9408c2ecf20Sopenharmony_ci		return;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->mcastpsq_lock);
9438c2ecf20Sopenharmony_ci	mcastq_empty = skb_queue_empty(&ar->mcastpsq);
9448c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->mcastpsq_lock);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (mcastq_empty)
9478c2ecf20Sopenharmony_ci		return;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	/* set the STA flag to dtim_expired for the frame to go out */
9508c2ecf20Sopenharmony_ci	set_bit(DTIM_EXPIRED, &vif->flags);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->mcastpsq_lock);
9538c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&ar->mcastpsq)) != NULL) {
9548c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->mcastpsq_lock);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci		ath6kl_data_tx(skb, vif->ndev);
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		spin_lock_bh(&ar->mcastpsq_lock);
9598c2ecf20Sopenharmony_ci	}
9608c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->mcastpsq_lock);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	clear_bit(DTIM_EXPIRED, &vif->flags);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	/* clear the LSB of the BitMapCtl field of the TIM IE */
9658c2ecf20Sopenharmony_ci	ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, MCAST_AID, 0);
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_civoid ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid,
9698c2ecf20Sopenharmony_ci			     u8 assoc_resp_len, u8 *assoc_info,
9708c2ecf20Sopenharmony_ci			     u16 prot_reason_status)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	if (vif->nw_type == AP_NETWORK) {
9758c2ecf20Sopenharmony_ci		/* disconnect due to other STA vif switching channels */
9768c2ecf20Sopenharmony_ci		if (reason == BSS_DISCONNECTED &&
9778c2ecf20Sopenharmony_ci		    prot_reason_status == WMI_AP_REASON_STA_ROAM) {
9788c2ecf20Sopenharmony_ci			ar->want_ch_switch |= 1 << vif->fw_vif_idx;
9798c2ecf20Sopenharmony_ci			/* bail back to this channel if STA vif fails connect */
9808c2ecf20Sopenharmony_ci			ar->last_ch = le16_to_cpu(vif->profile.ch);
9818c2ecf20Sopenharmony_ci		}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci		if (prot_reason_status == WMI_AP_REASON_MAX_STA) {
9848c2ecf20Sopenharmony_ci			/* send max client reached notification to user space */
9858c2ecf20Sopenharmony_ci			cfg80211_conn_failed(vif->ndev, bssid,
9868c2ecf20Sopenharmony_ci					     NL80211_CONN_FAIL_MAX_CLIENTS,
9878c2ecf20Sopenharmony_ci					     GFP_KERNEL);
9888c2ecf20Sopenharmony_ci		}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci		if (prot_reason_status == WMI_AP_REASON_ACL) {
9918c2ecf20Sopenharmony_ci			/* send blocked client notification to user space */
9928c2ecf20Sopenharmony_ci			cfg80211_conn_failed(vif->ndev, bssid,
9938c2ecf20Sopenharmony_ci					     NL80211_CONN_FAIL_BLOCKED_CLIENT,
9948c2ecf20Sopenharmony_ci					     GFP_KERNEL);
9958c2ecf20Sopenharmony_ci		}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		if (!ath6kl_remove_sta(ar, bssid, prot_reason_status))
9988c2ecf20Sopenharmony_ci			return;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci		/* if no more associated STAs, empty the mcast PS q */
10018c2ecf20Sopenharmony_ci		if (ar->sta_list_index == 0) {
10028c2ecf20Sopenharmony_ci			spin_lock_bh(&ar->mcastpsq_lock);
10038c2ecf20Sopenharmony_ci			skb_queue_purge(&ar->mcastpsq);
10048c2ecf20Sopenharmony_ci			spin_unlock_bh(&ar->mcastpsq_lock);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci			/* clear the LSB of the TIM IE's BitMapCtl field */
10078c2ecf20Sopenharmony_ci			if (test_bit(WMI_READY, &ar->flag))
10088c2ecf20Sopenharmony_ci				ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
10098c2ecf20Sopenharmony_ci						       MCAST_AID, 0);
10108c2ecf20Sopenharmony_ci		}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci		if (!is_broadcast_ether_addr(bssid)) {
10138c2ecf20Sopenharmony_ci			/* send event to application */
10148c2ecf20Sopenharmony_ci			cfg80211_del_sta(vif->ndev, bssid, GFP_KERNEL);
10158c2ecf20Sopenharmony_ci		}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci		if (memcmp(vif->ndev->dev_addr, bssid, ETH_ALEN) == 0) {
10188c2ecf20Sopenharmony_ci			memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list));
10198c2ecf20Sopenharmony_ci			clear_bit(CONNECTED, &vif->flags);
10208c2ecf20Sopenharmony_ci		}
10218c2ecf20Sopenharmony_ci		return;
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	ath6kl_cfg80211_disconnect_event(vif, reason, bssid,
10258c2ecf20Sopenharmony_ci					 assoc_resp_len, assoc_info,
10268c2ecf20Sopenharmony_ci					 prot_reason_status);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	aggr_reset_state(vif->aggr_cntxt->aggr_conn);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	del_timer(&vif->disconnect_timer);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	/*
10358c2ecf20Sopenharmony_ci	 * If the event is due to disconnect cmd from the host, only they
10368c2ecf20Sopenharmony_ci	 * the target would stop trying to connect. Under any other
10378c2ecf20Sopenharmony_ci	 * condition, target would keep trying to connect.
10388c2ecf20Sopenharmony_ci	 */
10398c2ecf20Sopenharmony_ci	if (reason == DISCONNECT_CMD) {
10408c2ecf20Sopenharmony_ci		if (!ar->usr_bss_filter && test_bit(WMI_READY, &ar->flag))
10418c2ecf20Sopenharmony_ci			ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
10428c2ecf20Sopenharmony_ci						 NONE_BSS_FILTER, 0);
10438c2ecf20Sopenharmony_ci	} else {
10448c2ecf20Sopenharmony_ci		set_bit(CONNECT_PEND, &vif->flags);
10458c2ecf20Sopenharmony_ci		if (((reason == ASSOC_FAILED) &&
10468c2ecf20Sopenharmony_ci		     (prot_reason_status == 0x11)) ||
10478c2ecf20Sopenharmony_ci		    ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) &&
10488c2ecf20Sopenharmony_ci		     (vif->reconnect_flag == 1))) {
10498c2ecf20Sopenharmony_ci			set_bit(CONNECTED, &vif->flags);
10508c2ecf20Sopenharmony_ci			return;
10518c2ecf20Sopenharmony_ci		}
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	/* restart disconnected concurrent vifs waiting for new channel */
10558c2ecf20Sopenharmony_ci	ath6kl_check_ch_switch(ar, ar->last_ch);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	/* update connect & link status atomically */
10588c2ecf20Sopenharmony_ci	spin_lock_bh(&vif->if_lock);
10598c2ecf20Sopenharmony_ci	clear_bit(CONNECTED, &vif->flags);
10608c2ecf20Sopenharmony_ci	netif_carrier_off(vif->ndev);
10618c2ecf20Sopenharmony_ci	spin_unlock_bh(&vif->if_lock);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	if ((reason != CSERV_DISCONNECT) || (vif->reconnect_flag != 1))
10648c2ecf20Sopenharmony_ci		vif->reconnect_flag = 0;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	if (reason != CSERV_DISCONNECT)
10678c2ecf20Sopenharmony_ci		ar->user_key_ctrl = 0;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	netif_stop_queue(vif->ndev);
10708c2ecf20Sopenharmony_ci	memset(vif->bssid, 0, sizeof(vif->bssid));
10718c2ecf20Sopenharmony_ci	vif->bss_ch = 0;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	ath6kl_tx_data_cleanup(ar);
10748c2ecf20Sopenharmony_ci}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistruct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->list_lock);
10818c2ecf20Sopenharmony_ci	if (list_empty(&ar->vif_list)) {
10828c2ecf20Sopenharmony_ci		spin_unlock_bh(&ar->list_lock);
10838c2ecf20Sopenharmony_ci		return NULL;
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	vif = list_first_entry(&ar->vif_list, struct ath6kl_vif, list);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->list_lock);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	return vif;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic int ath6kl_open(struct net_device *dev)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif = netdev_priv(dev);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	set_bit(WLAN_ENABLED, &vif->flags);
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	if (test_bit(CONNECTED, &vif->flags)) {
11008c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
11018c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
11028c2ecf20Sopenharmony_ci	} else {
11038c2ecf20Sopenharmony_ci		netif_carrier_off(dev);
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	return 0;
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic int ath6kl_close(struct net_device *dev)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif = netdev_priv(dev);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	ath6kl_cfg80211_stop(vif);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	clear_bit(WLAN_ENABLED, &vif->flags);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	return 0;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic int ath6kl_set_features(struct net_device *dev,
11238c2ecf20Sopenharmony_ci			       netdev_features_t features)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif = netdev_priv(dev);
11268c2ecf20Sopenharmony_ci	struct ath6kl *ar = vif->ar;
11278c2ecf20Sopenharmony_ci	int err = 0;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	if ((features & NETIF_F_RXCSUM) &&
11308c2ecf20Sopenharmony_ci	    (ar->rx_meta_ver != WMI_META_VERSION_2)) {
11318c2ecf20Sopenharmony_ci		ar->rx_meta_ver = WMI_META_VERSION_2;
11328c2ecf20Sopenharmony_ci		err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
11338c2ecf20Sopenharmony_ci							 vif->fw_vif_idx,
11348c2ecf20Sopenharmony_ci							 ar->rx_meta_ver, 0, 0);
11358c2ecf20Sopenharmony_ci		if (err) {
11368c2ecf20Sopenharmony_ci			dev->features = features & ~NETIF_F_RXCSUM;
11378c2ecf20Sopenharmony_ci			return err;
11388c2ecf20Sopenharmony_ci		}
11398c2ecf20Sopenharmony_ci	} else if (!(features & NETIF_F_RXCSUM) &&
11408c2ecf20Sopenharmony_ci		   (ar->rx_meta_ver == WMI_META_VERSION_2)) {
11418c2ecf20Sopenharmony_ci		ar->rx_meta_ver = 0;
11428c2ecf20Sopenharmony_ci		err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
11438c2ecf20Sopenharmony_ci							 vif->fw_vif_idx,
11448c2ecf20Sopenharmony_ci							 ar->rx_meta_ver, 0, 0);
11458c2ecf20Sopenharmony_ci		if (err) {
11468c2ecf20Sopenharmony_ci			dev->features = features | NETIF_F_RXCSUM;
11478c2ecf20Sopenharmony_ci			return err;
11488c2ecf20Sopenharmony_ci		}
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	return err;
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_cistatic void ath6kl_set_multicast_list(struct net_device *ndev)
11558c2ecf20Sopenharmony_ci{
11568c2ecf20Sopenharmony_ci	struct ath6kl_vif *vif = netdev_priv(ndev);
11578c2ecf20Sopenharmony_ci	bool mc_all_on = false;
11588c2ecf20Sopenharmony_ci	int mc_count = netdev_mc_count(ndev);
11598c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
11608c2ecf20Sopenharmony_ci	bool found;
11618c2ecf20Sopenharmony_ci	struct ath6kl_mc_filter *mc_filter, *tmp;
11628c2ecf20Sopenharmony_ci	struct list_head mc_filter_new;
11638c2ecf20Sopenharmony_ci	int ret;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	if (!test_bit(WMI_READY, &vif->ar->flag) ||
11668c2ecf20Sopenharmony_ci	    !test_bit(WLAN_ENABLED, &vif->flags))
11678c2ecf20Sopenharmony_ci		return;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/* Enable multicast-all filter. */
11708c2ecf20Sopenharmony_ci	mc_all_on = !!(ndev->flags & IFF_PROMISC) ||
11718c2ecf20Sopenharmony_ci		    !!(ndev->flags & IFF_ALLMULTI) ||
11728c2ecf20Sopenharmony_ci		    !!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	if (mc_all_on)
11758c2ecf20Sopenharmony_ci		set_bit(NETDEV_MCAST_ALL_ON, &vif->flags);
11768c2ecf20Sopenharmony_ci	else
11778c2ecf20Sopenharmony_ci		clear_bit(NETDEV_MCAST_ALL_ON, &vif->flags);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	if (test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
11808c2ecf20Sopenharmony_ci		     vif->ar->fw_capabilities)) {
11818c2ecf20Sopenharmony_ci		mc_all_on = mc_all_on || (vif->ar->state == ATH6KL_STATE_ON);
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if (!(ndev->flags & IFF_MULTICAST)) {
11858c2ecf20Sopenharmony_ci		mc_all_on = false;
11868c2ecf20Sopenharmony_ci		set_bit(NETDEV_MCAST_ALL_OFF, &vif->flags);
11878c2ecf20Sopenharmony_ci	} else {
11888c2ecf20Sopenharmony_ci		clear_bit(NETDEV_MCAST_ALL_OFF, &vif->flags);
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	/* Enable/disable "multicast-all" filter*/
11928c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast-all filter\n",
11938c2ecf20Sopenharmony_ci		   mc_all_on ? "enabling" : "disabling");
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx,
11968c2ecf20Sopenharmony_ci						  mc_all_on);
11978c2ecf20Sopenharmony_ci	if (ret) {
11988c2ecf20Sopenharmony_ci		ath6kl_warn("Failed to %s multicast-all receive\n",
11998c2ecf20Sopenharmony_ci			    mc_all_on ? "enable" : "disable");
12008c2ecf20Sopenharmony_ci		return;
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (test_bit(NETDEV_MCAST_ALL_ON, &vif->flags))
12048c2ecf20Sopenharmony_ci		return;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	/* Keep the driver and firmware mcast list in sync. */
12078c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
12088c2ecf20Sopenharmony_ci		found = false;
12098c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, ndev) {
12108c2ecf20Sopenharmony_ci			if (memcmp(ha->addr, mc_filter->hw_addr,
12118c2ecf20Sopenharmony_ci				   ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
12128c2ecf20Sopenharmony_ci				found = true;
12138c2ecf20Sopenharmony_ci				break;
12148c2ecf20Sopenharmony_ci			}
12158c2ecf20Sopenharmony_ci		}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		if (!found) {
12188c2ecf20Sopenharmony_ci			/*
12198c2ecf20Sopenharmony_ci			 * Delete the filter which was previously set
12208c2ecf20Sopenharmony_ci			 * but not in the new request.
12218c2ecf20Sopenharmony_ci			 */
12228c2ecf20Sopenharmony_ci			ath6kl_dbg(ATH6KL_DBG_TRC,
12238c2ecf20Sopenharmony_ci				   "Removing %pM from multicast filter\n",
12248c2ecf20Sopenharmony_ci				   mc_filter->hw_addr);
12258c2ecf20Sopenharmony_ci			ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
12268c2ecf20Sopenharmony_ci					vif->fw_vif_idx, mc_filter->hw_addr,
12278c2ecf20Sopenharmony_ci					false);
12288c2ecf20Sopenharmony_ci			if (ret) {
12298c2ecf20Sopenharmony_ci				ath6kl_warn("Failed to remove multicast filter:%pM\n",
12308c2ecf20Sopenharmony_ci					    mc_filter->hw_addr);
12318c2ecf20Sopenharmony_ci				return;
12328c2ecf20Sopenharmony_ci			}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci			list_del(&mc_filter->list);
12358c2ecf20Sopenharmony_ci			kfree(mc_filter);
12368c2ecf20Sopenharmony_ci		}
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mc_filter_new);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, ndev) {
12428c2ecf20Sopenharmony_ci		found = false;
12438c2ecf20Sopenharmony_ci		list_for_each_entry(mc_filter, &vif->mc_filter, list) {
12448c2ecf20Sopenharmony_ci			if (memcmp(ha->addr, mc_filter->hw_addr,
12458c2ecf20Sopenharmony_ci				   ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
12468c2ecf20Sopenharmony_ci				found = true;
12478c2ecf20Sopenharmony_ci				break;
12488c2ecf20Sopenharmony_ci			}
12498c2ecf20Sopenharmony_ci		}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci		if (!found) {
12528c2ecf20Sopenharmony_ci			mc_filter = kzalloc(sizeof(struct ath6kl_mc_filter),
12538c2ecf20Sopenharmony_ci					    GFP_ATOMIC);
12548c2ecf20Sopenharmony_ci			if (!mc_filter) {
12558c2ecf20Sopenharmony_ci				WARN_ON(1);
12568c2ecf20Sopenharmony_ci				goto out;
12578c2ecf20Sopenharmony_ci			}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci			memcpy(mc_filter->hw_addr, ha->addr,
12608c2ecf20Sopenharmony_ci			       ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE);
12618c2ecf20Sopenharmony_ci			/* Set the multicast filter */
12628c2ecf20Sopenharmony_ci			ath6kl_dbg(ATH6KL_DBG_TRC,
12638c2ecf20Sopenharmony_ci				   "Adding %pM to multicast filter list\n",
12648c2ecf20Sopenharmony_ci				   mc_filter->hw_addr);
12658c2ecf20Sopenharmony_ci			ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
12668c2ecf20Sopenharmony_ci					vif->fw_vif_idx, mc_filter->hw_addr,
12678c2ecf20Sopenharmony_ci					true);
12688c2ecf20Sopenharmony_ci			if (ret) {
12698c2ecf20Sopenharmony_ci				ath6kl_warn("Failed to add multicast filter :%pM\n",
12708c2ecf20Sopenharmony_ci					    mc_filter->hw_addr);
12718c2ecf20Sopenharmony_ci				kfree(mc_filter);
12728c2ecf20Sopenharmony_ci				goto out;
12738c2ecf20Sopenharmony_ci			}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci			list_add_tail(&mc_filter->list, &mc_filter_new);
12768c2ecf20Sopenharmony_ci		}
12778c2ecf20Sopenharmony_ci	}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ciout:
12808c2ecf20Sopenharmony_ci	list_splice_tail(&mc_filter_new, &vif->mc_filter);
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic const struct net_device_ops ath6kl_netdev_ops = {
12848c2ecf20Sopenharmony_ci	.ndo_open               = ath6kl_open,
12858c2ecf20Sopenharmony_ci	.ndo_stop               = ath6kl_close,
12868c2ecf20Sopenharmony_ci	.ndo_start_xmit         = ath6kl_data_tx,
12878c2ecf20Sopenharmony_ci	.ndo_set_features       = ath6kl_set_features,
12888c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= ath6kl_set_multicast_list,
12898c2ecf20Sopenharmony_ci};
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_civoid init_netdev(struct net_device *dev)
12928c2ecf20Sopenharmony_ci{
12938c2ecf20Sopenharmony_ci	struct ath6kl *ar = ath6kl_priv(dev);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	dev->netdev_ops = &ath6kl_netdev_ops;
12968c2ecf20Sopenharmony_ci	dev->needs_free_netdev = true;
12978c2ecf20Sopenharmony_ci	dev->watchdog_timeo = ATH6KL_TX_TIMEOUT;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	dev->needed_headroom = ETH_HLEN;
13008c2ecf20Sopenharmony_ci	dev->needed_headroom += roundup(sizeof(struct ath6kl_llc_snap_hdr) +
13018c2ecf20Sopenharmony_ci					sizeof(struct wmi_data_hdr) +
13028c2ecf20Sopenharmony_ci					HTC_HDR_LENGTH +
13038c2ecf20Sopenharmony_ci					WMI_MAX_TX_META_SZ +
13048c2ecf20Sopenharmony_ci					ATH6KL_HTC_ALIGN_BYTES, 4);
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	if (!test_bit(ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM,
13078c2ecf20Sopenharmony_ci		      ar->fw_capabilities))
13088c2ecf20Sopenharmony_ci		dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	return;
13118c2ecf20Sopenharmony_ci}
1312