18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Mac80211 STA API for ST-Ericsson CW1200 drivers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson 68c2ecf20Sopenharmony_ci * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/firmware.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "cw1200.h" 168c2ecf20Sopenharmony_ci#include "sta.h" 178c2ecf20Sopenharmony_ci#include "fwio.h" 188c2ecf20Sopenharmony_ci#include "bh.h" 198c2ecf20Sopenharmony_ci#include "debug.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#ifndef ERP_INFO_BYTE_OFFSET 228c2ecf20Sopenharmony_ci#define ERP_INFO_BYTE_OFFSET 2 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void cw1200_do_join(struct cw1200_common *priv); 268c2ecf20Sopenharmony_cistatic void cw1200_do_unjoin(struct cw1200_common *priv); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int cw1200_upload_beacon(struct cw1200_common *priv); 298c2ecf20Sopenharmony_cistatic int cw1200_upload_pspoll(struct cw1200_common *priv); 308c2ecf20Sopenharmony_cistatic int cw1200_upload_null(struct cw1200_common *priv); 318c2ecf20Sopenharmony_cistatic int cw1200_upload_qosnull(struct cw1200_common *priv); 328c2ecf20Sopenharmony_cistatic int cw1200_start_ap(struct cw1200_common *priv); 338c2ecf20Sopenharmony_cistatic int cw1200_update_beaconing(struct cw1200_common *priv); 348c2ecf20Sopenharmony_cistatic int cw1200_enable_beaconing(struct cw1200_common *priv, 358c2ecf20Sopenharmony_ci bool enable); 368c2ecf20Sopenharmony_cistatic void __cw1200_sta_notify(struct ieee80211_hw *dev, 378c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 388c2ecf20Sopenharmony_ci enum sta_notify_cmd notify_cmd, 398c2ecf20Sopenharmony_ci int link_id); 408c2ecf20Sopenharmony_cistatic int __cw1200_flush(struct cw1200_common *priv, bool drop); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline void __cw1200_free_event_queue(struct list_head *list) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct cw1200_wsm_event *event, *tmp; 458c2ecf20Sopenharmony_ci list_for_each_entry_safe(event, tmp, list, link) { 468c2ecf20Sopenharmony_ci list_del(&event->link); 478c2ecf20Sopenharmony_ci kfree(event); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* ******************************************************************** */ 528c2ecf20Sopenharmony_ci/* STA API */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciint cw1200_start(struct ieee80211_hw *dev) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 578c2ecf20Sopenharmony_ci int ret = 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci cw1200_pm_stay_awake(&priv->pm_state, HZ); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* default EDCA */ 648c2ecf20Sopenharmony_ci WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); 658c2ecf20Sopenharmony_ci WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); 668c2ecf20Sopenharmony_ci WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); 678c2ecf20Sopenharmony_ci WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); 688c2ecf20Sopenharmony_ci ret = wsm_set_edca_params(priv, &priv->edca); 698c2ecf20Sopenharmony_ci if (ret) 708c2ecf20Sopenharmony_ci goto out; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = cw1200_set_uapsd_param(priv, &priv->edca); 738c2ecf20Sopenharmony_ci if (ret) 748c2ecf20Sopenharmony_ci goto out; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci priv->setbssparams_done = false; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); 798c2ecf20Sopenharmony_ci priv->mode = NL80211_IFTYPE_MONITOR; 808c2ecf20Sopenharmony_ci priv->wep_default_key_id = -1; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci priv->cqm_beacon_loss_count = 10; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = cw1200_setup_mac(priv); 858c2ecf20Sopenharmony_ci if (ret) 868c2ecf20Sopenharmony_ci goto out; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciout: 898c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_civoid cw1200_stop(struct ieee80211_hw *dev) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 968c2ecf20Sopenharmony_ci LIST_HEAD(list); 978c2ecf20Sopenharmony_ci int i; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci while (down_trylock(&priv->scan.lock)) { 1028c2ecf20Sopenharmony_ci /* Scan is in progress. Force it to stop. */ 1038c2ecf20Sopenharmony_ci priv->scan.req = NULL; 1048c2ecf20Sopenharmony_ci schedule(); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci up(&priv->scan.lock); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->scan.probe_work); 1098c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->scan.timeout); 1108c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->clear_recent_scan_work); 1118c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->join_timeout); 1128c2ecf20Sopenharmony_ci cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 1138c2ecf20Sopenharmony_ci cancel_work_sync(&priv->unjoin_work); 1148c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->link_id_gc_work); 1158c2ecf20Sopenharmony_ci flush_workqueue(priv->workqueue); 1168c2ecf20Sopenharmony_ci del_timer_sync(&priv->mcast_timeout); 1178c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 1188c2ecf20Sopenharmony_ci priv->mode = NL80211_IFTYPE_UNSPECIFIED; 1198c2ecf20Sopenharmony_ci priv->listening = false; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci spin_lock(&priv->event_queue_lock); 1228c2ecf20Sopenharmony_ci list_splice_init(&priv->event_queue, &list); 1238c2ecf20Sopenharmony_ci spin_unlock(&priv->event_queue_lock); 1248c2ecf20Sopenharmony_ci __cw1200_free_event_queue(&list); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 1288c2ecf20Sopenharmony_ci priv->join_pending = false; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 1318c2ecf20Sopenharmony_ci cw1200_queue_clear(&priv->tx_queue[i]); 1328c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 1338c2ecf20Sopenharmony_ci tx_policy_clean(priv); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* HACK! */ 1368c2ecf20Sopenharmony_ci if (atomic_xchg(&priv->tx_lock, 1) != 1) 1378c2ecf20Sopenharmony_ci pr_debug("[STA] TX is force-unlocked due to stop request.\n"); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 1408c2ecf20Sopenharmony_ci atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */ 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int cw1200_bssloss_mitigation = 1; 1448c2ecf20Sopenharmony_cimodule_param(cw1200_bssloss_mitigation, int, 0644); 1458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)"); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_civoid __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, 1498c2ecf20Sopenharmony_ci int init, int good, int bad) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci int tx = 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci priv->delayed_link_loss = 0; 1548c2ecf20Sopenharmony_ci cancel_work_sync(&priv->bss_params_work); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n", 1578c2ecf20Sopenharmony_ci priv->bss_loss_state, 1588c2ecf20Sopenharmony_ci init, good, bad, 1598c2ecf20Sopenharmony_ci atomic_read(&priv->tx_lock), 1608c2ecf20Sopenharmony_ci priv->delayed_unjoin); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* If we have a pending unjoin */ 1638c2ecf20Sopenharmony_ci if (priv->delayed_unjoin) 1648c2ecf20Sopenharmony_ci return; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (init) { 1678c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, 1688c2ecf20Sopenharmony_ci &priv->bss_loss_work, 1698c2ecf20Sopenharmony_ci HZ); 1708c2ecf20Sopenharmony_ci priv->bss_loss_state = 0; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Skip the confimration procedure in P2P case */ 1738c2ecf20Sopenharmony_ci if (!priv->vif->p2p && !atomic_read(&priv->tx_lock)) 1748c2ecf20Sopenharmony_ci tx = 1; 1758c2ecf20Sopenharmony_ci } else if (good) { 1768c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->bss_loss_work); 1778c2ecf20Sopenharmony_ci priv->bss_loss_state = 0; 1788c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->bss_params_work); 1798c2ecf20Sopenharmony_ci } else if (bad) { 1808c2ecf20Sopenharmony_ci /* XXX Should we just keep going until we time out? */ 1818c2ecf20Sopenharmony_ci if (priv->bss_loss_state < 3) 1828c2ecf20Sopenharmony_ci tx = 1; 1838c2ecf20Sopenharmony_ci } else { 1848c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->bss_loss_work); 1858c2ecf20Sopenharmony_ci priv->bss_loss_state = 0; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Bypass mitigation if it's disabled */ 1898c2ecf20Sopenharmony_ci if (!cw1200_bssloss_mitigation) 1908c2ecf20Sopenharmony_ci tx = 0; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Spit out a NULL packet to our AP if necessary */ 1938c2ecf20Sopenharmony_ci if (tx) { 1948c2ecf20Sopenharmony_ci struct sk_buff *skb; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci priv->bss_loss_state++; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); 1998c2ecf20Sopenharmony_ci WARN_ON(!skb); 2008c2ecf20Sopenharmony_ci if (skb) 2018c2ecf20Sopenharmony_ci cw1200_tx(priv->hw, NULL, skb); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciint cw1200_add_interface(struct ieee80211_hw *dev, 2068c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int ret; 2098c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 2108c2ecf20Sopenharmony_ci /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | 2138c2ecf20Sopenharmony_ci IEEE80211_VIF_SUPPORTS_UAPSD | 2148c2ecf20Sopenharmony_ci IEEE80211_VIF_SUPPORTS_CQM_RSSI; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (priv->mode != NL80211_IFTYPE_MONITOR) { 2198c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 2208c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci switch (vif->type) { 2248c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 2258c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 2268c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 2278c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 2288c2ecf20Sopenharmony_ci priv->mode = vif->type; 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci default: 2318c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 2328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci priv->vif = vif; 2368c2ecf20Sopenharmony_ci memcpy(priv->mac_addr, vif->addr, ETH_ALEN); 2378c2ecf20Sopenharmony_ci ret = cw1200_setup_mac(priv); 2388c2ecf20Sopenharmony_ci /* Enable auto-calibration */ 2398c2ecf20Sopenharmony_ci /* Exception in subsequent channel switch; disabled. 2408c2ecf20Sopenharmony_ci * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, 2418c2ecf20Sopenharmony_ci * &auto_calibration_mode, sizeof(auto_calibration_mode)); 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_civoid cw1200_remove_interface(struct ieee80211_hw *dev, 2498c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 2528c2ecf20Sopenharmony_ci struct wsm_reset reset = { 2538c2ecf20Sopenharmony_ci .reset_statistics = true, 2548c2ecf20Sopenharmony_ci }; 2558c2ecf20Sopenharmony_ci int i; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 2588c2ecf20Sopenharmony_ci switch (priv->join_status) { 2598c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_JOINING: 2608c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_PRE_STA: 2618c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_STA: 2628c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_IBSS: 2638c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 2648c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 2658c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_AP: 2688c2ecf20Sopenharmony_ci for (i = 0; priv->link_id_map; ++i) { 2698c2ecf20Sopenharmony_ci if (priv->link_id_map & BIT(i)) { 2708c2ecf20Sopenharmony_ci reset.link_id = i; 2718c2ecf20Sopenharmony_ci wsm_reset(priv, &reset); 2728c2ecf20Sopenharmony_ci priv->link_id_map &= ~BIT(i); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); 2768c2ecf20Sopenharmony_ci priv->sta_asleep_mask = 0; 2778c2ecf20Sopenharmony_ci priv->enable_beacon = false; 2788c2ecf20Sopenharmony_ci priv->tx_multicast = false; 2798c2ecf20Sopenharmony_ci priv->aid0_bit_set = false; 2808c2ecf20Sopenharmony_ci priv->buffered_multicasts = false; 2818c2ecf20Sopenharmony_ci priv->pspoll_mask = 0; 2828c2ecf20Sopenharmony_ci reset.link_id = 0; 2838c2ecf20Sopenharmony_ci wsm_reset(priv, &reset); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_MONITOR: 2868c2ecf20Sopenharmony_ci cw1200_update_listening(priv, false); 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci default: 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci priv->vif = NULL; 2928c2ecf20Sopenharmony_ci priv->mode = NL80211_IFTYPE_MONITOR; 2938c2ecf20Sopenharmony_ci eth_zero_addr(priv->mac_addr); 2948c2ecf20Sopenharmony_ci memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo)); 2958c2ecf20Sopenharmony_ci cw1200_free_keys(priv); 2968c2ecf20Sopenharmony_ci cw1200_setup_mac(priv); 2978c2ecf20Sopenharmony_ci priv->listening = false; 2988c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 2998c2ecf20Sopenharmony_ci if (!__cw1200_flush(priv, true)) 3008c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ciint cw1200_change_interface(struct ieee80211_hw *dev, 3068c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 3078c2ecf20Sopenharmony_ci enum nl80211_iftype new_type, 3088c2ecf20Sopenharmony_ci bool p2p) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci int ret = 0; 3118c2ecf20Sopenharmony_ci pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type, 3128c2ecf20Sopenharmony_ci p2p, vif->type, vif->p2p); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (new_type != vif->type || vif->p2p != p2p) { 3158c2ecf20Sopenharmony_ci cw1200_remove_interface(dev, vif); 3168c2ecf20Sopenharmony_ci vif->type = new_type; 3178c2ecf20Sopenharmony_ci vif->p2p = p2p; 3188c2ecf20Sopenharmony_ci ret = cw1200_add_interface(dev, vif); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ciint cw1200_config(struct ieee80211_hw *dev, u32 changed) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci int ret = 0; 3278c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 3288c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &dev->conf; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pr_debug("CONFIG CHANGED: %08x\n", changed); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci down(&priv->scan.lock); 3338c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 3348c2ecf20Sopenharmony_ci /* TODO: IEEE80211_CONF_CHANGE_QOS */ 3358c2ecf20Sopenharmony_ci /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_POWER) { 3388c2ecf20Sopenharmony_ci priv->output_power = conf->power_level; 3398c2ecf20Sopenharmony_ci pr_debug("[STA] TX power: %d\n", priv->output_power); 3408c2ecf20Sopenharmony_ci wsm_set_output_power(priv, priv->output_power * 10); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && 3448c2ecf20Sopenharmony_ci (priv->channel != conf->chandef.chan)) { 3458c2ecf20Sopenharmony_ci struct ieee80211_channel *ch = conf->chandef.chan; 3468c2ecf20Sopenharmony_ci struct wsm_switch_channel channel = { 3478c2ecf20Sopenharmony_ci .channel_number = ch->hw_value, 3488c2ecf20Sopenharmony_ci }; 3498c2ecf20Sopenharmony_ci pr_debug("[STA] Freq %d (wsm ch: %d).\n", 3508c2ecf20Sopenharmony_ci ch->center_freq, ch->hw_value); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* __cw1200_flush() implicitly locks tx, if successful */ 3538c2ecf20Sopenharmony_ci if (!__cw1200_flush(priv, false)) { 3548c2ecf20Sopenharmony_ci if (!wsm_switch_channel(priv, &channel)) { 3558c2ecf20Sopenharmony_ci ret = wait_event_timeout(priv->channel_switch_done, 3568c2ecf20Sopenharmony_ci !priv->channel_switch_in_progress, 3578c2ecf20Sopenharmony_ci 3 * HZ); 3588c2ecf20Sopenharmony_ci if (ret) { 3598c2ecf20Sopenharmony_ci /* Already unlocks if successful */ 3608c2ecf20Sopenharmony_ci priv->channel = ch; 3618c2ecf20Sopenharmony_ci ret = 0; 3628c2ecf20Sopenharmony_ci } else { 3638c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci } else { 3668c2ecf20Sopenharmony_ci /* Unlock if switch channel fails */ 3678c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_PS) { 3738c2ecf20Sopenharmony_ci if (!(conf->flags & IEEE80211_CONF_PS)) 3748c2ecf20Sopenharmony_ci priv->powersave_mode.mode = WSM_PSM_ACTIVE; 3758c2ecf20Sopenharmony_ci else if (conf->dynamic_ps_timeout <= 0) 3768c2ecf20Sopenharmony_ci priv->powersave_mode.mode = WSM_PSM_PS; 3778c2ecf20Sopenharmony_ci else 3788c2ecf20Sopenharmony_ci priv->powersave_mode.mode = WSM_PSM_FAST_PS; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Firmware requires that value for this 1-byte field must 3818c2ecf20Sopenharmony_ci * be specified in units of 500us. Values above the 128ms 3828c2ecf20Sopenharmony_ci * threshold are not supported. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci if (conf->dynamic_ps_timeout >= 0x80) 3858c2ecf20Sopenharmony_ci priv->powersave_mode.fast_psm_idle_period = 0xFF; 3868c2ecf20Sopenharmony_ci else 3878c2ecf20Sopenharmony_ci priv->powersave_mode.fast_psm_idle_period = 3888c2ecf20Sopenharmony_ci conf->dynamic_ps_timeout << 1; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_STA && 3918c2ecf20Sopenharmony_ci priv->bss_params.aid) 3928c2ecf20Sopenharmony_ci cw1200_set_pm(priv, &priv->powersave_mode); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_MONITOR) { 3968c2ecf20Sopenharmony_ci /* TBD: It looks like it's transparent 3978c2ecf20Sopenharmony_ci * there's a monitor interface present -- use this 3988c2ecf20Sopenharmony_ci * to determine for example whether to calculate 3998c2ecf20Sopenharmony_ci * timestamps for packets or not, do not use instead 4008c2ecf20Sopenharmony_ci * of filter flags! 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_IDLE) { 4058c2ecf20Sopenharmony_ci struct wsm_operational_mode mode = { 4068c2ecf20Sopenharmony_ci .power_mode = cw1200_power_mode, 4078c2ecf20Sopenharmony_ci .disable_more_flag_usage = true, 4088c2ecf20Sopenharmony_ci }; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 4118c2ecf20Sopenharmony_ci /* Disable p2p-dev mode forced by TX request */ 4128c2ecf20Sopenharmony_ci if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && 4138c2ecf20Sopenharmony_ci (conf->flags & IEEE80211_CONF_IDLE) && 4148c2ecf20Sopenharmony_ci !priv->listening) { 4158c2ecf20Sopenharmony_ci cw1200_disable_listening(priv); 4168c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci wsm_set_operational_mode(priv, &mode); 4198c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { 4238c2ecf20Sopenharmony_ci pr_debug("[STA] Retry limits: %d (long), %d (short).\n", 4248c2ecf20Sopenharmony_ci conf->long_frame_max_tx_count, 4258c2ecf20Sopenharmony_ci conf->short_frame_max_tx_count); 4268c2ecf20Sopenharmony_ci spin_lock_bh(&priv->tx_policy_cache.lock); 4278c2ecf20Sopenharmony_ci priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; 4288c2ecf20Sopenharmony_ci priv->short_frame_max_tx_count = 4298c2ecf20Sopenharmony_ci (conf->short_frame_max_tx_count < 0x0F) ? 4308c2ecf20Sopenharmony_ci conf->short_frame_max_tx_count : 0x0F; 4318c2ecf20Sopenharmony_ci priv->hw->max_rate_tries = priv->short_frame_max_tx_count; 4328c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->tx_policy_cache.lock); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 4358c2ecf20Sopenharmony_ci up(&priv->scan.lock); 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_civoid cw1200_update_filtering(struct cw1200_common *priv) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci int ret; 4428c2ecf20Sopenharmony_ci bool bssid_filtering = !priv->rx_filter.bssid; 4438c2ecf20Sopenharmony_ci bool is_p2p = priv->vif && priv->vif->p2p; 4448c2ecf20Sopenharmony_ci bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci static struct wsm_beacon_filter_control bf_ctrl; 4478c2ecf20Sopenharmony_ci static struct wsm_mib_beacon_filter_table bf_tbl = { 4488c2ecf20Sopenharmony_ci .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC, 4498c2ecf20Sopenharmony_ci .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | 4508c2ecf20Sopenharmony_ci WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | 4518c2ecf20Sopenharmony_ci WSM_BEACON_FILTER_IE_HAS_APPEARED, 4528c2ecf20Sopenharmony_ci .entry[0].oui[0] = 0x50, 4538c2ecf20Sopenharmony_ci .entry[0].oui[1] = 0x6F, 4548c2ecf20Sopenharmony_ci .entry[0].oui[2] = 0x9A, 4558c2ecf20Sopenharmony_ci .entry[1].ie_id = WLAN_EID_HT_OPERATION, 4568c2ecf20Sopenharmony_ci .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | 4578c2ecf20Sopenharmony_ci WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | 4588c2ecf20Sopenharmony_ci WSM_BEACON_FILTER_IE_HAS_APPEARED, 4598c2ecf20Sopenharmony_ci .entry[2].ie_id = WLAN_EID_ERP_INFO, 4608c2ecf20Sopenharmony_ci .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | 4618c2ecf20Sopenharmony_ci WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | 4628c2ecf20Sopenharmony_ci WSM_BEACON_FILTER_IE_HAS_APPEARED, 4638c2ecf20Sopenharmony_ci }; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) 4668c2ecf20Sopenharmony_ci return; 4678c2ecf20Sopenharmony_ci else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) 4688c2ecf20Sopenharmony_ci bssid_filtering = false; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (priv->disable_beacon_filter) { 4718c2ecf20Sopenharmony_ci bf_ctrl.enabled = 0; 4728c2ecf20Sopenharmony_ci bf_ctrl.bcn_count = 1; 4738c2ecf20Sopenharmony_ci bf_tbl.num = __cpu_to_le32(0); 4748c2ecf20Sopenharmony_ci } else if (is_p2p || !is_sta) { 4758c2ecf20Sopenharmony_ci bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE | 4768c2ecf20Sopenharmony_ci WSM_BEACON_FILTER_AUTO_ERP; 4778c2ecf20Sopenharmony_ci bf_ctrl.bcn_count = 0; 4788c2ecf20Sopenharmony_ci bf_tbl.num = __cpu_to_le32(2); 4798c2ecf20Sopenharmony_ci } else { 4808c2ecf20Sopenharmony_ci bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE; 4818c2ecf20Sopenharmony_ci bf_ctrl.bcn_count = 0; 4828c2ecf20Sopenharmony_ci bf_tbl.num = __cpu_to_le32(3); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* When acting as p2p client being connected to p2p GO, in order to 4868c2ecf20Sopenharmony_ci * receive frames from a different p2p device, turn off bssid filter. 4878c2ecf20Sopenharmony_ci * 4888c2ecf20Sopenharmony_ci * WARNING: FW dependency! 4898c2ecf20Sopenharmony_ci * This can only be used with FW WSM371 and its successors. 4908c2ecf20Sopenharmony_ci * In that FW version even with bssid filter turned off, 4918c2ecf20Sopenharmony_ci * device will block most of the unwanted frames. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci if (is_p2p) 4948c2ecf20Sopenharmony_ci bssid_filtering = false; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ret = wsm_set_rx_filter(priv, &priv->rx_filter); 4978c2ecf20Sopenharmony_ci if (!ret) 4988c2ecf20Sopenharmony_ci ret = wsm_set_beacon_filter_table(priv, &bf_tbl); 4998c2ecf20Sopenharmony_ci if (!ret) 5008c2ecf20Sopenharmony_ci ret = wsm_beacon_filter_control(priv, &bf_ctrl); 5018c2ecf20Sopenharmony_ci if (!ret) 5028c2ecf20Sopenharmony_ci ret = wsm_set_bssid_filtering(priv, bssid_filtering); 5038c2ecf20Sopenharmony_ci if (!ret) 5048c2ecf20Sopenharmony_ci ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); 5058c2ecf20Sopenharmony_ci if (ret) 5068c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 5078c2ecf20Sopenharmony_ci "Update filtering failed: %d.\n", ret); 5088c2ecf20Sopenharmony_ci return; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_civoid cw1200_update_filtering_work(struct work_struct *work) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct cw1200_common *priv = 5148c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, 5158c2ecf20Sopenharmony_ci update_filtering_work); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci cw1200_update_filtering(priv); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_civoid cw1200_set_beacon_wakeup_period_work(struct work_struct *work) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct cw1200_common *priv = 5238c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, 5248c2ecf20Sopenharmony_ci set_beacon_wakeup_period_work); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci wsm_set_beacon_wakeup_period(priv, 5278c2ecf20Sopenharmony_ci priv->beacon_int * priv->join_dtim_period > 5288c2ecf20Sopenharmony_ci MAX_BEACON_SKIP_TIME_MS ? 1 : 5298c2ecf20Sopenharmony_ci priv->join_dtim_period, 0); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ciu64 cw1200_prepare_multicast(struct ieee80211_hw *hw, 5338c2ecf20Sopenharmony_ci struct netdev_hw_addr_list *mc_list) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci static u8 broadcast_ipv6[ETH_ALEN] = { 5368c2ecf20Sopenharmony_ci 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 5378c2ecf20Sopenharmony_ci }; 5388c2ecf20Sopenharmony_ci static u8 broadcast_ipv4[ETH_ALEN] = { 5398c2ecf20Sopenharmony_ci 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 5408c2ecf20Sopenharmony_ci }; 5418c2ecf20Sopenharmony_ci struct cw1200_common *priv = hw->priv; 5428c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 5438c2ecf20Sopenharmony_ci int count = 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Disable multicast filtering */ 5468c2ecf20Sopenharmony_ci priv->has_multicast_subscription = false; 5478c2ecf20Sopenharmony_ci memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Enable if requested */ 5538c2ecf20Sopenharmony_ci netdev_hw_addr_list_for_each(ha, mc_list) { 5548c2ecf20Sopenharmony_ci pr_debug("[STA] multicast: %pM\n", ha->addr); 5558c2ecf20Sopenharmony_ci memcpy(&priv->multicast_filter.macaddrs[count], 5568c2ecf20Sopenharmony_ci ha->addr, ETH_ALEN); 5578c2ecf20Sopenharmony_ci if (!ether_addr_equal(ha->addr, broadcast_ipv4) && 5588c2ecf20Sopenharmony_ci !ether_addr_equal(ha->addr, broadcast_ipv6)) 5598c2ecf20Sopenharmony_ci priv->has_multicast_subscription = true; 5608c2ecf20Sopenharmony_ci count++; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (count) { 5648c2ecf20Sopenharmony_ci priv->multicast_filter.enable = __cpu_to_le32(1); 5658c2ecf20Sopenharmony_ci priv->multicast_filter.num_addrs = __cpu_to_le32(count); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return netdev_hw_addr_list_count(mc_list); 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_civoid cw1200_configure_filter(struct ieee80211_hw *dev, 5728c2ecf20Sopenharmony_ci unsigned int changed_flags, 5738c2ecf20Sopenharmony_ci unsigned int *total_flags, 5748c2ecf20Sopenharmony_ci u64 multicast) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 5778c2ecf20Sopenharmony_ci bool listening = !!(*total_flags & 5788c2ecf20Sopenharmony_ci (FIF_OTHER_BSS | 5798c2ecf20Sopenharmony_ci FIF_BCN_PRBRESP_PROMISC | 5808c2ecf20Sopenharmony_ci FIF_PROBE_REQ)); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci *total_flags &= FIF_OTHER_BSS | 5838c2ecf20Sopenharmony_ci FIF_FCSFAIL | 5848c2ecf20Sopenharmony_ci FIF_BCN_PRBRESP_PROMISC | 5858c2ecf20Sopenharmony_ci FIF_PROBE_REQ; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci down(&priv->scan.lock); 5888c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci priv->rx_filter.promiscuous = 0; 5918c2ecf20Sopenharmony_ci priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | 5928c2ecf20Sopenharmony_ci FIF_PROBE_REQ)) ? 1 : 0; 5938c2ecf20Sopenharmony_ci priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; 5948c2ecf20Sopenharmony_ci priv->disable_beacon_filter = !(*total_flags & 5958c2ecf20Sopenharmony_ci (FIF_BCN_PRBRESP_PROMISC | 5968c2ecf20Sopenharmony_ci FIF_PROBE_REQ)); 5978c2ecf20Sopenharmony_ci if (priv->listening != listening) { 5988c2ecf20Sopenharmony_ci priv->listening = listening; 5998c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 6008c2ecf20Sopenharmony_ci cw1200_update_listening(priv, listening); 6018c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci cw1200_update_filtering(priv); 6048c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 6058c2ecf20Sopenharmony_ci up(&priv->scan.lock); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ciint cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, 6098c2ecf20Sopenharmony_ci u16 queue, const struct ieee80211_tx_queue_params *params) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 6128c2ecf20Sopenharmony_ci int ret = 0; 6138c2ecf20Sopenharmony_ci /* To prevent re-applying PM request OID again and again*/ 6148c2ecf20Sopenharmony_ci bool old_uapsd_flags; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (queue < dev->queues) { 6198c2ecf20Sopenharmony_ci old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); 6228c2ecf20Sopenharmony_ci ret = wsm_set_tx_queue_params(priv, 6238c2ecf20Sopenharmony_ci &priv->tx_queue_params.params[queue], queue); 6248c2ecf20Sopenharmony_ci if (ret) { 6258c2ecf20Sopenharmony_ci ret = -EINVAL; 6268c2ecf20Sopenharmony_ci goto out; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci WSM_EDCA_SET(&priv->edca, queue, params->aifs, 6308c2ecf20Sopenharmony_ci params->cw_min, params->cw_max, 6318c2ecf20Sopenharmony_ci params->txop, 0xc8, 6328c2ecf20Sopenharmony_ci params->uapsd); 6338c2ecf20Sopenharmony_ci ret = wsm_set_edca_params(priv, &priv->edca); 6348c2ecf20Sopenharmony_ci if (ret) { 6358c2ecf20Sopenharmony_ci ret = -EINVAL; 6368c2ecf20Sopenharmony_ci goto out; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_STATION) { 6408c2ecf20Sopenharmony_ci ret = cw1200_set_uapsd_param(priv, &priv->edca); 6418c2ecf20Sopenharmony_ci if (!ret && priv->setbssparams_done && 6428c2ecf20Sopenharmony_ci (priv->join_status == CW1200_JOIN_STATUS_STA) && 6438c2ecf20Sopenharmony_ci (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags))) 6448c2ecf20Sopenharmony_ci ret = cw1200_set_pm(priv, &priv->powersave_mode); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci } else { 6478c2ecf20Sopenharmony_ci ret = -EINVAL; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ciout: 6518c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 6528c2ecf20Sopenharmony_ci return ret; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ciint cw1200_get_stats(struct ieee80211_hw *dev, 6568c2ecf20Sopenharmony_ci struct ieee80211_low_level_stats *stats) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci memcpy(stats, &priv->stats, sizeof(*stats)); 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ciint cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct wsm_set_pm pm = *arg; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (priv->uapsd_info.uapsd_flags != 0) 6698c2ecf20Sopenharmony_ci pm.mode &= ~WSM_PSM_FAST_PS_FLAG; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (memcmp(&pm, &priv->firmware_ps_mode, 6728c2ecf20Sopenharmony_ci sizeof(struct wsm_set_pm))) { 6738c2ecf20Sopenharmony_ci priv->firmware_ps_mode = pm; 6748c2ecf20Sopenharmony_ci return wsm_set_pm(priv, &pm); 6758c2ecf20Sopenharmony_ci } else { 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ciint cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, 6818c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, struct ieee80211_sta *sta, 6828c2ecf20Sopenharmony_ci struct ieee80211_key_conf *key) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci int ret = -EOPNOTSUPP; 6858c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 6868c2ecf20Sopenharmony_ci struct ieee80211_key_seq seq; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (cmd == SET_KEY) { 6918c2ecf20Sopenharmony_ci u8 *peer_addr = NULL; 6928c2ecf20Sopenharmony_ci int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? 6938c2ecf20Sopenharmony_ci 1 : 0; 6948c2ecf20Sopenharmony_ci int idx = cw1200_alloc_key(priv); 6958c2ecf20Sopenharmony_ci struct wsm_add_key *wsm_key = &priv->keys[idx]; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (idx < 0) { 6988c2ecf20Sopenharmony_ci ret = -EINVAL; 6998c2ecf20Sopenharmony_ci goto finally; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (sta) 7038c2ecf20Sopenharmony_ci peer_addr = sta->addr; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | 7068c2ecf20Sopenharmony_ci IEEE80211_KEY_FLAG_RESERVE_TAILROOM; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci switch (key->cipher) { 7098c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 7108c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 7118c2ecf20Sopenharmony_ci if (key->keylen > 16) { 7128c2ecf20Sopenharmony_ci cw1200_free_key(priv, idx); 7138c2ecf20Sopenharmony_ci ret = -EINVAL; 7148c2ecf20Sopenharmony_ci goto finally; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (pairwise) { 7188c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; 7198c2ecf20Sopenharmony_ci memcpy(wsm_key->wep_pairwise.peer, 7208c2ecf20Sopenharmony_ci peer_addr, ETH_ALEN); 7218c2ecf20Sopenharmony_ci memcpy(wsm_key->wep_pairwise.keydata, 7228c2ecf20Sopenharmony_ci &key->key[0], key->keylen); 7238c2ecf20Sopenharmony_ci wsm_key->wep_pairwise.keylen = key->keylen; 7248c2ecf20Sopenharmony_ci } else { 7258c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; 7268c2ecf20Sopenharmony_ci memcpy(wsm_key->wep_group.keydata, 7278c2ecf20Sopenharmony_ci &key->key[0], key->keylen); 7288c2ecf20Sopenharmony_ci wsm_key->wep_group.keylen = key->keylen; 7298c2ecf20Sopenharmony_ci wsm_key->wep_group.keyid = key->keyidx; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci break; 7328c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 7338c2ecf20Sopenharmony_ci ieee80211_get_key_rx_seq(key, 0, &seq); 7348c2ecf20Sopenharmony_ci if (pairwise) { 7358c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; 7368c2ecf20Sopenharmony_ci memcpy(wsm_key->tkip_pairwise.peer, 7378c2ecf20Sopenharmony_ci peer_addr, ETH_ALEN); 7388c2ecf20Sopenharmony_ci memcpy(wsm_key->tkip_pairwise.keydata, 7398c2ecf20Sopenharmony_ci &key->key[0], 16); 7408c2ecf20Sopenharmony_ci memcpy(wsm_key->tkip_pairwise.tx_mic_key, 7418c2ecf20Sopenharmony_ci &key->key[16], 8); 7428c2ecf20Sopenharmony_ci memcpy(wsm_key->tkip_pairwise.rx_mic_key, 7438c2ecf20Sopenharmony_ci &key->key[24], 8); 7448c2ecf20Sopenharmony_ci } else { 7458c2ecf20Sopenharmony_ci size_t mic_offset = 7468c2ecf20Sopenharmony_ci (priv->mode == NL80211_IFTYPE_AP) ? 7478c2ecf20Sopenharmony_ci 16 : 24; 7488c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; 7498c2ecf20Sopenharmony_ci memcpy(wsm_key->tkip_group.keydata, 7508c2ecf20Sopenharmony_ci &key->key[0], 16); 7518c2ecf20Sopenharmony_ci memcpy(wsm_key->tkip_group.rx_mic_key, 7528c2ecf20Sopenharmony_ci &key->key[mic_offset], 8); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff; 7558c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff; 7568c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff; 7578c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff; 7588c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff; 7598c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff; 7608c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[6] = 0; 7618c2ecf20Sopenharmony_ci wsm_key->tkip_group.rx_seqnum[7] = 0; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci wsm_key->tkip_group.keyid = key->keyidx; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci break; 7668c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 7678c2ecf20Sopenharmony_ci ieee80211_get_key_rx_seq(key, 0, &seq); 7688c2ecf20Sopenharmony_ci if (pairwise) { 7698c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; 7708c2ecf20Sopenharmony_ci memcpy(wsm_key->aes_pairwise.peer, 7718c2ecf20Sopenharmony_ci peer_addr, ETH_ALEN); 7728c2ecf20Sopenharmony_ci memcpy(wsm_key->aes_pairwise.keydata, 7738c2ecf20Sopenharmony_ci &key->key[0], 16); 7748c2ecf20Sopenharmony_ci } else { 7758c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_AES_GROUP; 7768c2ecf20Sopenharmony_ci memcpy(wsm_key->aes_group.keydata, 7778c2ecf20Sopenharmony_ci &key->key[0], 16); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5]; 7808c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4]; 7818c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3]; 7828c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2]; 7838c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1]; 7848c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0]; 7858c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[6] = 0; 7868c2ecf20Sopenharmony_ci wsm_key->aes_group.rx_seqnum[7] = 0; 7878c2ecf20Sopenharmony_ci wsm_key->aes_group.keyid = key->keyidx; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci break; 7908c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_SMS4: 7918c2ecf20Sopenharmony_ci if (pairwise) { 7928c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; 7938c2ecf20Sopenharmony_ci memcpy(wsm_key->wapi_pairwise.peer, 7948c2ecf20Sopenharmony_ci peer_addr, ETH_ALEN); 7958c2ecf20Sopenharmony_ci memcpy(wsm_key->wapi_pairwise.keydata, 7968c2ecf20Sopenharmony_ci &key->key[0], 16); 7978c2ecf20Sopenharmony_ci memcpy(wsm_key->wapi_pairwise.mic_key, 7988c2ecf20Sopenharmony_ci &key->key[16], 16); 7998c2ecf20Sopenharmony_ci wsm_key->wapi_pairwise.keyid = key->keyidx; 8008c2ecf20Sopenharmony_ci } else { 8018c2ecf20Sopenharmony_ci wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; 8028c2ecf20Sopenharmony_ci memcpy(wsm_key->wapi_group.keydata, 8038c2ecf20Sopenharmony_ci &key->key[0], 16); 8048c2ecf20Sopenharmony_ci memcpy(wsm_key->wapi_group.mic_key, 8058c2ecf20Sopenharmony_ci &key->key[16], 16); 8068c2ecf20Sopenharmony_ci wsm_key->wapi_group.keyid = key->keyidx; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci default: 8108c2ecf20Sopenharmony_ci pr_warn("Unhandled key type %d\n", key->cipher); 8118c2ecf20Sopenharmony_ci cw1200_free_key(priv, idx); 8128c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 8138c2ecf20Sopenharmony_ci goto finally; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci ret = wsm_add_key(priv, wsm_key); 8168c2ecf20Sopenharmony_ci if (!ret) 8178c2ecf20Sopenharmony_ci key->hw_key_idx = idx; 8188c2ecf20Sopenharmony_ci else 8198c2ecf20Sopenharmony_ci cw1200_free_key(priv, idx); 8208c2ecf20Sopenharmony_ci } else if (cmd == DISABLE_KEY) { 8218c2ecf20Sopenharmony_ci struct wsm_remove_key wsm_key = { 8228c2ecf20Sopenharmony_ci .index = key->hw_key_idx, 8238c2ecf20Sopenharmony_ci }; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (wsm_key.index > WSM_KEY_MAX_INDEX) { 8268c2ecf20Sopenharmony_ci ret = -EINVAL; 8278c2ecf20Sopenharmony_ci goto finally; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci cw1200_free_key(priv, wsm_key.index); 8318c2ecf20Sopenharmony_ci ret = wsm_remove_key(priv, &wsm_key); 8328c2ecf20Sopenharmony_ci } else { 8338c2ecf20Sopenharmony_ci pr_warn("Unhandled key command %d\n", cmd); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cifinally: 8378c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 8388c2ecf20Sopenharmony_ci return ret; 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_civoid cw1200_wep_key_work(struct work_struct *work) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci struct cw1200_common *priv = 8448c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, wep_key_work); 8458c2ecf20Sopenharmony_ci u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); 8468c2ecf20Sopenharmony_ci struct cw1200_queue *queue = &priv->tx_queue[queue_id]; 8478c2ecf20Sopenharmony_ci __le32 wep_default_key_id = __cpu_to_le32( 8488c2ecf20Sopenharmony_ci priv->wep_default_key_id); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci pr_debug("[STA] Setting default WEP key: %d\n", 8518c2ecf20Sopenharmony_ci priv->wep_default_key_id); 8528c2ecf20Sopenharmony_ci wsm_flush_tx(priv); 8538c2ecf20Sopenharmony_ci wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, 8548c2ecf20Sopenharmony_ci &wep_default_key_id, sizeof(wep_default_key_id)); 8558c2ecf20Sopenharmony_ci cw1200_queue_requeue(queue, priv->pending_frame_id); 8568c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ciint cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci int ret = 0; 8628c2ecf20Sopenharmony_ci __le32 val32; 8638c2ecf20Sopenharmony_ci struct cw1200_common *priv = hw->priv; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) 8668c2ecf20Sopenharmony_ci return 0; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (value != (u32) -1) 8698c2ecf20Sopenharmony_ci val32 = __cpu_to_le32(value); 8708c2ecf20Sopenharmony_ci else 8718c2ecf20Sopenharmony_ci val32 = 0; /* disabled */ 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (priv->rts_threshold == value) 8748c2ecf20Sopenharmony_ci goto out; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci pr_debug("[STA] Setting RTS threshold: %d\n", 8778c2ecf20Sopenharmony_ci priv->rts_threshold); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* mutex_lock(&priv->conf_mutex); */ 8808c2ecf20Sopenharmony_ci ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, 8818c2ecf20Sopenharmony_ci &val32, sizeof(val32)); 8828c2ecf20Sopenharmony_ci if (!ret) 8838c2ecf20Sopenharmony_ci priv->rts_threshold = value; 8848c2ecf20Sopenharmony_ci /* mutex_unlock(&priv->conf_mutex); */ 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ciout: 8878c2ecf20Sopenharmony_ci return ret; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/* If successful, LOCKS the TX queue! */ 8918c2ecf20Sopenharmony_cistatic int __cw1200_flush(struct cw1200_common *priv, bool drop) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci int i, ret; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci for (;;) { 8968c2ecf20Sopenharmony_ci /* TODO: correct flush handling is required when dev_stop. 8978c2ecf20Sopenharmony_ci * Temporary workaround: 2s 8988c2ecf20Sopenharmony_ci */ 8998c2ecf20Sopenharmony_ci if (drop) { 9008c2ecf20Sopenharmony_ci for (i = 0; i < 4; ++i) 9018c2ecf20Sopenharmony_ci cw1200_queue_clear(&priv->tx_queue[i]); 9028c2ecf20Sopenharmony_ci } else { 9038c2ecf20Sopenharmony_ci ret = wait_event_timeout( 9048c2ecf20Sopenharmony_ci priv->tx_queue_stats.wait_link_id_empty, 9058c2ecf20Sopenharmony_ci cw1200_queue_stats_is_empty( 9068c2ecf20Sopenharmony_ci &priv->tx_queue_stats, -1), 9078c2ecf20Sopenharmony_ci 2 * HZ); 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (!drop && ret <= 0) { 9118c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci } else { 9148c2ecf20Sopenharmony_ci ret = 0; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 9188c2ecf20Sopenharmony_ci if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) { 9198c2ecf20Sopenharmony_ci /* Highly unlikely: WSM requeued frames. */ 9208c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 9218c2ecf20Sopenharmony_ci continue; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci break; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci return ret; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_civoid cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 9298c2ecf20Sopenharmony_ci u32 queues, bool drop) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci struct cw1200_common *priv = hw->priv; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci switch (priv->mode) { 9348c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 9358c2ecf20Sopenharmony_ci drop = true; 9368c2ecf20Sopenharmony_ci break; 9378c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 9388c2ecf20Sopenharmony_ci if (!priv->enable_beacon) 9398c2ecf20Sopenharmony_ci drop = true; 9408c2ecf20Sopenharmony_ci break; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (!__cw1200_flush(priv, drop)) 9448c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci/* ******************************************************************** */ 9508c2ecf20Sopenharmony_ci/* WSM callbacks */ 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_civoid cw1200_free_event_queue(struct cw1200_common *priv) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci LIST_HEAD(list); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci spin_lock(&priv->event_queue_lock); 9578c2ecf20Sopenharmony_ci list_splice_init(&priv->event_queue, &list); 9588c2ecf20Sopenharmony_ci spin_unlock(&priv->event_queue_lock); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci __cw1200_free_event_queue(&list); 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_civoid cw1200_event_handler(struct work_struct *work) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct cw1200_common *priv = 9668c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, event_handler); 9678c2ecf20Sopenharmony_ci struct cw1200_wsm_event *event; 9688c2ecf20Sopenharmony_ci LIST_HEAD(list); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci spin_lock(&priv->event_queue_lock); 9718c2ecf20Sopenharmony_ci list_splice_init(&priv->event_queue, &list); 9728c2ecf20Sopenharmony_ci spin_unlock(&priv->event_queue_lock); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci list_for_each_entry(event, &list, link) { 9758c2ecf20Sopenharmony_ci switch (event->evt.id) { 9768c2ecf20Sopenharmony_ci case WSM_EVENT_ERROR: 9778c2ecf20Sopenharmony_ci pr_err("Unhandled WSM Error from LMAC\n"); 9788c2ecf20Sopenharmony_ci break; 9798c2ecf20Sopenharmony_ci case WSM_EVENT_BSS_LOST: 9808c2ecf20Sopenharmony_ci pr_debug("[CQM] BSS lost.\n"); 9818c2ecf20Sopenharmony_ci cancel_work_sync(&priv->unjoin_work); 9828c2ecf20Sopenharmony_ci if (!down_trylock(&priv->scan.lock)) { 9838c2ecf20Sopenharmony_ci cw1200_cqm_bssloss_sm(priv, 1, 0, 0); 9848c2ecf20Sopenharmony_ci up(&priv->scan.lock); 9858c2ecf20Sopenharmony_ci } else { 9868c2ecf20Sopenharmony_ci /* Scan is in progress. Delay reporting. 9878c2ecf20Sopenharmony_ci * Scan complete will trigger bss_loss_work 9888c2ecf20Sopenharmony_ci */ 9898c2ecf20Sopenharmony_ci priv->delayed_link_loss = 1; 9908c2ecf20Sopenharmony_ci /* Also start a watchdog. */ 9918c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, 9928c2ecf20Sopenharmony_ci &priv->bss_loss_work, 5*HZ); 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci case WSM_EVENT_BSS_REGAINED: 9968c2ecf20Sopenharmony_ci pr_debug("[CQM] BSS regained.\n"); 9978c2ecf20Sopenharmony_ci cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 9988c2ecf20Sopenharmony_ci cancel_work_sync(&priv->unjoin_work); 9998c2ecf20Sopenharmony_ci break; 10008c2ecf20Sopenharmony_ci case WSM_EVENT_RADAR_DETECTED: 10018c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, "radar pulse detected\n"); 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci case WSM_EVENT_RCPI_RSSI: 10048c2ecf20Sopenharmony_ci { 10058c2ecf20Sopenharmony_ci /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 10068c2ecf20Sopenharmony_ci * RSSI = RCPI / 2 - 110 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci int rcpi_rssi = (int)(event->evt.data & 0xFF); 10098c2ecf20Sopenharmony_ci int cqm_evt; 10108c2ecf20Sopenharmony_ci if (priv->cqm_use_rssi) 10118c2ecf20Sopenharmony_ci rcpi_rssi = (s8)rcpi_rssi; 10128c2ecf20Sopenharmony_ci else 10138c2ecf20Sopenharmony_ci rcpi_rssi = rcpi_rssi / 2 - 110; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ? 10168c2ecf20Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : 10178c2ecf20Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; 10188c2ecf20Sopenharmony_ci pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi); 10198c2ecf20Sopenharmony_ci ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, rcpi_rssi, 10208c2ecf20Sopenharmony_ci GFP_KERNEL); 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci case WSM_EVENT_BT_INACTIVE: 10248c2ecf20Sopenharmony_ci pr_warn("Unhandled BT INACTIVE from LMAC\n"); 10258c2ecf20Sopenharmony_ci break; 10268c2ecf20Sopenharmony_ci case WSM_EVENT_BT_ACTIVE: 10278c2ecf20Sopenharmony_ci pr_warn("Unhandled BT ACTIVE from LMAC\n"); 10288c2ecf20Sopenharmony_ci break; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci __cw1200_free_event_queue(&list); 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_civoid cw1200_bss_loss_work(struct work_struct *work) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci struct cw1200_common *priv = 10378c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, bss_loss_work.work); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci pr_debug("[CQM] Reporting connection loss.\n"); 10408c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 10418c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 10428c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_civoid cw1200_bss_params_work(struct work_struct *work) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci struct cw1200_common *priv = 10488c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, bss_params_work); 10498c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci priv->bss_params.reset_beacon_loss = 1; 10528c2ecf20Sopenharmony_ci wsm_set_bss_params(priv, &priv->bss_params); 10538c2ecf20Sopenharmony_ci priv->bss_params.reset_beacon_loss = 0; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci/* ******************************************************************** */ 10598c2ecf20Sopenharmony_ci/* Internal API */ 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci/* This function is called to Parse the SDD file 10628c2ecf20Sopenharmony_ci * to extract listen_interval and PTA related information 10638c2ecf20Sopenharmony_ci * sdd is a TLV: u8 id, u8 len, u8 data[] 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_cistatic int cw1200_parse_sdd_file(struct cw1200_common *priv) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci const u8 *p = priv->sdd->data; 10688c2ecf20Sopenharmony_ci int ret = 0; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci while (p + 2 <= priv->sdd->data + priv->sdd->size) { 10718c2ecf20Sopenharmony_ci if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) { 10728c2ecf20Sopenharmony_ci pr_warn("Malformed sdd structure\n"); 10738c2ecf20Sopenharmony_ci return -1; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci switch (p[0]) { 10768c2ecf20Sopenharmony_ci case SDD_PTA_CFG_ELT_ID: { 10778c2ecf20Sopenharmony_ci u16 v; 10788c2ecf20Sopenharmony_ci if (p[1] < 4) { 10798c2ecf20Sopenharmony_ci pr_warn("SDD_PTA_CFG_ELT_ID malformed\n"); 10808c2ecf20Sopenharmony_ci ret = -1; 10818c2ecf20Sopenharmony_ci break; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci v = le16_to_cpu(*((__le16 *)(p + 2))); 10848c2ecf20Sopenharmony_ci if (!v) /* non-zero means this is enabled */ 10858c2ecf20Sopenharmony_ci break; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci v = le16_to_cpu(*((__le16 *)(p + 4))); 10888c2ecf20Sopenharmony_ci priv->conf_listen_interval = (v >> 7) & 0x1F; 10898c2ecf20Sopenharmony_ci pr_debug("PTA found; Listen Interval %d\n", 10908c2ecf20Sopenharmony_ci priv->conf_listen_interval); 10918c2ecf20Sopenharmony_ci break; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci case SDD_REFERENCE_FREQUENCY_ELT_ID: { 10948c2ecf20Sopenharmony_ci u16 clk = le16_to_cpu(*((__le16 *)(p + 2))); 10958c2ecf20Sopenharmony_ci if (clk != priv->hw_refclk) 10968c2ecf20Sopenharmony_ci pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n", 10978c2ecf20Sopenharmony_ci clk, priv->hw_refclk); 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci default: 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci p += p[1] + 2; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci if (!priv->bt_present) { 11078c2ecf20Sopenharmony_ci pr_debug("PTA element NOT found.\n"); 11088c2ecf20Sopenharmony_ci priv->conf_listen_interval = 0; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci return ret; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ciint cw1200_setup_mac(struct cw1200_common *priv) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci int ret = 0; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* NOTE: There is a bug in FW: it reports signal 11188c2ecf20Sopenharmony_ci * as RSSI if RSSI subscription is enabled. 11198c2ecf20Sopenharmony_ci * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. 11208c2ecf20Sopenharmony_ci * 11218c2ecf20Sopenharmony_ci * NOTE2: RSSI based reports have been switched to RCPI, since 11228c2ecf20Sopenharmony_ci * FW has a bug and RSSI reported values are not stable, 11238c2ecf20Sopenharmony_ci * what can lead to signal level oscilations in user-end applications 11248c2ecf20Sopenharmony_ci */ 11258c2ecf20Sopenharmony_ci struct wsm_rcpi_rssi_threshold threshold = { 11268c2ecf20Sopenharmony_ci .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | 11278c2ecf20Sopenharmony_ci WSM_RCPI_RSSI_DONT_USE_UPPER | 11288c2ecf20Sopenharmony_ci WSM_RCPI_RSSI_DONT_USE_LOWER, 11298c2ecf20Sopenharmony_ci .rollingAverageCount = 16, 11308c2ecf20Sopenharmony_ci }; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci struct wsm_configuration cfg = { 11338c2ecf20Sopenharmony_ci .dot11StationId = &priv->mac_addr[0], 11348c2ecf20Sopenharmony_ci }; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci /* Remember the decission here to make sure, we will handle 11378c2ecf20Sopenharmony_ci * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) 11408c2ecf20Sopenharmony_ci priv->cqm_use_rssi = true; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (!priv->sdd) { 11438c2ecf20Sopenharmony_ci ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev); 11448c2ecf20Sopenharmony_ci if (ret) { 11458c2ecf20Sopenharmony_ci pr_err("Can't load sdd file %s.\n", priv->sdd_path); 11468c2ecf20Sopenharmony_ci return ret; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci cw1200_parse_sdd_file(priv); 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci cfg.dpdData = priv->sdd->data; 11528c2ecf20Sopenharmony_ci cfg.dpdData_size = priv->sdd->size; 11538c2ecf20Sopenharmony_ci ret = wsm_configuration(priv, &cfg); 11548c2ecf20Sopenharmony_ci if (ret) 11558c2ecf20Sopenharmony_ci return ret; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci /* Configure RSSI/SCPI reporting as RSSI. */ 11588c2ecf20Sopenharmony_ci wsm_set_rcpi_rssi_threshold(priv, &threshold); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return 0; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic void cw1200_join_complete(struct cw1200_common *priv) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci priv->join_pending = false; 11688c2ecf20Sopenharmony_ci if (priv->join_complete_status) { 11698c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 11708c2ecf20Sopenharmony_ci cw1200_update_listening(priv, priv->listening); 11718c2ecf20Sopenharmony_ci cw1200_do_unjoin(priv); 11728c2ecf20Sopenharmony_ci ieee80211_connection_loss(priv->vif); 11738c2ecf20Sopenharmony_ci } else { 11748c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_ADHOC) 11758c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_IBSS; 11768c2ecf20Sopenharmony_ci else 11778c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PRE_STA; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */ 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_civoid cw1200_join_complete_work(struct work_struct *work) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci struct cw1200_common *priv = 11858c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, join_complete_work); 11868c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 11878c2ecf20Sopenharmony_ci cw1200_join_complete(priv); 11888c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_civoid cw1200_join_complete_cb(struct cw1200_common *priv, 11928c2ecf20Sopenharmony_ci struct wsm_join_complete *arg) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n", 11958c2ecf20Sopenharmony_ci arg->status); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (cancel_delayed_work(&priv->join_timeout)) { 11988c2ecf20Sopenharmony_ci priv->join_complete_status = arg->status; 11998c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->join_complete_work); 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci/* MUST be called with tx_lock held! It will be unlocked for us. */ 12048c2ecf20Sopenharmony_cistatic void cw1200_do_join(struct cw1200_common *priv) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci const u8 *bssid; 12078c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; 12088c2ecf20Sopenharmony_ci struct cfg80211_bss *bss = NULL; 12098c2ecf20Sopenharmony_ci struct wsm_protected_mgmt_policy mgmt_policy; 12108c2ecf20Sopenharmony_ci struct wsm_join join = { 12118c2ecf20Sopenharmony_ci .mode = conf->ibss_joined ? 12128c2ecf20Sopenharmony_ci WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, 12138c2ecf20Sopenharmony_ci .preamble_type = WSM_JOIN_PREAMBLE_LONG, 12148c2ecf20Sopenharmony_ci .probe_for_join = 1, 12158c2ecf20Sopenharmony_ci .atim_window = 0, 12168c2ecf20Sopenharmony_ci .basic_rate_set = cw1200_rate_mask_to_wsm(priv, 12178c2ecf20Sopenharmony_ci conf->basic_rates), 12188c2ecf20Sopenharmony_ci }; 12198c2ecf20Sopenharmony_ci if (delayed_work_pending(&priv->join_timeout)) { 12208c2ecf20Sopenharmony_ci pr_warn("[STA] - Join request already pending, skipping..\n"); 12218c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 12228c2ecf20Sopenharmony_ci return; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (priv->join_status) 12268c2ecf20Sopenharmony_ci cw1200_do_unjoin(priv); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci bssid = priv->vif->bss_conf.bssid; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0, 12318c2ecf20Sopenharmony_ci IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if (!bss && !conf->ibss_joined) { 12348c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 12358c2ecf20Sopenharmony_ci return; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci /* Under the conf lock: check scan status and 12418c2ecf20Sopenharmony_ci * bail out if it is in progress. 12428c2ecf20Sopenharmony_ci */ 12438c2ecf20Sopenharmony_ci if (atomic_read(&priv->scan.in_progress)) { 12448c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 12458c2ecf20Sopenharmony_ci goto done_put; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci priv->join_pending = true; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* Sanity check basic rates */ 12518c2ecf20Sopenharmony_ci if (!join.basic_rate_set) 12528c2ecf20Sopenharmony_ci join.basic_rate_set = 7; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci /* Sanity check beacon interval */ 12558c2ecf20Sopenharmony_ci if (!priv->beacon_int) 12568c2ecf20Sopenharmony_ci priv->beacon_int = 1; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci join.beacon_interval = priv->beacon_int; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* BT Coex related changes */ 12618c2ecf20Sopenharmony_ci if (priv->bt_present) { 12628c2ecf20Sopenharmony_ci if (((priv->conf_listen_interval * 100) % 12638c2ecf20Sopenharmony_ci priv->beacon_int) == 0) 12648c2ecf20Sopenharmony_ci priv->listen_interval = 12658c2ecf20Sopenharmony_ci ((priv->conf_listen_interval * 100) / 12668c2ecf20Sopenharmony_ci priv->beacon_int); 12678c2ecf20Sopenharmony_ci else 12688c2ecf20Sopenharmony_ci priv->listen_interval = 12698c2ecf20Sopenharmony_ci ((priv->conf_listen_interval * 100) / 12708c2ecf20Sopenharmony_ci priv->beacon_int + 1); 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (priv->hw->conf.ps_dtim_period) 12748c2ecf20Sopenharmony_ci priv->join_dtim_period = priv->hw->conf.ps_dtim_period; 12758c2ecf20Sopenharmony_ci join.dtim_period = priv->join_dtim_period; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci join.channel_number = priv->channel->hw_value; 12788c2ecf20Sopenharmony_ci join.band = (priv->channel->band == NL80211_BAND_5GHZ) ? 12798c2ecf20Sopenharmony_ci WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci memcpy(join.bssid, bssid, sizeof(join.bssid)); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n", 12848c2ecf20Sopenharmony_ci join.bssid, 12858c2ecf20Sopenharmony_ci join.dtim_period, priv->beacon_int); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci if (!conf->ibss_joined) { 12888c2ecf20Sopenharmony_ci const u8 *ssidie; 12898c2ecf20Sopenharmony_ci rcu_read_lock(); 12908c2ecf20Sopenharmony_ci ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); 12918c2ecf20Sopenharmony_ci if (ssidie) { 12928c2ecf20Sopenharmony_ci join.ssid_len = ssidie[1]; 12938c2ecf20Sopenharmony_ci memcpy(join.ssid, &ssidie[2], join.ssid_len); 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci rcu_read_unlock(); 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (priv->vif->p2p) { 12998c2ecf20Sopenharmony_ci join.flags |= WSM_JOIN_FLAGS_P2P_GO; 13008c2ecf20Sopenharmony_ci join.basic_rate_set = 13018c2ecf20Sopenharmony_ci cw1200_rate_mask_to_wsm(priv, 0xFF0); 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci /* Enable asynchronous join calls */ 13058c2ecf20Sopenharmony_ci if (!conf->ibss_joined) { 13068c2ecf20Sopenharmony_ci join.flags |= WSM_JOIN_FLAGS_FORCE; 13078c2ecf20Sopenharmony_ci join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci wsm_flush_tx(priv); 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci /* Stay Awake for Join and Auth Timeouts and a bit more */ 13138c2ecf20Sopenharmony_ci cw1200_pm_stay_awake(&priv->pm_state, 13148c2ecf20Sopenharmony_ci CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci cw1200_update_listening(priv, false); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci /* Turn on Block ACKs */ 13198c2ecf20Sopenharmony_ci wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask, 13208c2ecf20Sopenharmony_ci priv->ba_rx_tid_mask); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* Set up timeout */ 13238c2ecf20Sopenharmony_ci if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) { 13248c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_JOINING; 13258c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, 13268c2ecf20Sopenharmony_ci &priv->join_timeout, 13278c2ecf20Sopenharmony_ci CW1200_JOIN_TIMEOUT); 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci /* 802.11w protected mgmt frames */ 13318c2ecf20Sopenharmony_ci mgmt_policy.protectedMgmtEnable = 0; 13328c2ecf20Sopenharmony_ci mgmt_policy.unprotectedMgmtFramesAllowed = 1; 13338c2ecf20Sopenharmony_ci mgmt_policy.encryptionForAuthFrame = 1; 13348c2ecf20Sopenharmony_ci wsm_set_protected_mgmt_policy(priv, &mgmt_policy); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci /* Perform actual join */ 13378c2ecf20Sopenharmony_ci if (wsm_join(priv, &join)) { 13388c2ecf20Sopenharmony_ci pr_err("[STA] cw1200_join_work: wsm_join failed!\n"); 13398c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->join_timeout); 13408c2ecf20Sopenharmony_ci cw1200_update_listening(priv, priv->listening); 13418c2ecf20Sopenharmony_ci /* Tx lock still held, unjoin will clear it. */ 13428c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 13438c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 13448c2ecf20Sopenharmony_ci } else { 13458c2ecf20Sopenharmony_ci if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND)) 13468c2ecf20Sopenharmony_ci cw1200_join_complete(priv); /* Will clear tx_lock */ 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* Upload keys */ 13498c2ecf20Sopenharmony_ci cw1200_upload_keys(priv); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci /* Due to beacon filtering it is possible that the 13528c2ecf20Sopenharmony_ci * AP's beacon is not known for the mac80211 stack. 13538c2ecf20Sopenharmony_ci * Disable filtering temporary to make sure the stack 13548c2ecf20Sopenharmony_ci * receives at least one 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_ci priv->disable_beacon_filter = true; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci cw1200_update_filtering(priv); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_cidone_put: 13618c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 13628c2ecf20Sopenharmony_ci if (bss) 13638c2ecf20Sopenharmony_ci cfg80211_put_bss(priv->hw->wiphy, bss); 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_civoid cw1200_join_timeout(struct work_struct *work) 13678c2ecf20Sopenharmony_ci{ 13688c2ecf20Sopenharmony_ci struct cw1200_common *priv = 13698c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, join_timeout.work); 13708c2ecf20Sopenharmony_ci pr_debug("[WSM] Join timed out.\n"); 13718c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 13728c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 13738c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic void cw1200_do_unjoin(struct cw1200_common *priv) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci struct wsm_reset reset = { 13798c2ecf20Sopenharmony_ci .reset_statistics = true, 13808c2ecf20Sopenharmony_ci }; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->join_timeout); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 13858c2ecf20Sopenharmony_ci priv->join_pending = false; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci if (atomic_read(&priv->scan.in_progress)) { 13888c2ecf20Sopenharmony_ci if (priv->delayed_unjoin) 13898c2ecf20Sopenharmony_ci wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n"); 13908c2ecf20Sopenharmony_ci else 13918c2ecf20Sopenharmony_ci priv->delayed_unjoin = true; 13928c2ecf20Sopenharmony_ci goto done; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci priv->delayed_link_loss = false; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (!priv->join_status) 13988c2ecf20Sopenharmony_ci goto done; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_AP) 14018c2ecf20Sopenharmony_ci goto done; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci cancel_work_sync(&priv->update_filtering_work); 14048c2ecf20Sopenharmony_ci cancel_work_sync(&priv->set_beacon_wakeup_period_work); 14058c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci /* Unjoin is a reset. */ 14088c2ecf20Sopenharmony_ci wsm_flush_tx(priv); 14098c2ecf20Sopenharmony_ci wsm_keep_alive_period(priv, 0); 14108c2ecf20Sopenharmony_ci wsm_reset(priv, &reset); 14118c2ecf20Sopenharmony_ci wsm_set_output_power(priv, priv->output_power * 10); 14128c2ecf20Sopenharmony_ci priv->join_dtim_period = 0; 14138c2ecf20Sopenharmony_ci cw1200_setup_mac(priv); 14148c2ecf20Sopenharmony_ci cw1200_free_event_queue(priv); 14158c2ecf20Sopenharmony_ci cancel_work_sync(&priv->event_handler); 14168c2ecf20Sopenharmony_ci cw1200_update_listening(priv, priv->listening); 14178c2ecf20Sopenharmony_ci cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci /* Disable Block ACKs */ 14208c2ecf20Sopenharmony_ci wsm_set_block_ack_policy(priv, 0, 0); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci priv->disable_beacon_filter = false; 14238c2ecf20Sopenharmony_ci cw1200_update_filtering(priv); 14248c2ecf20Sopenharmony_ci memset(&priv->association_mode, 0, 14258c2ecf20Sopenharmony_ci sizeof(priv->association_mode)); 14268c2ecf20Sopenharmony_ci memset(&priv->bss_params, 0, sizeof(priv->bss_params)); 14278c2ecf20Sopenharmony_ci priv->setbssparams_done = false; 14288c2ecf20Sopenharmony_ci memset(&priv->firmware_ps_mode, 0, 14298c2ecf20Sopenharmony_ci sizeof(priv->firmware_ps_mode)); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci pr_debug("[STA] Unjoin completed.\n"); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cidone: 14348c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_civoid cw1200_unjoin_work(struct work_struct *work) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci struct cw1200_common *priv = 14408c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, unjoin_work); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci cw1200_do_unjoin(priv); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* Tell the stack we're dead */ 14458c2ecf20Sopenharmony_ci ieee80211_connection_loss(priv->vif); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ciint cw1200_enable_listening(struct cw1200_common *priv) 14518c2ecf20Sopenharmony_ci{ 14528c2ecf20Sopenharmony_ci struct wsm_start start = { 14538c2ecf20Sopenharmony_ci .mode = WSM_START_MODE_P2P_DEV, 14548c2ecf20Sopenharmony_ci .band = WSM_PHY_BAND_2_4G, 14558c2ecf20Sopenharmony_ci .beacon_interval = 100, 14568c2ecf20Sopenharmony_ci .dtim_period = 1, 14578c2ecf20Sopenharmony_ci .probe_delay = 0, 14588c2ecf20Sopenharmony_ci .basic_rate_set = 0x0F, 14598c2ecf20Sopenharmony_ci }; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (priv->channel) { 14628c2ecf20Sopenharmony_ci start.band = priv->channel->band == NL80211_BAND_5GHZ ? 14638c2ecf20Sopenharmony_ci WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; 14648c2ecf20Sopenharmony_ci start.channel_number = priv->channel->hw_value; 14658c2ecf20Sopenharmony_ci } else { 14668c2ecf20Sopenharmony_ci start.band = WSM_PHY_BAND_2_4G; 14678c2ecf20Sopenharmony_ci start.channel_number = 1; 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci return wsm_start(priv, &start); 14718c2ecf20Sopenharmony_ci} 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ciint cw1200_disable_listening(struct cw1200_common *priv) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci int ret; 14768c2ecf20Sopenharmony_ci struct wsm_reset reset = { 14778c2ecf20Sopenharmony_ci .reset_statistics = true, 14788c2ecf20Sopenharmony_ci }; 14798c2ecf20Sopenharmony_ci ret = wsm_reset(priv, &reset); 14808c2ecf20Sopenharmony_ci return ret; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_civoid cw1200_update_listening(struct cw1200_common *priv, bool enabled) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci if (enabled) { 14868c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) { 14878c2ecf20Sopenharmony_ci if (!cw1200_enable_listening(priv)) 14888c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_MONITOR; 14898c2ecf20Sopenharmony_ci wsm_set_probe_responder(priv, true); 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci } else { 14928c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { 14938c2ecf20Sopenharmony_ci if (!cw1200_disable_listening(priv)) 14948c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 14958c2ecf20Sopenharmony_ci wsm_set_probe_responder(priv, false); 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci} 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ciint cw1200_set_uapsd_param(struct cw1200_common *priv, 15018c2ecf20Sopenharmony_ci const struct wsm_edca_params *arg) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci int ret; 15048c2ecf20Sopenharmony_ci u16 uapsd_flags = 0; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci /* Here's the mapping AC [queue, bit] 15078c2ecf20Sopenharmony_ci * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] 15088c2ecf20Sopenharmony_ci */ 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (arg->uapsd_enable[0]) 15118c2ecf20Sopenharmony_ci uapsd_flags |= 1 << 3; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci if (arg->uapsd_enable[1]) 15148c2ecf20Sopenharmony_ci uapsd_flags |= 1 << 2; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (arg->uapsd_enable[2]) 15178c2ecf20Sopenharmony_ci uapsd_flags |= 1 << 1; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci if (arg->uapsd_enable[3]) 15208c2ecf20Sopenharmony_ci uapsd_flags |= 1; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci /* Currently pseudo U-APSD operation is not supported, so setting 15238c2ecf20Sopenharmony_ci * MinAutoTriggerInterval, MaxAutoTriggerInterval and 15248c2ecf20Sopenharmony_ci * AutoTriggerStep to 0 15258c2ecf20Sopenharmony_ci */ 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags); 15288c2ecf20Sopenharmony_ci priv->uapsd_info.min_auto_trigger_interval = 0; 15298c2ecf20Sopenharmony_ci priv->uapsd_info.max_auto_trigger_interval = 0; 15308c2ecf20Sopenharmony_ci priv->uapsd_info.auto_trigger_step = 0; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); 15338c2ecf20Sopenharmony_ci return ret; 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci/* ******************************************************************** */ 15378c2ecf20Sopenharmony_ci/* AP API */ 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ciint cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 15408c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 15418c2ecf20Sopenharmony_ci{ 15428c2ecf20Sopenharmony_ci struct cw1200_common *priv = hw->priv; 15438c2ecf20Sopenharmony_ci struct cw1200_sta_priv *sta_priv = 15448c2ecf20Sopenharmony_ci (struct cw1200_sta_priv *)&sta->drv_priv; 15458c2ecf20Sopenharmony_ci struct cw1200_link_entry *entry; 15468c2ecf20Sopenharmony_ci struct sk_buff *skb; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (priv->mode != NL80211_IFTYPE_AP) 15498c2ecf20Sopenharmony_ci return 0; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); 15528c2ecf20Sopenharmony_ci if (WARN_ON(!sta_priv->link_id)) { 15538c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, 15548c2ecf20Sopenharmony_ci "[AP] No more link IDs available.\n"); 15558c2ecf20Sopenharmony_ci return -ENOENT; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci entry = &priv->link_id_db[sta_priv->link_id - 1]; 15598c2ecf20Sopenharmony_ci spin_lock_bh(&priv->ps_state_lock); 15608c2ecf20Sopenharmony_ci if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == 15618c2ecf20Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) 15628c2ecf20Sopenharmony_ci priv->sta_asleep_mask |= BIT(sta_priv->link_id); 15638c2ecf20Sopenharmony_ci entry->status = CW1200_LINK_HARD; 15648c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&entry->rx_queue))) 15658c2ecf20Sopenharmony_ci ieee80211_rx_irqsafe(priv->hw, skb); 15668c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->ps_state_lock); 15678c2ecf20Sopenharmony_ci return 0; 15688c2ecf20Sopenharmony_ci} 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ciint cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 15718c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 15728c2ecf20Sopenharmony_ci{ 15738c2ecf20Sopenharmony_ci struct cw1200_common *priv = hw->priv; 15748c2ecf20Sopenharmony_ci struct cw1200_sta_priv *sta_priv = 15758c2ecf20Sopenharmony_ci (struct cw1200_sta_priv *)&sta->drv_priv; 15768c2ecf20Sopenharmony_ci struct cw1200_link_entry *entry; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) 15798c2ecf20Sopenharmony_ci return 0; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci entry = &priv->link_id_db[sta_priv->link_id - 1]; 15828c2ecf20Sopenharmony_ci spin_lock_bh(&priv->ps_state_lock); 15838c2ecf20Sopenharmony_ci entry->status = CW1200_LINK_RESERVE; 15848c2ecf20Sopenharmony_ci entry->timestamp = jiffies; 15858c2ecf20Sopenharmony_ci wsm_lock_tx_async(priv); 15868c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) 15878c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 15888c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->ps_state_lock); 15898c2ecf20Sopenharmony_ci flush_workqueue(priv->workqueue); 15908c2ecf20Sopenharmony_ci return 0; 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic void __cw1200_sta_notify(struct ieee80211_hw *dev, 15948c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 15958c2ecf20Sopenharmony_ci enum sta_notify_cmd notify_cmd, 15968c2ecf20Sopenharmony_ci int link_id) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 15998c2ecf20Sopenharmony_ci u32 bit, prev; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci /* Zero link id means "for all link IDs" */ 16028c2ecf20Sopenharmony_ci if (link_id) 16038c2ecf20Sopenharmony_ci bit = BIT(link_id); 16048c2ecf20Sopenharmony_ci else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) 16058c2ecf20Sopenharmony_ci bit = 0; 16068c2ecf20Sopenharmony_ci else 16078c2ecf20Sopenharmony_ci bit = priv->link_id_map; 16088c2ecf20Sopenharmony_ci prev = priv->sta_asleep_mask & bit; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci switch (notify_cmd) { 16118c2ecf20Sopenharmony_ci case STA_NOTIFY_SLEEP: 16128c2ecf20Sopenharmony_ci if (!prev) { 16138c2ecf20Sopenharmony_ci if (priv->buffered_multicasts && 16148c2ecf20Sopenharmony_ci !priv->sta_asleep_mask) 16158c2ecf20Sopenharmony_ci queue_work(priv->workqueue, 16168c2ecf20Sopenharmony_ci &priv->multicast_start_work); 16178c2ecf20Sopenharmony_ci priv->sta_asleep_mask |= bit; 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci break; 16208c2ecf20Sopenharmony_ci case STA_NOTIFY_AWAKE: 16218c2ecf20Sopenharmony_ci if (prev) { 16228c2ecf20Sopenharmony_ci priv->sta_asleep_mask &= ~bit; 16238c2ecf20Sopenharmony_ci priv->pspoll_mask &= ~bit; 16248c2ecf20Sopenharmony_ci if (priv->tx_multicast && link_id && 16258c2ecf20Sopenharmony_ci !priv->sta_asleep_mask) 16268c2ecf20Sopenharmony_ci queue_work(priv->workqueue, 16278c2ecf20Sopenharmony_ci &priv->multicast_stop_work); 16288c2ecf20Sopenharmony_ci cw1200_bh_wakeup(priv); 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_civoid cw1200_sta_notify(struct ieee80211_hw *dev, 16358c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 16368c2ecf20Sopenharmony_ci enum sta_notify_cmd notify_cmd, 16378c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 16408c2ecf20Sopenharmony_ci struct cw1200_sta_priv *sta_priv = 16418c2ecf20Sopenharmony_ci (struct cw1200_sta_priv *)&sta->drv_priv; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci spin_lock_bh(&priv->ps_state_lock); 16448c2ecf20Sopenharmony_ci __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); 16458c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->ps_state_lock); 16468c2ecf20Sopenharmony_ci} 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_cistatic void cw1200_ps_notify(struct cw1200_common *priv, 16498c2ecf20Sopenharmony_ci int link_id, bool ps) 16508c2ecf20Sopenharmony_ci{ 16518c2ecf20Sopenharmony_ci if (link_id > CW1200_MAX_STA_IN_AP_MODE) 16528c2ecf20Sopenharmony_ci return; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n", 16558c2ecf20Sopenharmony_ci ps ? "Stop" : "Start", 16568c2ecf20Sopenharmony_ci link_id, priv->sta_asleep_mask); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci __cw1200_sta_notify(priv->hw, priv->vif, 16598c2ecf20Sopenharmony_ci ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); 16608c2ecf20Sopenharmony_ci} 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_cistatic int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci struct sk_buff *skb; 16658c2ecf20Sopenharmony_ci struct wsm_update_ie update_ie = { 16668c2ecf20Sopenharmony_ci .what = WSM_UPDATE_IE_BEACON, 16678c2ecf20Sopenharmony_ci .count = 1, 16688c2ecf20Sopenharmony_ci }; 16698c2ecf20Sopenharmony_ci u16 tim_offset, tim_length; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis"); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, 16748c2ecf20Sopenharmony_ci &tim_offset, &tim_length); 16758c2ecf20Sopenharmony_ci if (!skb) { 16768c2ecf20Sopenharmony_ci if (!__cw1200_flush(priv, true)) 16778c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 16788c2ecf20Sopenharmony_ci return -ENOENT; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci if (tim_offset && tim_length >= 6) { 16828c2ecf20Sopenharmony_ci /* Ignore DTIM count from mac80211: 16838c2ecf20Sopenharmony_ci * firmware handles DTIM internally. 16848c2ecf20Sopenharmony_ci */ 16858c2ecf20Sopenharmony_ci skb->data[tim_offset + 2] = 0; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci /* Set/reset aid0 bit */ 16888c2ecf20Sopenharmony_ci if (aid0_bit_set) 16898c2ecf20Sopenharmony_ci skb->data[tim_offset + 4] |= 1; 16908c2ecf20Sopenharmony_ci else 16918c2ecf20Sopenharmony_ci skb->data[tim_offset + 4] &= ~1; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci update_ie.ies = &skb->data[tim_offset]; 16958c2ecf20Sopenharmony_ci update_ie.length = tim_length; 16968c2ecf20Sopenharmony_ci wsm_update_ie(priv, &update_ie); 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci return 0; 17018c2ecf20Sopenharmony_ci} 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_civoid cw1200_set_tim_work(struct work_struct *work) 17048c2ecf20Sopenharmony_ci{ 17058c2ecf20Sopenharmony_ci struct cw1200_common *priv = 17068c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, set_tim_work); 17078c2ecf20Sopenharmony_ci (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ciint cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, 17118c2ecf20Sopenharmony_ci bool set) 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 17148c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->set_tim_work); 17158c2ecf20Sopenharmony_ci return 0; 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_civoid cw1200_set_cts_work(struct work_struct *work) 17198c2ecf20Sopenharmony_ci{ 17208c2ecf20Sopenharmony_ci struct cw1200_common *priv = 17218c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, set_cts_work); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; 17248c2ecf20Sopenharmony_ci struct wsm_update_ie update_ie = { 17258c2ecf20Sopenharmony_ci .what = WSM_UPDATE_IE_BEACON, 17268c2ecf20Sopenharmony_ci .count = 1, 17278c2ecf20Sopenharmony_ci .ies = erp_ie, 17288c2ecf20Sopenharmony_ci .length = 3, 17298c2ecf20Sopenharmony_ci }; 17308c2ecf20Sopenharmony_ci u32 erp_info; 17318c2ecf20Sopenharmony_ci __le32 use_cts_prot; 17328c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 17338c2ecf20Sopenharmony_ci erp_info = priv->erp_info; 17348c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 17358c2ecf20Sopenharmony_ci use_cts_prot = 17368c2ecf20Sopenharmony_ci erp_info & WLAN_ERP_USE_PROTECTION ? 17378c2ecf20Sopenharmony_ci __cpu_to_le32(1) : 0; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci pr_debug("[STA] ERP information 0x%x\n", erp_info); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, 17448c2ecf20Sopenharmony_ci &use_cts_prot, sizeof(use_cts_prot)); 17458c2ecf20Sopenharmony_ci wsm_update_ie(priv, &update_ie); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci return; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic int cw1200_set_btcoexinfo(struct cw1200_common *priv) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci struct wsm_override_internal_txrate arg; 17538c2ecf20Sopenharmony_ci int ret = 0; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_STATION) { 17568c2ecf20Sopenharmony_ci /* Plumb PSPOLL and NULL template */ 17578c2ecf20Sopenharmony_ci cw1200_upload_pspoll(priv); 17588c2ecf20Sopenharmony_ci cw1200_upload_null(priv); 17598c2ecf20Sopenharmony_ci cw1200_upload_qosnull(priv); 17608c2ecf20Sopenharmony_ci } else { 17618c2ecf20Sopenharmony_ci return 0; 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci if (!priv->vif->p2p) { 17678c2ecf20Sopenharmony_ci /* STATION mode */ 17688c2ecf20Sopenharmony_ci if (priv->bss_params.operational_rate_set & ~0xF) { 17698c2ecf20Sopenharmony_ci pr_debug("[STA] STA has ERP rates\n"); 17708c2ecf20Sopenharmony_ci /* G or BG mode */ 17718c2ecf20Sopenharmony_ci arg.internalTxRate = (__ffs( 17728c2ecf20Sopenharmony_ci priv->bss_params.operational_rate_set & ~0xF)); 17738c2ecf20Sopenharmony_ci } else { 17748c2ecf20Sopenharmony_ci pr_debug("[STA] STA has non ERP rates\n"); 17758c2ecf20Sopenharmony_ci /* B only mode */ 17768c2ecf20Sopenharmony_ci arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); 17798c2ecf20Sopenharmony_ci } else { 17808c2ecf20Sopenharmony_ci /* P2P mode */ 17818c2ecf20Sopenharmony_ci arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); 17828c2ecf20Sopenharmony_ci arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", 17868c2ecf20Sopenharmony_ci priv->mode, 17878c2ecf20Sopenharmony_ci arg.internalTxRate, 17888c2ecf20Sopenharmony_ci arg.nonErpInternalTxRate); 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, 17918c2ecf20Sopenharmony_ci &arg, sizeof(arg)); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci return ret; 17948c2ecf20Sopenharmony_ci} 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_civoid cw1200_bss_info_changed(struct ieee80211_hw *dev, 17978c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 17988c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *info, 17998c2ecf20Sopenharmony_ci u32 changed) 18008c2ecf20Sopenharmony_ci{ 18018c2ecf20Sopenharmony_ci struct cw1200_common *priv = dev->priv; 18028c2ecf20Sopenharmony_ci bool do_join = false; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci pr_debug("BSS CHANGED: %08x\n", changed); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci /* TODO: BSS_CHANGED_QOS */ 18098c2ecf20Sopenharmony_ci /* TODO: BSS_CHANGED_TXPOWER */ 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_ARP_FILTER) { 18128c2ecf20Sopenharmony_ci struct wsm_mib_arp_ipv4_filter filter = {0}; 18138c2ecf20Sopenharmony_ci int i; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", 18168c2ecf20Sopenharmony_ci info->arp_addr_cnt); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci /* Currently only one IP address is supported by firmware. 18198c2ecf20Sopenharmony_ci * In case of more IPs arp filtering will be disabled. 18208c2ecf20Sopenharmony_ci */ 18218c2ecf20Sopenharmony_ci if (info->arp_addr_cnt > 0 && 18228c2ecf20Sopenharmony_ci info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { 18238c2ecf20Sopenharmony_ci for (i = 0; i < info->arp_addr_cnt; i++) { 18248c2ecf20Sopenharmony_ci filter.ipv4addrs[i] = info->arp_addr_list[i]; 18258c2ecf20Sopenharmony_ci pr_debug("[STA] addr[%d]: 0x%X\n", 18268c2ecf20Sopenharmony_ci i, filter.ipv4addrs[i]); 18278c2ecf20Sopenharmony_ci } 18288c2ecf20Sopenharmony_ci filter.enable = __cpu_to_le32(1); 18298c2ecf20Sopenharmony_ci } 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci pr_debug("[STA] arp ip filter enable: %d\n", 18328c2ecf20Sopenharmony_ci __le32_to_cpu(filter.enable)); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci wsm_set_arp_ipv4_filter(priv, &filter); 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci if (changed & 18388c2ecf20Sopenharmony_ci (BSS_CHANGED_BEACON | 18398c2ecf20Sopenharmony_ci BSS_CHANGED_AP_PROBE_RESP | 18408c2ecf20Sopenharmony_ci BSS_CHANGED_BSSID | 18418c2ecf20Sopenharmony_ci BSS_CHANGED_SSID | 18428c2ecf20Sopenharmony_ci BSS_CHANGED_IBSS)) { 18438c2ecf20Sopenharmony_ci pr_debug("BSS_CHANGED_BEACON\n"); 18448c2ecf20Sopenharmony_ci priv->beacon_int = info->beacon_int; 18458c2ecf20Sopenharmony_ci cw1200_update_beaconing(priv); 18468c2ecf20Sopenharmony_ci cw1200_upload_beacon(priv); 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_BEACON_ENABLED) { 18508c2ecf20Sopenharmony_ci pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci if (priv->enable_beacon != info->enable_beacon) { 18538c2ecf20Sopenharmony_ci cw1200_enable_beaconing(priv, info->enable_beacon); 18548c2ecf20Sopenharmony_ci priv->enable_beacon = info->enable_beacon; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci } 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_BEACON_INT) { 18598c2ecf20Sopenharmony_ci pr_debug("CHANGED_BEACON_INT\n"); 18608c2ecf20Sopenharmony_ci if (info->ibss_joined) 18618c2ecf20Sopenharmony_ci do_join = true; 18628c2ecf20Sopenharmony_ci else if (priv->join_status == CW1200_JOIN_STATUS_AP) 18638c2ecf20Sopenharmony_ci cw1200_update_beaconing(priv); 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci /* assoc/disassoc, or maybe AID changed */ 18678c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_ASSOC) { 18688c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 18698c2ecf20Sopenharmony_ci priv->wep_default_key_id = -1; 18708c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_BSSID) { 18748c2ecf20Sopenharmony_ci pr_debug("BSS_CHANGED_BSSID\n"); 18758c2ecf20Sopenharmony_ci do_join = true; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci if (changed & 18798c2ecf20Sopenharmony_ci (BSS_CHANGED_ASSOC | 18808c2ecf20Sopenharmony_ci BSS_CHANGED_BSSID | 18818c2ecf20Sopenharmony_ci BSS_CHANGED_IBSS | 18828c2ecf20Sopenharmony_ci BSS_CHANGED_BASIC_RATES | 18838c2ecf20Sopenharmony_ci BSS_CHANGED_HT)) { 18848c2ecf20Sopenharmony_ci pr_debug("BSS_CHANGED_ASSOC\n"); 18858c2ecf20Sopenharmony_ci if (info->assoc) { 18868c2ecf20Sopenharmony_ci if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) { 18878c2ecf20Sopenharmony_ci ieee80211_connection_loss(vif); 18888c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 18898c2ecf20Sopenharmony_ci return; 18908c2ecf20Sopenharmony_ci } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) { 18918c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_STA; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci } else { 18948c2ecf20Sopenharmony_ci do_join = true; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci if (info->assoc || info->ibss_joined) { 18988c2ecf20Sopenharmony_ci struct ieee80211_sta *sta = NULL; 18998c2ecf20Sopenharmony_ci __le32 htprot = 0; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci if (info->dtim_period) 19028c2ecf20Sopenharmony_ci priv->join_dtim_period = info->dtim_period; 19038c2ecf20Sopenharmony_ci priv->beacon_int = info->beacon_int; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci rcu_read_lock(); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci if (info->bssid && !info->ibss_joined) 19088c2ecf20Sopenharmony_ci sta = ieee80211_find_sta(vif, info->bssid); 19098c2ecf20Sopenharmony_ci if (sta) { 19108c2ecf20Sopenharmony_ci priv->ht_info.ht_cap = sta->ht_cap; 19118c2ecf20Sopenharmony_ci priv->bss_params.operational_rate_set = 19128c2ecf20Sopenharmony_ci cw1200_rate_mask_to_wsm(priv, 19138c2ecf20Sopenharmony_ci sta->supp_rates[priv->channel->band]); 19148c2ecf20Sopenharmony_ci priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef); 19158c2ecf20Sopenharmony_ci priv->ht_info.operation_mode = info->ht_operation_mode; 19168c2ecf20Sopenharmony_ci } else { 19178c2ecf20Sopenharmony_ci memset(&priv->ht_info, 0, 19188c2ecf20Sopenharmony_ci sizeof(priv->ht_info)); 19198c2ecf20Sopenharmony_ci priv->bss_params.operational_rate_set = -1; 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci rcu_read_unlock(); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci /* Non Greenfield stations present */ 19248c2ecf20Sopenharmony_ci if (priv->ht_info.operation_mode & 19258c2ecf20Sopenharmony_ci IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) 19268c2ecf20Sopenharmony_ci htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci /* Set HT protection method */ 19298c2ecf20Sopenharmony_ci htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci /* TODO: 19328c2ecf20Sopenharmony_ci * STBC_param.dual_cts 19338c2ecf20Sopenharmony_ci * STBC_param.LSIG_TXOP_FILL 19348c2ecf20Sopenharmony_ci */ 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION, 19378c2ecf20Sopenharmony_ci &htprot, sizeof(htprot)); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci priv->association_mode.greenfield = 19408c2ecf20Sopenharmony_ci cw1200_ht_greenfield(&priv->ht_info); 19418c2ecf20Sopenharmony_ci priv->association_mode.flags = 19428c2ecf20Sopenharmony_ci WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | 19438c2ecf20Sopenharmony_ci WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | 19448c2ecf20Sopenharmony_ci WSM_ASSOCIATION_MODE_USE_HT_MODE | 19458c2ecf20Sopenharmony_ci WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | 19468c2ecf20Sopenharmony_ci WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; 19478c2ecf20Sopenharmony_ci priv->association_mode.preamble = 19488c2ecf20Sopenharmony_ci info->use_short_preamble ? 19498c2ecf20Sopenharmony_ci WSM_JOIN_PREAMBLE_SHORT : 19508c2ecf20Sopenharmony_ci WSM_JOIN_PREAMBLE_LONG; 19518c2ecf20Sopenharmony_ci priv->association_mode.basic_rate_set = __cpu_to_le32( 19528c2ecf20Sopenharmony_ci cw1200_rate_mask_to_wsm(priv, 19538c2ecf20Sopenharmony_ci info->basic_rates)); 19548c2ecf20Sopenharmony_ci priv->association_mode.mpdu_start_spacing = 19558c2ecf20Sopenharmony_ci cw1200_ht_ampdu_density(&priv->ht_info); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 19588c2ecf20Sopenharmony_ci cancel_work_sync(&priv->unjoin_work); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count; 19618c2ecf20Sopenharmony_ci priv->bss_params.aid = info->aid; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (priv->join_dtim_period < 1) 19648c2ecf20Sopenharmony_ci priv->join_dtim_period = 1; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci pr_debug("[STA] DTIM %d, interval: %d\n", 19678c2ecf20Sopenharmony_ci priv->join_dtim_period, priv->beacon_int); 19688c2ecf20Sopenharmony_ci pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", 19698c2ecf20Sopenharmony_ci priv->association_mode.preamble, 19708c2ecf20Sopenharmony_ci priv->association_mode.greenfield, 19718c2ecf20Sopenharmony_ci priv->bss_params.aid, 19728c2ecf20Sopenharmony_ci priv->bss_params.operational_rate_set, 19738c2ecf20Sopenharmony_ci priv->association_mode.basic_rate_set); 19748c2ecf20Sopenharmony_ci wsm_set_association_mode(priv, &priv->association_mode); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci if (!info->ibss_joined) { 19778c2ecf20Sopenharmony_ci wsm_keep_alive_period(priv, 30 /* sec */); 19788c2ecf20Sopenharmony_ci wsm_set_bss_params(priv, &priv->bss_params); 19798c2ecf20Sopenharmony_ci priv->setbssparams_done = true; 19808c2ecf20Sopenharmony_ci cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work); 19818c2ecf20Sopenharmony_ci cw1200_set_pm(priv, &priv->powersave_mode); 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci if (priv->vif->p2p) { 19848c2ecf20Sopenharmony_ci pr_debug("[STA] Setting p2p powersave configuration.\n"); 19858c2ecf20Sopenharmony_ci wsm_set_p2p_ps_modeinfo(priv, 19868c2ecf20Sopenharmony_ci &priv->p2p_ps_modeinfo); 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci if (priv->bt_present) 19898c2ecf20Sopenharmony_ci cw1200_set_btcoexinfo(priv); 19908c2ecf20Sopenharmony_ci } else { 19918c2ecf20Sopenharmony_ci memset(&priv->association_mode, 0, 19928c2ecf20Sopenharmony_ci sizeof(priv->association_mode)); 19938c2ecf20Sopenharmony_ci memset(&priv->bss_params, 0, sizeof(priv->bss_params)); 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci } 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* ERP Protection */ 19988c2ecf20Sopenharmony_ci if (changed & (BSS_CHANGED_ASSOC | 19998c2ecf20Sopenharmony_ci BSS_CHANGED_ERP_CTS_PROT | 20008c2ecf20Sopenharmony_ci BSS_CHANGED_ERP_PREAMBLE)) { 20018c2ecf20Sopenharmony_ci u32 prev_erp_info = priv->erp_info; 20028c2ecf20Sopenharmony_ci if (info->use_cts_prot) 20038c2ecf20Sopenharmony_ci priv->erp_info |= WLAN_ERP_USE_PROTECTION; 20048c2ecf20Sopenharmony_ci else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) 20058c2ecf20Sopenharmony_ci priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (info->use_short_preamble) 20088c2ecf20Sopenharmony_ci priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; 20098c2ecf20Sopenharmony_ci else 20108c2ecf20Sopenharmony_ci priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci pr_debug("[STA] ERP Protection: %x\n", priv->erp_info); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci if (prev_erp_info != priv->erp_info) 20158c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->set_cts_work); 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci /* ERP Slottime */ 20198c2ecf20Sopenharmony_ci if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { 20208c2ecf20Sopenharmony_ci __le32 slot_time = info->use_short_slot ? 20218c2ecf20Sopenharmony_ci __cpu_to_le32(9) : __cpu_to_le32(20); 20228c2ecf20Sopenharmony_ci pr_debug("[STA] Slot time: %d us.\n", 20238c2ecf20Sopenharmony_ci __le32_to_cpu(slot_time)); 20248c2ecf20Sopenharmony_ci wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, 20258c2ecf20Sopenharmony_ci &slot_time, sizeof(slot_time)); 20268c2ecf20Sopenharmony_ci } 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { 20298c2ecf20Sopenharmony_ci struct wsm_rcpi_rssi_threshold threshold = { 20308c2ecf20Sopenharmony_ci .rollingAverageCount = 8, 20318c2ecf20Sopenharmony_ci }; 20328c2ecf20Sopenharmony_ci pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n", 20338c2ecf20Sopenharmony_ci info->cqm_rssi_thold, info->cqm_rssi_hyst); 20348c2ecf20Sopenharmony_ci priv->cqm_rssi_thold = info->cqm_rssi_thold; 20358c2ecf20Sopenharmony_ci priv->cqm_rssi_hyst = info->cqm_rssi_hyst; 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { 20388c2ecf20Sopenharmony_ci /* RSSI subscription enabled */ 20398c2ecf20Sopenharmony_ci /* TODO: It's not a correct way of setting threshold. 20408c2ecf20Sopenharmony_ci * Upper and lower must be set equal here and adjusted 20418c2ecf20Sopenharmony_ci * in callback. However current implementation is much 20428c2ecf20Sopenharmony_ci * more relaible and stable. 20438c2ecf20Sopenharmony_ci */ 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 20468c2ecf20Sopenharmony_ci * RSSI = RCPI / 2 - 110 20478c2ecf20Sopenharmony_ci */ 20488c2ecf20Sopenharmony_ci if (priv->cqm_use_rssi) { 20498c2ecf20Sopenharmony_ci threshold.upperThreshold = 20508c2ecf20Sopenharmony_ci info->cqm_rssi_thold + info->cqm_rssi_hyst; 20518c2ecf20Sopenharmony_ci threshold.lowerThreshold = 20528c2ecf20Sopenharmony_ci info->cqm_rssi_thold; 20538c2ecf20Sopenharmony_ci threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; 20548c2ecf20Sopenharmony_ci } else { 20558c2ecf20Sopenharmony_ci threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2; 20568c2ecf20Sopenharmony_ci threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2; 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; 20598c2ecf20Sopenharmony_ci } else { 20608c2ecf20Sopenharmony_ci /* There is a bug in FW, see sta.c. We have to enable 20618c2ecf20Sopenharmony_ci * dummy subscription to get correct RSSI values. 20628c2ecf20Sopenharmony_ci */ 20638c2ecf20Sopenharmony_ci threshold.rssiRcpiMode |= 20648c2ecf20Sopenharmony_ci WSM_RCPI_RSSI_THRESHOLD_ENABLE | 20658c2ecf20Sopenharmony_ci WSM_RCPI_RSSI_DONT_USE_UPPER | 20668c2ecf20Sopenharmony_ci WSM_RCPI_RSSI_DONT_USE_LOWER; 20678c2ecf20Sopenharmony_ci if (priv->cqm_use_rssi) 20688c2ecf20Sopenharmony_ci threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci wsm_set_rcpi_rssi_threshold(priv, &threshold); 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci if (do_join) { 20758c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 20768c2ecf20Sopenharmony_ci cw1200_do_join(priv); /* Will unlock it for us */ 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci} 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_civoid cw1200_multicast_start_work(struct work_struct *work) 20818c2ecf20Sopenharmony_ci{ 20828c2ecf20Sopenharmony_ci struct cw1200_common *priv = 20838c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, multicast_start_work); 20848c2ecf20Sopenharmony_ci long tmo = priv->join_dtim_period * 20858c2ecf20Sopenharmony_ci (priv->beacon_int + 20) * HZ / 1024; 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci cancel_work_sync(&priv->multicast_stop_work); 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci if (!priv->aid0_bit_set) { 20908c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 20918c2ecf20Sopenharmony_ci cw1200_set_tim_impl(priv, true); 20928c2ecf20Sopenharmony_ci priv->aid0_bit_set = true; 20938c2ecf20Sopenharmony_ci mod_timer(&priv->mcast_timeout, jiffies + tmo); 20948c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci} 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_civoid cw1200_multicast_stop_work(struct work_struct *work) 20998c2ecf20Sopenharmony_ci{ 21008c2ecf20Sopenharmony_ci struct cw1200_common *priv = 21018c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, multicast_stop_work); 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci if (priv->aid0_bit_set) { 21048c2ecf20Sopenharmony_ci del_timer_sync(&priv->mcast_timeout); 21058c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 21068c2ecf20Sopenharmony_ci priv->aid0_bit_set = false; 21078c2ecf20Sopenharmony_ci cw1200_set_tim_impl(priv, false); 21088c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci} 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_civoid cw1200_mcast_timeout(struct timer_list *t) 21138c2ecf20Sopenharmony_ci{ 21148c2ecf20Sopenharmony_ci struct cw1200_common *priv = from_timer(priv, t, mcast_timeout); 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci wiphy_warn(priv->hw->wiphy, 21178c2ecf20Sopenharmony_ci "Multicast delivery timeout.\n"); 21188c2ecf20Sopenharmony_ci spin_lock_bh(&priv->ps_state_lock); 21198c2ecf20Sopenharmony_ci priv->tx_multicast = priv->aid0_bit_set && 21208c2ecf20Sopenharmony_ci priv->buffered_multicasts; 21218c2ecf20Sopenharmony_ci if (priv->tx_multicast) 21228c2ecf20Sopenharmony_ci cw1200_bh_wakeup(priv); 21238c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->ps_state_lock); 21248c2ecf20Sopenharmony_ci} 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ciint cw1200_ampdu_action(struct ieee80211_hw *hw, 21278c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 21288c2ecf20Sopenharmony_ci struct ieee80211_ampdu_params *params) 21298c2ecf20Sopenharmony_ci{ 21308c2ecf20Sopenharmony_ci /* Aggregation is implemented fully in firmware, 21318c2ecf20Sopenharmony_ci * including block ack negotiation. Do not allow 21328c2ecf20Sopenharmony_ci * mac80211 stack to do anything: it interferes with 21338c2ecf20Sopenharmony_ci * the firmware. 21348c2ecf20Sopenharmony_ci */ 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci /* Note that we still need this function stubbed. */ 21378c2ecf20Sopenharmony_ci return -ENOTSUPP; 21388c2ecf20Sopenharmony_ci} 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci/* ******************************************************************** */ 21418c2ecf20Sopenharmony_ci/* WSM callback */ 21428c2ecf20Sopenharmony_civoid cw1200_suspend_resume(struct cw1200_common *priv, 21438c2ecf20Sopenharmony_ci struct wsm_suspend_resume *arg) 21448c2ecf20Sopenharmony_ci{ 21458c2ecf20Sopenharmony_ci pr_debug("[AP] %s: %s\n", 21468c2ecf20Sopenharmony_ci arg->stop ? "stop" : "start", 21478c2ecf20Sopenharmony_ci arg->multicast ? "broadcast" : "unicast"); 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci if (arg->multicast) { 21508c2ecf20Sopenharmony_ci bool cancel_tmo = false; 21518c2ecf20Sopenharmony_ci spin_lock_bh(&priv->ps_state_lock); 21528c2ecf20Sopenharmony_ci if (arg->stop) { 21538c2ecf20Sopenharmony_ci priv->tx_multicast = false; 21548c2ecf20Sopenharmony_ci } else { 21558c2ecf20Sopenharmony_ci /* Firmware sends this indication every DTIM if there 21568c2ecf20Sopenharmony_ci * is a STA in powersave connected. There is no reason 21578c2ecf20Sopenharmony_ci * to suspend, following wakeup will consume much more 21588c2ecf20Sopenharmony_ci * power than it could be saved. 21598c2ecf20Sopenharmony_ci */ 21608c2ecf20Sopenharmony_ci cw1200_pm_stay_awake(&priv->pm_state, 21618c2ecf20Sopenharmony_ci priv->join_dtim_period * 21628c2ecf20Sopenharmony_ci (priv->beacon_int + 20) * HZ / 1024); 21638c2ecf20Sopenharmony_ci priv->tx_multicast = (priv->aid0_bit_set && 21648c2ecf20Sopenharmony_ci priv->buffered_multicasts); 21658c2ecf20Sopenharmony_ci if (priv->tx_multicast) { 21668c2ecf20Sopenharmony_ci cancel_tmo = true; 21678c2ecf20Sopenharmony_ci cw1200_bh_wakeup(priv); 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->ps_state_lock); 21718c2ecf20Sopenharmony_ci if (cancel_tmo) 21728c2ecf20Sopenharmony_ci del_timer_sync(&priv->mcast_timeout); 21738c2ecf20Sopenharmony_ci } else { 21748c2ecf20Sopenharmony_ci spin_lock_bh(&priv->ps_state_lock); 21758c2ecf20Sopenharmony_ci cw1200_ps_notify(priv, arg->link_id, arg->stop); 21768c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->ps_state_lock); 21778c2ecf20Sopenharmony_ci if (!arg->stop) 21788c2ecf20Sopenharmony_ci cw1200_bh_wakeup(priv); 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci return; 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci/* ******************************************************************** */ 21848c2ecf20Sopenharmony_ci/* AP privates */ 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_cistatic int cw1200_upload_beacon(struct cw1200_common *priv) 21878c2ecf20Sopenharmony_ci{ 21888c2ecf20Sopenharmony_ci int ret = 0; 21898c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt; 21908c2ecf20Sopenharmony_ci struct wsm_template_frame frame = { 21918c2ecf20Sopenharmony_ci .frame_type = WSM_FRAME_TYPE_BEACON, 21928c2ecf20Sopenharmony_ci }; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci u16 tim_offset; 21958c2ecf20Sopenharmony_ci u16 tim_len; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_STATION || 21988c2ecf20Sopenharmony_ci priv->mode == NL80211_IFTYPE_MONITOR || 21998c2ecf20Sopenharmony_ci priv->mode == NL80211_IFTYPE_UNSPECIFIED) 22008c2ecf20Sopenharmony_ci goto done; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci if (priv->vif->p2p) 22038c2ecf20Sopenharmony_ci frame.rate = WSM_TRANSMIT_RATE_6; 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, 22068c2ecf20Sopenharmony_ci &tim_offset, &tim_len); 22078c2ecf20Sopenharmony_ci if (!frame.skb) 22088c2ecf20Sopenharmony_ci return -ENOMEM; 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci ret = wsm_set_template_frame(priv, &frame); 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci if (ret) 22138c2ecf20Sopenharmony_ci goto done; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci /* TODO: Distill probe resp; remove TIM 22168c2ecf20Sopenharmony_ci * and any other beacon-specific IEs 22178c2ecf20Sopenharmony_ci */ 22188c2ecf20Sopenharmony_ci mgmt = (void *)frame.skb->data; 22198c2ecf20Sopenharmony_ci mgmt->frame_control = 22208c2ecf20Sopenharmony_ci __cpu_to_le16(IEEE80211_FTYPE_MGMT | 22218c2ecf20Sopenharmony_ci IEEE80211_STYPE_PROBE_RESP); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; 22248c2ecf20Sopenharmony_ci if (priv->vif->p2p) { 22258c2ecf20Sopenharmony_ci ret = wsm_set_probe_responder(priv, true); 22268c2ecf20Sopenharmony_ci } else { 22278c2ecf20Sopenharmony_ci ret = wsm_set_template_frame(priv, &frame); 22288c2ecf20Sopenharmony_ci wsm_set_probe_responder(priv, false); 22298c2ecf20Sopenharmony_ci } 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_cidone: 22328c2ecf20Sopenharmony_ci dev_kfree_skb(frame.skb); 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci return ret; 22358c2ecf20Sopenharmony_ci} 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_cistatic int cw1200_upload_pspoll(struct cw1200_common *priv) 22388c2ecf20Sopenharmony_ci{ 22398c2ecf20Sopenharmony_ci int ret = 0; 22408c2ecf20Sopenharmony_ci struct wsm_template_frame frame = { 22418c2ecf20Sopenharmony_ci .frame_type = WSM_FRAME_TYPE_PS_POLL, 22428c2ecf20Sopenharmony_ci .rate = 0xFF, 22438c2ecf20Sopenharmony_ci }; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); 22478c2ecf20Sopenharmony_ci if (!frame.skb) 22488c2ecf20Sopenharmony_ci return -ENOMEM; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci ret = wsm_set_template_frame(priv, &frame); 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci dev_kfree_skb(frame.skb); 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci return ret; 22558c2ecf20Sopenharmony_ci} 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_cistatic int cw1200_upload_null(struct cw1200_common *priv) 22588c2ecf20Sopenharmony_ci{ 22598c2ecf20Sopenharmony_ci int ret = 0; 22608c2ecf20Sopenharmony_ci struct wsm_template_frame frame = { 22618c2ecf20Sopenharmony_ci .frame_type = WSM_FRAME_TYPE_NULL, 22628c2ecf20Sopenharmony_ci .rate = 0xFF, 22638c2ecf20Sopenharmony_ci }; 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); 22668c2ecf20Sopenharmony_ci if (!frame.skb) 22678c2ecf20Sopenharmony_ci return -ENOMEM; 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci ret = wsm_set_template_frame(priv, &frame); 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci dev_kfree_skb(frame.skb); 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci return ret; 22748c2ecf20Sopenharmony_ci} 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_cistatic int cw1200_upload_qosnull(struct cw1200_common *priv) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci /* TODO: This needs to be implemented 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci struct wsm_template_frame frame = { 22818c2ecf20Sopenharmony_ci .frame_type = WSM_FRAME_TYPE_QOS_NULL, 22828c2ecf20Sopenharmony_ci .rate = 0xFF, 22838c2ecf20Sopenharmony_ci }; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif); 22868c2ecf20Sopenharmony_ci if (!frame.skb) 22878c2ecf20Sopenharmony_ci return -ENOMEM; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci ret = wsm_set_template_frame(priv, &frame); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci dev_kfree_skb(frame.skb); 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci */ 22948c2ecf20Sopenharmony_ci return 0; 22958c2ecf20Sopenharmony_ci} 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_cistatic int cw1200_enable_beaconing(struct cw1200_common *priv, 22988c2ecf20Sopenharmony_ci bool enable) 22998c2ecf20Sopenharmony_ci{ 23008c2ecf20Sopenharmony_ci struct wsm_beacon_transmit transmit = { 23018c2ecf20Sopenharmony_ci .enable_beaconing = enable, 23028c2ecf20Sopenharmony_ci }; 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci return wsm_beacon_transmit(priv, &transmit); 23058c2ecf20Sopenharmony_ci} 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_cistatic int cw1200_start_ap(struct cw1200_common *priv) 23088c2ecf20Sopenharmony_ci{ 23098c2ecf20Sopenharmony_ci int ret; 23108c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; 23118c2ecf20Sopenharmony_ci struct wsm_start start = { 23128c2ecf20Sopenharmony_ci .mode = priv->vif->p2p ? 23138c2ecf20Sopenharmony_ci WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, 23148c2ecf20Sopenharmony_ci .band = (priv->channel->band == NL80211_BAND_5GHZ) ? 23158c2ecf20Sopenharmony_ci WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, 23168c2ecf20Sopenharmony_ci .channel_number = priv->channel->hw_value, 23178c2ecf20Sopenharmony_ci .beacon_interval = conf->beacon_int, 23188c2ecf20Sopenharmony_ci .dtim_period = conf->dtim_period, 23198c2ecf20Sopenharmony_ci .preamble = conf->use_short_preamble ? 23208c2ecf20Sopenharmony_ci WSM_JOIN_PREAMBLE_SHORT : 23218c2ecf20Sopenharmony_ci WSM_JOIN_PREAMBLE_LONG, 23228c2ecf20Sopenharmony_ci .probe_delay = 100, 23238c2ecf20Sopenharmony_ci .basic_rate_set = cw1200_rate_mask_to_wsm(priv, 23248c2ecf20Sopenharmony_ci conf->basic_rates), 23258c2ecf20Sopenharmony_ci }; 23268c2ecf20Sopenharmony_ci struct wsm_operational_mode mode = { 23278c2ecf20Sopenharmony_ci .power_mode = cw1200_power_mode, 23288c2ecf20Sopenharmony_ci .disable_more_flag_usage = true, 23298c2ecf20Sopenharmony_ci }; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci memset(start.ssid, 0, sizeof(start.ssid)); 23328c2ecf20Sopenharmony_ci if (!conf->hidden_ssid) { 23338c2ecf20Sopenharmony_ci start.ssid_len = conf->ssid_len; 23348c2ecf20Sopenharmony_ci memcpy(start.ssid, conf->ssid, start.ssid_len); 23358c2ecf20Sopenharmony_ci } 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci priv->beacon_int = conf->beacon_int; 23388c2ecf20Sopenharmony_ci priv->join_dtim_period = conf->dtim_period; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", 23438c2ecf20Sopenharmony_ci start.channel_number, start.band, 23448c2ecf20Sopenharmony_ci start.beacon_interval, start.dtim_period, 23458c2ecf20Sopenharmony_ci start.basic_rate_set, 23468c2ecf20Sopenharmony_ci start.ssid_len, start.ssid); 23478c2ecf20Sopenharmony_ci ret = wsm_start(priv, &start); 23488c2ecf20Sopenharmony_ci if (!ret) 23498c2ecf20Sopenharmony_ci ret = cw1200_upload_keys(priv); 23508c2ecf20Sopenharmony_ci if (!ret && priv->vif->p2p) { 23518c2ecf20Sopenharmony_ci pr_debug("[AP] Setting p2p powersave configuration.\n"); 23528c2ecf20Sopenharmony_ci wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo); 23538c2ecf20Sopenharmony_ci } 23548c2ecf20Sopenharmony_ci if (!ret) { 23558c2ecf20Sopenharmony_ci wsm_set_block_ack_policy(priv, 0, 0); 23568c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_AP; 23578c2ecf20Sopenharmony_ci cw1200_update_filtering(priv); 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci wsm_set_operational_mode(priv, &mode); 23608c2ecf20Sopenharmony_ci return ret; 23618c2ecf20Sopenharmony_ci} 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_cistatic int cw1200_update_beaconing(struct cw1200_common *priv) 23648c2ecf20Sopenharmony_ci{ 23658c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; 23668c2ecf20Sopenharmony_ci struct wsm_reset reset = { 23678c2ecf20Sopenharmony_ci .link_id = 0, 23688c2ecf20Sopenharmony_ci .reset_statistics = true, 23698c2ecf20Sopenharmony_ci }; 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_AP) { 23728c2ecf20Sopenharmony_ci /* TODO: check if changed channel, band */ 23738c2ecf20Sopenharmony_ci if (priv->join_status != CW1200_JOIN_STATUS_AP || 23748c2ecf20Sopenharmony_ci priv->beacon_int != conf->beacon_int) { 23758c2ecf20Sopenharmony_ci pr_debug("ap restarting\n"); 23768c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 23778c2ecf20Sopenharmony_ci if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) 23788c2ecf20Sopenharmony_ci wsm_reset(priv, &reset); 23798c2ecf20Sopenharmony_ci priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 23808c2ecf20Sopenharmony_ci cw1200_start_ap(priv); 23818c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 23828c2ecf20Sopenharmony_ci } else 23838c2ecf20Sopenharmony_ci pr_debug("ap started join_status: %d\n", 23848c2ecf20Sopenharmony_ci priv->join_status); 23858c2ecf20Sopenharmony_ci } 23868c2ecf20Sopenharmony_ci return 0; 23878c2ecf20Sopenharmony_ci} 2388