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