1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2a910e4a9SSolomon Peachy /* 3a910e4a9SSolomon Peachy * Mac80211 STA API for ST-Ericsson CW1200 drivers 4a910e4a9SSolomon Peachy * 5a910e4a9SSolomon Peachy * Copyright (c) 2010, ST-Ericsson 6a910e4a9SSolomon Peachy * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 7a910e4a9SSolomon Peachy */ 8a910e4a9SSolomon Peachy 9a910e4a9SSolomon Peachy #include <linux/vmalloc.h> 10a910e4a9SSolomon Peachy #include <linux/sched.h> 11a910e4a9SSolomon Peachy #include <linux/firmware.h> 12a910e4a9SSolomon Peachy #include <linux/module.h> 1335df5388Sdingtianhong #include <linux/etherdevice.h> 14a910e4a9SSolomon Peachy 15a910e4a9SSolomon Peachy #include "cw1200.h" 16a910e4a9SSolomon Peachy #include "sta.h" 17a910e4a9SSolomon Peachy #include "fwio.h" 18a910e4a9SSolomon Peachy #include "bh.h" 19a910e4a9SSolomon Peachy #include "debug.h" 20a910e4a9SSolomon Peachy 21a910e4a9SSolomon Peachy #ifndef ERP_INFO_BYTE_OFFSET 22a910e4a9SSolomon Peachy #define ERP_INFO_BYTE_OFFSET 2 23a910e4a9SSolomon Peachy #endif 24a910e4a9SSolomon Peachy 25a910e4a9SSolomon Peachy static void cw1200_do_join(struct cw1200_common *priv); 26a910e4a9SSolomon Peachy static void cw1200_do_unjoin(struct cw1200_common *priv); 27a910e4a9SSolomon Peachy 28a910e4a9SSolomon Peachy static int cw1200_upload_beacon(struct cw1200_common *priv); 29a910e4a9SSolomon Peachy static int cw1200_upload_pspoll(struct cw1200_common *priv); 30a910e4a9SSolomon Peachy static int cw1200_upload_null(struct cw1200_common *priv); 31a910e4a9SSolomon Peachy static int cw1200_upload_qosnull(struct cw1200_common *priv); 32a910e4a9SSolomon Peachy static int cw1200_start_ap(struct cw1200_common *priv); 33a910e4a9SSolomon Peachy static int cw1200_update_beaconing(struct cw1200_common *priv); 34a910e4a9SSolomon Peachy static int cw1200_enable_beaconing(struct cw1200_common *priv, 35a910e4a9SSolomon Peachy bool enable); 36a910e4a9SSolomon Peachy static void __cw1200_sta_notify(struct ieee80211_hw *dev, 37a910e4a9SSolomon Peachy struct ieee80211_vif *vif, 38a910e4a9SSolomon Peachy enum sta_notify_cmd notify_cmd, 39a910e4a9SSolomon Peachy int link_id); 40a910e4a9SSolomon Peachy static int __cw1200_flush(struct cw1200_common *priv, bool drop); 41a910e4a9SSolomon Peachy 42a910e4a9SSolomon Peachy static inline void __cw1200_free_event_queue(struct list_head *list) 43a910e4a9SSolomon Peachy { 44a910e4a9SSolomon Peachy struct cw1200_wsm_event *event, *tmp; 45a910e4a9SSolomon Peachy list_for_each_entry_safe(event, tmp, list, link) { 46a910e4a9SSolomon Peachy list_del(&event->link); 47a910e4a9SSolomon Peachy kfree(event); 48a910e4a9SSolomon Peachy } 49a910e4a9SSolomon Peachy } 50a910e4a9SSolomon Peachy 51a910e4a9SSolomon Peachy /* ******************************************************************** */ 52a910e4a9SSolomon Peachy /* STA API */ 53a910e4a9SSolomon Peachy 54a910e4a9SSolomon Peachy int cw1200_start(struct ieee80211_hw *dev) 55a910e4a9SSolomon Peachy { 56a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 57a910e4a9SSolomon Peachy int ret = 0; 58a910e4a9SSolomon Peachy 59a910e4a9SSolomon Peachy cw1200_pm_stay_awake(&priv->pm_state, HZ); 60a910e4a9SSolomon Peachy 61a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 62a910e4a9SSolomon Peachy 63a910e4a9SSolomon Peachy /* default EDCA */ 64a910e4a9SSolomon Peachy WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); 65a910e4a9SSolomon Peachy WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); 66a910e4a9SSolomon Peachy WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); 67a910e4a9SSolomon Peachy WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); 68a910e4a9SSolomon Peachy ret = wsm_set_edca_params(priv, &priv->edca); 69a910e4a9SSolomon Peachy if (ret) 70a910e4a9SSolomon Peachy goto out; 71a910e4a9SSolomon Peachy 72a910e4a9SSolomon Peachy ret = cw1200_set_uapsd_param(priv, &priv->edca); 73a910e4a9SSolomon Peachy if (ret) 74a910e4a9SSolomon Peachy goto out; 75a910e4a9SSolomon Peachy 76a910e4a9SSolomon Peachy priv->setbssparams_done = false; 77a910e4a9SSolomon Peachy 78a910e4a9SSolomon Peachy memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); 79a910e4a9SSolomon Peachy priv->mode = NL80211_IFTYPE_MONITOR; 80a910e4a9SSolomon Peachy priv->wep_default_key_id = -1; 81a910e4a9SSolomon Peachy 82a910e4a9SSolomon Peachy priv->cqm_beacon_loss_count = 10; 83a910e4a9SSolomon Peachy 84a910e4a9SSolomon Peachy ret = cw1200_setup_mac(priv); 85a910e4a9SSolomon Peachy if (ret) 86a910e4a9SSolomon Peachy goto out; 87a910e4a9SSolomon Peachy 88a910e4a9SSolomon Peachy out: 89a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 90a910e4a9SSolomon Peachy return ret; 91a910e4a9SSolomon Peachy } 92a910e4a9SSolomon Peachy 93a910e4a9SSolomon Peachy void cw1200_stop(struct ieee80211_hw *dev) 94a910e4a9SSolomon Peachy { 95a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 96a910e4a9SSolomon Peachy LIST_HEAD(list); 97a910e4a9SSolomon Peachy int i; 98a910e4a9SSolomon Peachy 99a910e4a9SSolomon Peachy wsm_lock_tx(priv); 100a910e4a9SSolomon Peachy 101a910e4a9SSolomon Peachy while (down_trylock(&priv->scan.lock)) { 102a910e4a9SSolomon Peachy /* Scan is in progress. Force it to stop. */ 103a910e4a9SSolomon Peachy priv->scan.req = NULL; 104a910e4a9SSolomon Peachy schedule(); 105a910e4a9SSolomon Peachy } 106a910e4a9SSolomon Peachy up(&priv->scan.lock); 107a910e4a9SSolomon Peachy 108a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->scan.probe_work); 109a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->scan.timeout); 110a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->clear_recent_scan_work); 111a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->join_timeout); 112a910e4a9SSolomon Peachy cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 113a910e4a9SSolomon Peachy cancel_work_sync(&priv->unjoin_work); 114a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->link_id_gc_work); 115a910e4a9SSolomon Peachy flush_workqueue(priv->workqueue); 116a910e4a9SSolomon Peachy del_timer_sync(&priv->mcast_timeout); 117a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 118a910e4a9SSolomon Peachy priv->mode = NL80211_IFTYPE_UNSPECIFIED; 119a910e4a9SSolomon Peachy priv->listening = false; 120a910e4a9SSolomon Peachy 121a910e4a9SSolomon Peachy spin_lock(&priv->event_queue_lock); 122a910e4a9SSolomon Peachy list_splice_init(&priv->event_queue, &list); 123a910e4a9SSolomon Peachy spin_unlock(&priv->event_queue_lock); 124a910e4a9SSolomon Peachy __cw1200_free_event_queue(&list); 125a910e4a9SSolomon Peachy 126a910e4a9SSolomon Peachy 127a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 128a910e4a9SSolomon Peachy priv->join_pending = false; 129a910e4a9SSolomon Peachy 130a910e4a9SSolomon Peachy for (i = 0; i < 4; i++) 131a910e4a9SSolomon Peachy cw1200_queue_clear(&priv->tx_queue[i]); 132a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 133a910e4a9SSolomon Peachy tx_policy_clean(priv); 134a910e4a9SSolomon Peachy 135a910e4a9SSolomon Peachy /* HACK! */ 136a910e4a9SSolomon Peachy if (atomic_xchg(&priv->tx_lock, 1) != 1) 137a910e4a9SSolomon Peachy pr_debug("[STA] TX is force-unlocked due to stop request.\n"); 138a910e4a9SSolomon Peachy 139a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 140a910e4a9SSolomon Peachy atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */ 141a910e4a9SSolomon Peachy } 142a910e4a9SSolomon Peachy 143a910e4a9SSolomon Peachy static int cw1200_bssloss_mitigation = 1; 144a910e4a9SSolomon Peachy module_param(cw1200_bssloss_mitigation, int, 0644); 145a910e4a9SSolomon Peachy MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)"); 146a910e4a9SSolomon Peachy 147a910e4a9SSolomon Peachy 148a910e4a9SSolomon Peachy void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, 149a910e4a9SSolomon Peachy int init, int good, int bad) 150a910e4a9SSolomon Peachy { 151a910e4a9SSolomon Peachy int tx = 0; 152a910e4a9SSolomon Peachy 153a910e4a9SSolomon Peachy priv->delayed_link_loss = 0; 154a910e4a9SSolomon Peachy cancel_work_sync(&priv->bss_params_work); 155a910e4a9SSolomon Peachy 156a910e4a9SSolomon Peachy pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n", 157a910e4a9SSolomon Peachy priv->bss_loss_state, 158a910e4a9SSolomon Peachy init, good, bad, 159a910e4a9SSolomon Peachy atomic_read(&priv->tx_lock), 160a910e4a9SSolomon Peachy priv->delayed_unjoin); 161a910e4a9SSolomon Peachy 162a910e4a9SSolomon Peachy /* If we have a pending unjoin */ 163a910e4a9SSolomon Peachy if (priv->delayed_unjoin) 164a910e4a9SSolomon Peachy return; 165a910e4a9SSolomon Peachy 166a910e4a9SSolomon Peachy if (init) { 167a910e4a9SSolomon Peachy queue_delayed_work(priv->workqueue, 168a910e4a9SSolomon Peachy &priv->bss_loss_work, 169a910e4a9SSolomon Peachy HZ); 170a910e4a9SSolomon Peachy priv->bss_loss_state = 0; 171a910e4a9SSolomon Peachy 172a910e4a9SSolomon Peachy /* Skip the confimration procedure in P2P case */ 173a910e4a9SSolomon Peachy if (!priv->vif->p2p && !atomic_read(&priv->tx_lock)) 174a910e4a9SSolomon Peachy tx = 1; 175a910e4a9SSolomon Peachy } else if (good) { 176a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->bss_loss_work); 177a910e4a9SSolomon Peachy priv->bss_loss_state = 0; 178a910e4a9SSolomon Peachy queue_work(priv->workqueue, &priv->bss_params_work); 179a910e4a9SSolomon Peachy } else if (bad) { 180a910e4a9SSolomon Peachy /* XXX Should we just keep going until we time out? */ 181a910e4a9SSolomon Peachy if (priv->bss_loss_state < 3) 182a910e4a9SSolomon Peachy tx = 1; 183a910e4a9SSolomon Peachy } else { 184a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->bss_loss_work); 185a910e4a9SSolomon Peachy priv->bss_loss_state = 0; 186a910e4a9SSolomon Peachy } 187a910e4a9SSolomon Peachy 188a910e4a9SSolomon Peachy /* Bypass mitigation if it's disabled */ 189a910e4a9SSolomon Peachy if (!cw1200_bssloss_mitigation) 190a910e4a9SSolomon Peachy tx = 0; 191a910e4a9SSolomon Peachy 192a910e4a9SSolomon Peachy /* Spit out a NULL packet to our AP if necessary */ 193a910e4a9SSolomon Peachy if (tx) { 194a910e4a9SSolomon Peachy struct sk_buff *skb; 195a910e4a9SSolomon Peachy 196a910e4a9SSolomon Peachy priv->bss_loss_state++; 197a910e4a9SSolomon Peachy 1987b6ddeafSJohannes Berg skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); 199a910e4a9SSolomon Peachy WARN_ON(!skb); 200a910e4a9SSolomon Peachy if (skb) 201a910e4a9SSolomon Peachy cw1200_tx(priv->hw, NULL, skb); 202a910e4a9SSolomon Peachy } 203a910e4a9SSolomon Peachy } 204a910e4a9SSolomon Peachy 205a910e4a9SSolomon Peachy int cw1200_add_interface(struct ieee80211_hw *dev, 206a910e4a9SSolomon Peachy struct ieee80211_vif *vif) 207a910e4a9SSolomon Peachy { 208a910e4a9SSolomon Peachy int ret; 209a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 210a910e4a9SSolomon Peachy /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ 211a910e4a9SSolomon Peachy 212a910e4a9SSolomon Peachy vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | 213848955ccSJohannes Berg IEEE80211_VIF_SUPPORTS_UAPSD | 214a910e4a9SSolomon Peachy IEEE80211_VIF_SUPPORTS_CQM_RSSI; 215a910e4a9SSolomon Peachy 216a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 217a910e4a9SSolomon Peachy 218a910e4a9SSolomon Peachy if (priv->mode != NL80211_IFTYPE_MONITOR) { 219a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 220a910e4a9SSolomon Peachy return -EOPNOTSUPP; 221a910e4a9SSolomon Peachy } 222a910e4a9SSolomon Peachy 223a910e4a9SSolomon Peachy switch (vif->type) { 224a910e4a9SSolomon Peachy case NL80211_IFTYPE_STATION: 225a910e4a9SSolomon Peachy case NL80211_IFTYPE_ADHOC: 226a910e4a9SSolomon Peachy case NL80211_IFTYPE_MESH_POINT: 227a910e4a9SSolomon Peachy case NL80211_IFTYPE_AP: 228a910e4a9SSolomon Peachy priv->mode = vif->type; 229a910e4a9SSolomon Peachy break; 230a910e4a9SSolomon Peachy default: 231a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 232a910e4a9SSolomon Peachy return -EOPNOTSUPP; 233a910e4a9SSolomon Peachy } 234a910e4a9SSolomon Peachy 235a910e4a9SSolomon Peachy priv->vif = vif; 236a910e4a9SSolomon Peachy memcpy(priv->mac_addr, vif->addr, ETH_ALEN); 237a910e4a9SSolomon Peachy ret = cw1200_setup_mac(priv); 238a910e4a9SSolomon Peachy /* Enable auto-calibration */ 239a910e4a9SSolomon Peachy /* Exception in subsequent channel switch; disabled. 240a910e4a9SSolomon Peachy * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, 241a910e4a9SSolomon Peachy * &auto_calibration_mode, sizeof(auto_calibration_mode)); 242a910e4a9SSolomon Peachy */ 243a910e4a9SSolomon Peachy 244a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 245a910e4a9SSolomon Peachy return ret; 246a910e4a9SSolomon Peachy } 247a910e4a9SSolomon Peachy 248a910e4a9SSolomon Peachy void cw1200_remove_interface(struct ieee80211_hw *dev, 249a910e4a9SSolomon Peachy struct ieee80211_vif *vif) 250a910e4a9SSolomon Peachy { 251a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 252a910e4a9SSolomon Peachy struct wsm_reset reset = { 253a910e4a9SSolomon Peachy .reset_statistics = true, 254a910e4a9SSolomon Peachy }; 255a910e4a9SSolomon Peachy int i; 256a910e4a9SSolomon Peachy 257a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 258a910e4a9SSolomon Peachy switch (priv->join_status) { 259a910e4a9SSolomon Peachy case CW1200_JOIN_STATUS_JOINING: 260a910e4a9SSolomon Peachy case CW1200_JOIN_STATUS_PRE_STA: 261a910e4a9SSolomon Peachy case CW1200_JOIN_STATUS_STA: 262a910e4a9SSolomon Peachy case CW1200_JOIN_STATUS_IBSS: 263a910e4a9SSolomon Peachy wsm_lock_tx(priv); 264a910e4a9SSolomon Peachy if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 265a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 266a910e4a9SSolomon Peachy break; 267a910e4a9SSolomon Peachy case CW1200_JOIN_STATUS_AP: 268a910e4a9SSolomon Peachy for (i = 0; priv->link_id_map; ++i) { 269a910e4a9SSolomon Peachy if (priv->link_id_map & BIT(i)) { 270a910e4a9SSolomon Peachy reset.link_id = i; 271a910e4a9SSolomon Peachy wsm_reset(priv, &reset); 272a910e4a9SSolomon Peachy priv->link_id_map &= ~BIT(i); 273a910e4a9SSolomon Peachy } 274a910e4a9SSolomon Peachy } 275a910e4a9SSolomon Peachy memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); 276a910e4a9SSolomon Peachy priv->sta_asleep_mask = 0; 277a910e4a9SSolomon Peachy priv->enable_beacon = false; 278a910e4a9SSolomon Peachy priv->tx_multicast = false; 279a910e4a9SSolomon Peachy priv->aid0_bit_set = false; 280a910e4a9SSolomon Peachy priv->buffered_multicasts = false; 281a910e4a9SSolomon Peachy priv->pspoll_mask = 0; 282a910e4a9SSolomon Peachy reset.link_id = 0; 283a910e4a9SSolomon Peachy wsm_reset(priv, &reset); 284a910e4a9SSolomon Peachy break; 285a910e4a9SSolomon Peachy case CW1200_JOIN_STATUS_MONITOR: 286a910e4a9SSolomon Peachy cw1200_update_listening(priv, false); 287a910e4a9SSolomon Peachy break; 288a910e4a9SSolomon Peachy default: 289a910e4a9SSolomon Peachy break; 290a910e4a9SSolomon Peachy } 291a910e4a9SSolomon Peachy priv->vif = NULL; 292a910e4a9SSolomon Peachy priv->mode = NL80211_IFTYPE_MONITOR; 29393803b33SJoe Perches eth_zero_addr(priv->mac_addr); 294a910e4a9SSolomon Peachy memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo)); 295a910e4a9SSolomon Peachy cw1200_free_keys(priv); 296a910e4a9SSolomon Peachy cw1200_setup_mac(priv); 297a910e4a9SSolomon Peachy priv->listening = false; 298a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 299a910e4a9SSolomon Peachy if (!__cw1200_flush(priv, true)) 300a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 301a910e4a9SSolomon Peachy 302a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 303a910e4a9SSolomon Peachy } 304a910e4a9SSolomon Peachy 305a910e4a9SSolomon Peachy int cw1200_change_interface(struct ieee80211_hw *dev, 306a910e4a9SSolomon Peachy struct ieee80211_vif *vif, 307a910e4a9SSolomon Peachy enum nl80211_iftype new_type, 308a910e4a9SSolomon Peachy bool p2p) 309a910e4a9SSolomon Peachy { 310a910e4a9SSolomon Peachy int ret = 0; 311a910e4a9SSolomon Peachy pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type, 312a910e4a9SSolomon Peachy p2p, vif->type, vif->p2p); 313a910e4a9SSolomon Peachy 314a910e4a9SSolomon Peachy if (new_type != vif->type || vif->p2p != p2p) { 315a910e4a9SSolomon Peachy cw1200_remove_interface(dev, vif); 316a910e4a9SSolomon Peachy vif->type = new_type; 317a910e4a9SSolomon Peachy vif->p2p = p2p; 318a910e4a9SSolomon Peachy ret = cw1200_add_interface(dev, vif); 319a910e4a9SSolomon Peachy } 320a910e4a9SSolomon Peachy 321a910e4a9SSolomon Peachy return ret; 322a910e4a9SSolomon Peachy } 323a910e4a9SSolomon Peachy 324a910e4a9SSolomon Peachy int cw1200_config(struct ieee80211_hw *dev, u32 changed) 325a910e4a9SSolomon Peachy { 326a910e4a9SSolomon Peachy int ret = 0; 327a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 328a910e4a9SSolomon Peachy struct ieee80211_conf *conf = &dev->conf; 329a910e4a9SSolomon Peachy 330a910e4a9SSolomon Peachy pr_debug("CONFIG CHANGED: %08x\n", changed); 331a910e4a9SSolomon Peachy 332a910e4a9SSolomon Peachy down(&priv->scan.lock); 333a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 334a910e4a9SSolomon Peachy /* TODO: IEEE80211_CONF_CHANGE_QOS */ 335a910e4a9SSolomon Peachy /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */ 336a910e4a9SSolomon Peachy 337a910e4a9SSolomon Peachy if (changed & IEEE80211_CONF_CHANGE_POWER) { 338a910e4a9SSolomon Peachy priv->output_power = conf->power_level; 339a910e4a9SSolomon Peachy pr_debug("[STA] TX power: %d\n", priv->output_power); 340a910e4a9SSolomon Peachy wsm_set_output_power(priv, priv->output_power * 10); 341a910e4a9SSolomon Peachy } 342a910e4a9SSolomon Peachy 343a910e4a9SSolomon Peachy if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && 344a910e4a9SSolomon Peachy (priv->channel != conf->chandef.chan)) { 345a910e4a9SSolomon Peachy struct ieee80211_channel *ch = conf->chandef.chan; 346a910e4a9SSolomon Peachy struct wsm_switch_channel channel = { 347a910e4a9SSolomon Peachy .channel_number = ch->hw_value, 348a910e4a9SSolomon Peachy }; 349a910e4a9SSolomon Peachy pr_debug("[STA] Freq %d (wsm ch: %d).\n", 350a910e4a9SSolomon Peachy ch->center_freq, ch->hw_value); 351a910e4a9SSolomon Peachy 352a910e4a9SSolomon Peachy /* __cw1200_flush() implicitly locks tx, if successful */ 353a910e4a9SSolomon Peachy if (!__cw1200_flush(priv, false)) { 354a910e4a9SSolomon Peachy if (!wsm_switch_channel(priv, &channel)) { 355a910e4a9SSolomon Peachy ret = wait_event_timeout(priv->channel_switch_done, 356a910e4a9SSolomon Peachy !priv->channel_switch_in_progress, 357a910e4a9SSolomon Peachy 3 * HZ); 358a910e4a9SSolomon Peachy if (ret) { 359a910e4a9SSolomon Peachy /* Already unlocks if successful */ 360a910e4a9SSolomon Peachy priv->channel = ch; 361a910e4a9SSolomon Peachy ret = 0; 362a910e4a9SSolomon Peachy } else { 363a910e4a9SSolomon Peachy ret = -ETIMEDOUT; 364a910e4a9SSolomon Peachy } 365a910e4a9SSolomon Peachy } else { 366a910e4a9SSolomon Peachy /* Unlock if switch channel fails */ 367a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 368a910e4a9SSolomon Peachy } 369a910e4a9SSolomon Peachy } 370a910e4a9SSolomon Peachy } 371a910e4a9SSolomon Peachy 372a910e4a9SSolomon Peachy if (changed & IEEE80211_CONF_CHANGE_PS) { 373a910e4a9SSolomon Peachy if (!(conf->flags & IEEE80211_CONF_PS)) 374a910e4a9SSolomon Peachy priv->powersave_mode.mode = WSM_PSM_ACTIVE; 375a910e4a9SSolomon Peachy else if (conf->dynamic_ps_timeout <= 0) 376a910e4a9SSolomon Peachy priv->powersave_mode.mode = WSM_PSM_PS; 377a910e4a9SSolomon Peachy else 378a910e4a9SSolomon Peachy priv->powersave_mode.mode = WSM_PSM_FAST_PS; 379a910e4a9SSolomon Peachy 380a910e4a9SSolomon Peachy /* Firmware requires that value for this 1-byte field must 381a910e4a9SSolomon Peachy * be specified in units of 500us. Values above the 128ms 382a910e4a9SSolomon Peachy * threshold are not supported. 383a910e4a9SSolomon Peachy */ 384a910e4a9SSolomon Peachy if (conf->dynamic_ps_timeout >= 0x80) 385a910e4a9SSolomon Peachy priv->powersave_mode.fast_psm_idle_period = 0xFF; 386a910e4a9SSolomon Peachy else 387a910e4a9SSolomon Peachy priv->powersave_mode.fast_psm_idle_period = 388a910e4a9SSolomon Peachy conf->dynamic_ps_timeout << 1; 389a910e4a9SSolomon Peachy 390a910e4a9SSolomon Peachy if (priv->join_status == CW1200_JOIN_STATUS_STA && 391a910e4a9SSolomon Peachy priv->bss_params.aid) 392a910e4a9SSolomon Peachy cw1200_set_pm(priv, &priv->powersave_mode); 393a910e4a9SSolomon Peachy } 394a910e4a9SSolomon Peachy 395a910e4a9SSolomon Peachy if (changed & IEEE80211_CONF_CHANGE_MONITOR) { 396a910e4a9SSolomon Peachy /* TBD: It looks like it's transparent 397a910e4a9SSolomon Peachy * there's a monitor interface present -- use this 398a910e4a9SSolomon Peachy * to determine for example whether to calculate 399a910e4a9SSolomon Peachy * timestamps for packets or not, do not use instead 400a910e4a9SSolomon Peachy * of filter flags! 401a910e4a9SSolomon Peachy */ 402a910e4a9SSolomon Peachy } 403a910e4a9SSolomon Peachy 404a910e4a9SSolomon Peachy if (changed & IEEE80211_CONF_CHANGE_IDLE) { 405a910e4a9SSolomon Peachy struct wsm_operational_mode mode = { 406a910e4a9SSolomon Peachy .power_mode = cw1200_power_mode, 407a910e4a9SSolomon Peachy .disable_more_flag_usage = true, 408a910e4a9SSolomon Peachy }; 409a910e4a9SSolomon Peachy 410a910e4a9SSolomon Peachy wsm_lock_tx(priv); 411a910e4a9SSolomon Peachy /* Disable p2p-dev mode forced by TX request */ 412a910e4a9SSolomon Peachy if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && 413a910e4a9SSolomon Peachy (conf->flags & IEEE80211_CONF_IDLE) && 414a910e4a9SSolomon Peachy !priv->listening) { 415a910e4a9SSolomon Peachy cw1200_disable_listening(priv); 416a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 417a910e4a9SSolomon Peachy } 418a910e4a9SSolomon Peachy wsm_set_operational_mode(priv, &mode); 419a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 420a910e4a9SSolomon Peachy } 421a910e4a9SSolomon Peachy 422a910e4a9SSolomon Peachy if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { 423a910e4a9SSolomon Peachy pr_debug("[STA] Retry limits: %d (long), %d (short).\n", 424a910e4a9SSolomon Peachy conf->long_frame_max_tx_count, 425a910e4a9SSolomon Peachy conf->short_frame_max_tx_count); 426a910e4a9SSolomon Peachy spin_lock_bh(&priv->tx_policy_cache.lock); 427a910e4a9SSolomon Peachy priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; 428a910e4a9SSolomon Peachy priv->short_frame_max_tx_count = 429a910e4a9SSolomon Peachy (conf->short_frame_max_tx_count < 0x0F) ? 430a910e4a9SSolomon Peachy conf->short_frame_max_tx_count : 0x0F; 431a910e4a9SSolomon Peachy priv->hw->max_rate_tries = priv->short_frame_max_tx_count; 432a910e4a9SSolomon Peachy spin_unlock_bh(&priv->tx_policy_cache.lock); 433a910e4a9SSolomon Peachy } 434a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 435a910e4a9SSolomon Peachy up(&priv->scan.lock); 436a910e4a9SSolomon Peachy return ret; 437a910e4a9SSolomon Peachy } 438a910e4a9SSolomon Peachy 439a910e4a9SSolomon Peachy void cw1200_update_filtering(struct cw1200_common *priv) 440a910e4a9SSolomon Peachy { 441a910e4a9SSolomon Peachy int ret; 442a910e4a9SSolomon Peachy bool bssid_filtering = !priv->rx_filter.bssid; 443a910e4a9SSolomon Peachy bool is_p2p = priv->vif && priv->vif->p2p; 444a910e4a9SSolomon Peachy bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type; 445a910e4a9SSolomon Peachy 446a910e4a9SSolomon Peachy static struct wsm_beacon_filter_control bf_ctrl; 447a910e4a9SSolomon Peachy static struct wsm_mib_beacon_filter_table bf_tbl = { 448a910e4a9SSolomon Peachy .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC, 449a910e4a9SSolomon Peachy .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | 450a910e4a9SSolomon Peachy WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | 451a910e4a9SSolomon Peachy WSM_BEACON_FILTER_IE_HAS_APPEARED, 452a910e4a9SSolomon Peachy .entry[0].oui[0] = 0x50, 453a910e4a9SSolomon Peachy .entry[0].oui[1] = 0x6F, 454a910e4a9SSolomon Peachy .entry[0].oui[2] = 0x9A, 455a910e4a9SSolomon Peachy .entry[1].ie_id = WLAN_EID_HT_OPERATION, 456a910e4a9SSolomon Peachy .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | 457a910e4a9SSolomon Peachy WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | 458a910e4a9SSolomon Peachy WSM_BEACON_FILTER_IE_HAS_APPEARED, 459a910e4a9SSolomon Peachy .entry[2].ie_id = WLAN_EID_ERP_INFO, 460a910e4a9SSolomon Peachy .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | 461a910e4a9SSolomon Peachy WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | 462a910e4a9SSolomon Peachy WSM_BEACON_FILTER_IE_HAS_APPEARED, 463a910e4a9SSolomon Peachy }; 464a910e4a9SSolomon Peachy 465a910e4a9SSolomon Peachy if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) 466a910e4a9SSolomon Peachy return; 467a910e4a9SSolomon Peachy else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) 468a910e4a9SSolomon Peachy bssid_filtering = false; 469a910e4a9SSolomon Peachy 470a910e4a9SSolomon Peachy if (priv->disable_beacon_filter) { 471a910e4a9SSolomon Peachy bf_ctrl.enabled = 0; 472a910e4a9SSolomon Peachy bf_ctrl.bcn_count = 1; 473a910e4a9SSolomon Peachy bf_tbl.num = __cpu_to_le32(0); 474a910e4a9SSolomon Peachy } else if (is_p2p || !is_sta) { 475a910e4a9SSolomon Peachy bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE | 476a910e4a9SSolomon Peachy WSM_BEACON_FILTER_AUTO_ERP; 477a910e4a9SSolomon Peachy bf_ctrl.bcn_count = 0; 478a910e4a9SSolomon Peachy bf_tbl.num = __cpu_to_le32(2); 479a910e4a9SSolomon Peachy } else { 480a910e4a9SSolomon Peachy bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE; 481a910e4a9SSolomon Peachy bf_ctrl.bcn_count = 0; 482a910e4a9SSolomon Peachy bf_tbl.num = __cpu_to_le32(3); 483a910e4a9SSolomon Peachy } 484a910e4a9SSolomon Peachy 4858b3e7be4SSolomon Peachy /* When acting as p2p client being connected to p2p GO, in order to 486a910e4a9SSolomon Peachy * receive frames from a different p2p device, turn off bssid filter. 487a910e4a9SSolomon Peachy * 488a910e4a9SSolomon Peachy * WARNING: FW dependency! 489a910e4a9SSolomon Peachy * This can only be used with FW WSM371 and its successors. 490a910e4a9SSolomon Peachy * In that FW version even with bssid filter turned off, 491a910e4a9SSolomon Peachy * device will block most of the unwanted frames. 492a910e4a9SSolomon Peachy */ 493a910e4a9SSolomon Peachy if (is_p2p) 494a910e4a9SSolomon Peachy bssid_filtering = false; 495a910e4a9SSolomon Peachy 496a910e4a9SSolomon Peachy ret = wsm_set_rx_filter(priv, &priv->rx_filter); 497a910e4a9SSolomon Peachy if (!ret) 498a910e4a9SSolomon Peachy ret = wsm_set_beacon_filter_table(priv, &bf_tbl); 499a910e4a9SSolomon Peachy if (!ret) 500a910e4a9SSolomon Peachy ret = wsm_beacon_filter_control(priv, &bf_ctrl); 501a910e4a9SSolomon Peachy if (!ret) 502a910e4a9SSolomon Peachy ret = wsm_set_bssid_filtering(priv, bssid_filtering); 503a910e4a9SSolomon Peachy if (!ret) 504a910e4a9SSolomon Peachy ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); 505a910e4a9SSolomon Peachy if (ret) 506a910e4a9SSolomon Peachy wiphy_err(priv->hw->wiphy, 507a910e4a9SSolomon Peachy "Update filtering failed: %d.\n", ret); 508a910e4a9SSolomon Peachy return; 509a910e4a9SSolomon Peachy } 510a910e4a9SSolomon Peachy 511a910e4a9SSolomon Peachy void cw1200_update_filtering_work(struct work_struct *work) 512a910e4a9SSolomon Peachy { 513a910e4a9SSolomon Peachy struct cw1200_common *priv = 514a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, 515a910e4a9SSolomon Peachy update_filtering_work); 516a910e4a9SSolomon Peachy 517a910e4a9SSolomon Peachy cw1200_update_filtering(priv); 518a910e4a9SSolomon Peachy } 519a910e4a9SSolomon Peachy 520a910e4a9SSolomon Peachy void cw1200_set_beacon_wakeup_period_work(struct work_struct *work) 521a910e4a9SSolomon Peachy { 522a910e4a9SSolomon Peachy struct cw1200_common *priv = 523a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, 524a910e4a9SSolomon Peachy set_beacon_wakeup_period_work); 525a910e4a9SSolomon Peachy 526a910e4a9SSolomon Peachy wsm_set_beacon_wakeup_period(priv, 527a910e4a9SSolomon Peachy priv->beacon_int * priv->join_dtim_period > 528a910e4a9SSolomon Peachy MAX_BEACON_SKIP_TIME_MS ? 1 : 529a910e4a9SSolomon Peachy priv->join_dtim_period, 0); 530a910e4a9SSolomon Peachy } 531a910e4a9SSolomon Peachy 532a910e4a9SSolomon Peachy u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, 533a910e4a9SSolomon Peachy struct netdev_hw_addr_list *mc_list) 534a910e4a9SSolomon Peachy { 535a910e4a9SSolomon Peachy static u8 broadcast_ipv6[ETH_ALEN] = { 536a910e4a9SSolomon Peachy 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 537a910e4a9SSolomon Peachy }; 538a910e4a9SSolomon Peachy static u8 broadcast_ipv4[ETH_ALEN] = { 539a910e4a9SSolomon Peachy 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 540a910e4a9SSolomon Peachy }; 541a910e4a9SSolomon Peachy struct cw1200_common *priv = hw->priv; 542a910e4a9SSolomon Peachy struct netdev_hw_addr *ha; 543a910e4a9SSolomon Peachy int count = 0; 544a910e4a9SSolomon Peachy 545a910e4a9SSolomon Peachy /* Disable multicast filtering */ 546a910e4a9SSolomon Peachy priv->has_multicast_subscription = false; 547a910e4a9SSolomon Peachy memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); 548a910e4a9SSolomon Peachy 549a910e4a9SSolomon Peachy if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) 550a910e4a9SSolomon Peachy return 0; 551a910e4a9SSolomon Peachy 552a910e4a9SSolomon Peachy /* Enable if requested */ 553a910e4a9SSolomon Peachy netdev_hw_addr_list_for_each(ha, mc_list) { 554a910e4a9SSolomon Peachy pr_debug("[STA] multicast: %pM\n", ha->addr); 555a910e4a9SSolomon Peachy memcpy(&priv->multicast_filter.macaddrs[count], 556a910e4a9SSolomon Peachy ha->addr, ETH_ALEN); 55735df5388Sdingtianhong if (!ether_addr_equal(ha->addr, broadcast_ipv4) && 55835df5388Sdingtianhong !ether_addr_equal(ha->addr, broadcast_ipv6)) 559a910e4a9SSolomon Peachy priv->has_multicast_subscription = true; 560a910e4a9SSolomon Peachy count++; 561a910e4a9SSolomon Peachy } 562a910e4a9SSolomon Peachy 563a910e4a9SSolomon Peachy if (count) { 564a910e4a9SSolomon Peachy priv->multicast_filter.enable = __cpu_to_le32(1); 565a910e4a9SSolomon Peachy priv->multicast_filter.num_addrs = __cpu_to_le32(count); 566a910e4a9SSolomon Peachy } 567a910e4a9SSolomon Peachy 568a910e4a9SSolomon Peachy return netdev_hw_addr_list_count(mc_list); 569a910e4a9SSolomon Peachy } 570a910e4a9SSolomon Peachy 571a910e4a9SSolomon Peachy void cw1200_configure_filter(struct ieee80211_hw *dev, 572a910e4a9SSolomon Peachy unsigned int changed_flags, 573a910e4a9SSolomon Peachy unsigned int *total_flags, 574a910e4a9SSolomon Peachy u64 multicast) 575a910e4a9SSolomon Peachy { 576a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 577a910e4a9SSolomon Peachy bool listening = !!(*total_flags & 578df140465SJohannes Berg (FIF_OTHER_BSS | 579a910e4a9SSolomon Peachy FIF_BCN_PRBRESP_PROMISC | 580a910e4a9SSolomon Peachy FIF_PROBE_REQ)); 581a910e4a9SSolomon Peachy 582df140465SJohannes Berg *total_flags &= FIF_OTHER_BSS | 583a910e4a9SSolomon Peachy FIF_FCSFAIL | 584a910e4a9SSolomon Peachy FIF_BCN_PRBRESP_PROMISC | 585a910e4a9SSolomon Peachy FIF_PROBE_REQ; 586a910e4a9SSolomon Peachy 587a910e4a9SSolomon Peachy down(&priv->scan.lock); 588a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 589a910e4a9SSolomon Peachy 590df140465SJohannes Berg priv->rx_filter.promiscuous = 0; 591a910e4a9SSolomon Peachy priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | 592a910e4a9SSolomon Peachy FIF_PROBE_REQ)) ? 1 : 0; 593a910e4a9SSolomon Peachy priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; 594a910e4a9SSolomon Peachy priv->disable_beacon_filter = !(*total_flags & 595a910e4a9SSolomon Peachy (FIF_BCN_PRBRESP_PROMISC | 596a910e4a9SSolomon Peachy FIF_PROBE_REQ)); 597a910e4a9SSolomon Peachy if (priv->listening != listening) { 598a910e4a9SSolomon Peachy priv->listening = listening; 599a910e4a9SSolomon Peachy wsm_lock_tx(priv); 600a910e4a9SSolomon Peachy cw1200_update_listening(priv, listening); 601a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 602a910e4a9SSolomon Peachy } 603a910e4a9SSolomon Peachy cw1200_update_filtering(priv); 604a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 605a910e4a9SSolomon Peachy up(&priv->scan.lock); 606a910e4a9SSolomon Peachy } 607a910e4a9SSolomon Peachy 608a910e4a9SSolomon Peachy int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, 609a910e4a9SSolomon Peachy u16 queue, const struct ieee80211_tx_queue_params *params) 610a910e4a9SSolomon Peachy { 611a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 612a910e4a9SSolomon Peachy int ret = 0; 613a910e4a9SSolomon Peachy /* To prevent re-applying PM request OID again and again*/ 614a910e4a9SSolomon Peachy bool old_uapsd_flags; 615a910e4a9SSolomon Peachy 616a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 617a910e4a9SSolomon Peachy 618a910e4a9SSolomon Peachy if (queue < dev->queues) { 6197258416cSSolomon Peachy old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags); 620a910e4a9SSolomon Peachy 621a910e4a9SSolomon Peachy WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); 622a910e4a9SSolomon Peachy ret = wsm_set_tx_queue_params(priv, 623a910e4a9SSolomon Peachy &priv->tx_queue_params.params[queue], queue); 624a910e4a9SSolomon Peachy if (ret) { 625a910e4a9SSolomon Peachy ret = -EINVAL; 626a910e4a9SSolomon Peachy goto out; 627a910e4a9SSolomon Peachy } 628a910e4a9SSolomon Peachy 629a910e4a9SSolomon Peachy WSM_EDCA_SET(&priv->edca, queue, params->aifs, 630a910e4a9SSolomon Peachy params->cw_min, params->cw_max, 631a910e4a9SSolomon Peachy params->txop, 0xc8, 632a910e4a9SSolomon Peachy params->uapsd); 633a910e4a9SSolomon Peachy ret = wsm_set_edca_params(priv, &priv->edca); 634a910e4a9SSolomon Peachy if (ret) { 635a910e4a9SSolomon Peachy ret = -EINVAL; 636a910e4a9SSolomon Peachy goto out; 637a910e4a9SSolomon Peachy } 638a910e4a9SSolomon Peachy 639a910e4a9SSolomon Peachy if (priv->mode == NL80211_IFTYPE_STATION) { 640a910e4a9SSolomon Peachy ret = cw1200_set_uapsd_param(priv, &priv->edca); 641a910e4a9SSolomon Peachy if (!ret && priv->setbssparams_done && 642a910e4a9SSolomon Peachy (priv->join_status == CW1200_JOIN_STATUS_STA) && 6437258416cSSolomon Peachy (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags))) 644a910e4a9SSolomon Peachy ret = cw1200_set_pm(priv, &priv->powersave_mode); 645a910e4a9SSolomon Peachy } 646a910e4a9SSolomon Peachy } else { 647a910e4a9SSolomon Peachy ret = -EINVAL; 648a910e4a9SSolomon Peachy } 649a910e4a9SSolomon Peachy 650a910e4a9SSolomon Peachy out: 651a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 652a910e4a9SSolomon Peachy return ret; 653a910e4a9SSolomon Peachy } 654a910e4a9SSolomon Peachy 655a910e4a9SSolomon Peachy int cw1200_get_stats(struct ieee80211_hw *dev, 656a910e4a9SSolomon Peachy struct ieee80211_low_level_stats *stats) 657a910e4a9SSolomon Peachy { 658a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 659a910e4a9SSolomon Peachy 660a910e4a9SSolomon Peachy memcpy(stats, &priv->stats, sizeof(*stats)); 661a910e4a9SSolomon Peachy return 0; 662a910e4a9SSolomon Peachy } 663a910e4a9SSolomon Peachy 664a910e4a9SSolomon Peachy int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) 665a910e4a9SSolomon Peachy { 666a910e4a9SSolomon Peachy struct wsm_set_pm pm = *arg; 667a910e4a9SSolomon Peachy 668a910e4a9SSolomon Peachy if (priv->uapsd_info.uapsd_flags != 0) 669a910e4a9SSolomon Peachy pm.mode &= ~WSM_PSM_FAST_PS_FLAG; 670a910e4a9SSolomon Peachy 671a910e4a9SSolomon Peachy if (memcmp(&pm, &priv->firmware_ps_mode, 672a910e4a9SSolomon Peachy sizeof(struct wsm_set_pm))) { 673a910e4a9SSolomon Peachy priv->firmware_ps_mode = pm; 674a910e4a9SSolomon Peachy return wsm_set_pm(priv, &pm); 675a910e4a9SSolomon Peachy } else { 676a910e4a9SSolomon Peachy return 0; 677a910e4a9SSolomon Peachy } 678a910e4a9SSolomon Peachy } 679a910e4a9SSolomon Peachy 680a910e4a9SSolomon Peachy int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, 681a910e4a9SSolomon Peachy struct ieee80211_vif *vif, struct ieee80211_sta *sta, 682a910e4a9SSolomon Peachy struct ieee80211_key_conf *key) 683a910e4a9SSolomon Peachy { 684a910e4a9SSolomon Peachy int ret = -EOPNOTSUPP; 685a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 686a910e4a9SSolomon Peachy struct ieee80211_key_seq seq; 687a910e4a9SSolomon Peachy 688a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 689a910e4a9SSolomon Peachy 690a910e4a9SSolomon Peachy if (cmd == SET_KEY) { 691a910e4a9SSolomon Peachy u8 *peer_addr = NULL; 692a910e4a9SSolomon Peachy int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? 693a910e4a9SSolomon Peachy 1 : 0; 694a910e4a9SSolomon Peachy int idx = cw1200_alloc_key(priv); 695a910e4a9SSolomon Peachy struct wsm_add_key *wsm_key = &priv->keys[idx]; 696a910e4a9SSolomon Peachy 697a910e4a9SSolomon Peachy if (idx < 0) { 698a910e4a9SSolomon Peachy ret = -EINVAL; 699a910e4a9SSolomon Peachy goto finally; 700a910e4a9SSolomon Peachy } 701a910e4a9SSolomon Peachy 702a910e4a9SSolomon Peachy if (sta) 703a910e4a9SSolomon Peachy peer_addr = sta->addr; 704a910e4a9SSolomon Peachy 705db12847cSIdo Yariv key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | 706db12847cSIdo Yariv IEEE80211_KEY_FLAG_RESERVE_TAILROOM; 707a910e4a9SSolomon Peachy 708a910e4a9SSolomon Peachy switch (key->cipher) { 709a910e4a9SSolomon Peachy case WLAN_CIPHER_SUITE_WEP40: 710a910e4a9SSolomon Peachy case WLAN_CIPHER_SUITE_WEP104: 711a910e4a9SSolomon Peachy if (key->keylen > 16) { 712a910e4a9SSolomon Peachy cw1200_free_key(priv, idx); 713a910e4a9SSolomon Peachy ret = -EINVAL; 714a910e4a9SSolomon Peachy goto finally; 715a910e4a9SSolomon Peachy } 716a910e4a9SSolomon Peachy 717a910e4a9SSolomon Peachy if (pairwise) { 718a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; 719a910e4a9SSolomon Peachy memcpy(wsm_key->wep_pairwise.peer, 720a910e4a9SSolomon Peachy peer_addr, ETH_ALEN); 721a910e4a9SSolomon Peachy memcpy(wsm_key->wep_pairwise.keydata, 722a910e4a9SSolomon Peachy &key->key[0], key->keylen); 723a910e4a9SSolomon Peachy wsm_key->wep_pairwise.keylen = key->keylen; 724a910e4a9SSolomon Peachy } else { 725a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; 726a910e4a9SSolomon Peachy memcpy(wsm_key->wep_group.keydata, 727a910e4a9SSolomon Peachy &key->key[0], key->keylen); 728a910e4a9SSolomon Peachy wsm_key->wep_group.keylen = key->keylen; 729a910e4a9SSolomon Peachy wsm_key->wep_group.keyid = key->keyidx; 730a910e4a9SSolomon Peachy } 731a910e4a9SSolomon Peachy break; 732a910e4a9SSolomon Peachy case WLAN_CIPHER_SUITE_TKIP: 733a910e4a9SSolomon Peachy ieee80211_get_key_rx_seq(key, 0, &seq); 734a910e4a9SSolomon Peachy if (pairwise) { 735a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; 736a910e4a9SSolomon Peachy memcpy(wsm_key->tkip_pairwise.peer, 737a910e4a9SSolomon Peachy peer_addr, ETH_ALEN); 738a910e4a9SSolomon Peachy memcpy(wsm_key->tkip_pairwise.keydata, 739a910e4a9SSolomon Peachy &key->key[0], 16); 740a910e4a9SSolomon Peachy memcpy(wsm_key->tkip_pairwise.tx_mic_key, 741a910e4a9SSolomon Peachy &key->key[16], 8); 742a910e4a9SSolomon Peachy memcpy(wsm_key->tkip_pairwise.rx_mic_key, 743a910e4a9SSolomon Peachy &key->key[24], 8); 744a910e4a9SSolomon Peachy } else { 745a910e4a9SSolomon Peachy size_t mic_offset = 746a910e4a9SSolomon Peachy (priv->mode == NL80211_IFTYPE_AP) ? 747a910e4a9SSolomon Peachy 16 : 24; 748a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; 749a910e4a9SSolomon Peachy memcpy(wsm_key->tkip_group.keydata, 750a910e4a9SSolomon Peachy &key->key[0], 16); 751a910e4a9SSolomon Peachy memcpy(wsm_key->tkip_group.rx_mic_key, 752a910e4a9SSolomon Peachy &key->key[mic_offset], 8); 753a910e4a9SSolomon Peachy 754a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff; 755a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff; 756a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff; 757a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff; 758a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff; 759a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff; 760a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[6] = 0; 761a910e4a9SSolomon Peachy wsm_key->tkip_group.rx_seqnum[7] = 0; 762a910e4a9SSolomon Peachy 763a910e4a9SSolomon Peachy wsm_key->tkip_group.keyid = key->keyidx; 764a910e4a9SSolomon Peachy } 765a910e4a9SSolomon Peachy break; 766a910e4a9SSolomon Peachy case WLAN_CIPHER_SUITE_CCMP: 767a910e4a9SSolomon Peachy ieee80211_get_key_rx_seq(key, 0, &seq); 768a910e4a9SSolomon Peachy if (pairwise) { 769a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; 770a910e4a9SSolomon Peachy memcpy(wsm_key->aes_pairwise.peer, 771a910e4a9SSolomon Peachy peer_addr, ETH_ALEN); 772a910e4a9SSolomon Peachy memcpy(wsm_key->aes_pairwise.keydata, 773a910e4a9SSolomon Peachy &key->key[0], 16); 774a910e4a9SSolomon Peachy } else { 775a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_AES_GROUP; 776a910e4a9SSolomon Peachy memcpy(wsm_key->aes_group.keydata, 777a910e4a9SSolomon Peachy &key->key[0], 16); 778a910e4a9SSolomon Peachy 779a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5]; 780a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4]; 781a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3]; 782a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2]; 783a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1]; 784a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0]; 785a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[6] = 0; 786a910e4a9SSolomon Peachy wsm_key->aes_group.rx_seqnum[7] = 0; 787a910e4a9SSolomon Peachy wsm_key->aes_group.keyid = key->keyidx; 788a910e4a9SSolomon Peachy } 789a910e4a9SSolomon Peachy break; 790a910e4a9SSolomon Peachy case WLAN_CIPHER_SUITE_SMS4: 791a910e4a9SSolomon Peachy if (pairwise) { 792a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; 793a910e4a9SSolomon Peachy memcpy(wsm_key->wapi_pairwise.peer, 794a910e4a9SSolomon Peachy peer_addr, ETH_ALEN); 795a910e4a9SSolomon Peachy memcpy(wsm_key->wapi_pairwise.keydata, 796a910e4a9SSolomon Peachy &key->key[0], 16); 797a910e4a9SSolomon Peachy memcpy(wsm_key->wapi_pairwise.mic_key, 798a910e4a9SSolomon Peachy &key->key[16], 16); 799a910e4a9SSolomon Peachy wsm_key->wapi_pairwise.keyid = key->keyidx; 800a910e4a9SSolomon Peachy } else { 801a910e4a9SSolomon Peachy wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; 802a910e4a9SSolomon Peachy memcpy(wsm_key->wapi_group.keydata, 803a910e4a9SSolomon Peachy &key->key[0], 16); 804a910e4a9SSolomon Peachy memcpy(wsm_key->wapi_group.mic_key, 805a910e4a9SSolomon Peachy &key->key[16], 16); 806a910e4a9SSolomon Peachy wsm_key->wapi_group.keyid = key->keyidx; 807a910e4a9SSolomon Peachy } 808a910e4a9SSolomon Peachy break; 809a910e4a9SSolomon Peachy default: 810a910e4a9SSolomon Peachy pr_warn("Unhandled key type %d\n", key->cipher); 811a910e4a9SSolomon Peachy cw1200_free_key(priv, idx); 812a910e4a9SSolomon Peachy ret = -EOPNOTSUPP; 813a910e4a9SSolomon Peachy goto finally; 814a910e4a9SSolomon Peachy } 815a910e4a9SSolomon Peachy ret = wsm_add_key(priv, wsm_key); 816a910e4a9SSolomon Peachy if (!ret) 817a910e4a9SSolomon Peachy key->hw_key_idx = idx; 818a910e4a9SSolomon Peachy else 819a910e4a9SSolomon Peachy cw1200_free_key(priv, idx); 820a910e4a9SSolomon Peachy } else if (cmd == DISABLE_KEY) { 821a910e4a9SSolomon Peachy struct wsm_remove_key wsm_key = { 822a910e4a9SSolomon Peachy .index = key->hw_key_idx, 823a910e4a9SSolomon Peachy }; 824a910e4a9SSolomon Peachy 825a910e4a9SSolomon Peachy if (wsm_key.index > WSM_KEY_MAX_INDEX) { 826a910e4a9SSolomon Peachy ret = -EINVAL; 827a910e4a9SSolomon Peachy goto finally; 828a910e4a9SSolomon Peachy } 829a910e4a9SSolomon Peachy 830a910e4a9SSolomon Peachy cw1200_free_key(priv, wsm_key.index); 831a910e4a9SSolomon Peachy ret = wsm_remove_key(priv, &wsm_key); 832a910e4a9SSolomon Peachy } else { 833a910e4a9SSolomon Peachy pr_warn("Unhandled key command %d\n", cmd); 834a910e4a9SSolomon Peachy } 835a910e4a9SSolomon Peachy 836a910e4a9SSolomon Peachy finally: 837a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 838a910e4a9SSolomon Peachy return ret; 839a910e4a9SSolomon Peachy } 840a910e4a9SSolomon Peachy 841a910e4a9SSolomon Peachy void cw1200_wep_key_work(struct work_struct *work) 842a910e4a9SSolomon Peachy { 843a910e4a9SSolomon Peachy struct cw1200_common *priv = 844a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, wep_key_work); 845a910e4a9SSolomon Peachy u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); 846a910e4a9SSolomon Peachy struct cw1200_queue *queue = &priv->tx_queue[queue_id]; 847a910e4a9SSolomon Peachy __le32 wep_default_key_id = __cpu_to_le32( 848a910e4a9SSolomon Peachy priv->wep_default_key_id); 849a910e4a9SSolomon Peachy 850a910e4a9SSolomon Peachy pr_debug("[STA] Setting default WEP key: %d\n", 851a910e4a9SSolomon Peachy priv->wep_default_key_id); 852a910e4a9SSolomon Peachy wsm_flush_tx(priv); 853a910e4a9SSolomon Peachy wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, 854a910e4a9SSolomon Peachy &wep_default_key_id, sizeof(wep_default_key_id)); 855a910e4a9SSolomon Peachy cw1200_queue_requeue(queue, priv->pending_frame_id); 856a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 857a910e4a9SSolomon Peachy } 858a910e4a9SSolomon Peachy 859a910e4a9SSolomon Peachy int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) 860a910e4a9SSolomon Peachy { 861a910e4a9SSolomon Peachy int ret = 0; 862a910e4a9SSolomon Peachy __le32 val32; 863a910e4a9SSolomon Peachy struct cw1200_common *priv = hw->priv; 864a910e4a9SSolomon Peachy 865a910e4a9SSolomon Peachy if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) 866a910e4a9SSolomon Peachy return 0; 867a910e4a9SSolomon Peachy 868a910e4a9SSolomon Peachy if (value != (u32) -1) 869a910e4a9SSolomon Peachy val32 = __cpu_to_le32(value); 870a910e4a9SSolomon Peachy else 871a910e4a9SSolomon Peachy val32 = 0; /* disabled */ 872a910e4a9SSolomon Peachy 873a910e4a9SSolomon Peachy if (priv->rts_threshold == value) 874a910e4a9SSolomon Peachy goto out; 875a910e4a9SSolomon Peachy 876a910e4a9SSolomon Peachy pr_debug("[STA] Setting RTS threshold: %d\n", 877a910e4a9SSolomon Peachy priv->rts_threshold); 878a910e4a9SSolomon Peachy 879a910e4a9SSolomon Peachy /* mutex_lock(&priv->conf_mutex); */ 880a910e4a9SSolomon Peachy ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, 881a910e4a9SSolomon Peachy &val32, sizeof(val32)); 882a910e4a9SSolomon Peachy if (!ret) 883a910e4a9SSolomon Peachy priv->rts_threshold = value; 884a910e4a9SSolomon Peachy /* mutex_unlock(&priv->conf_mutex); */ 885a910e4a9SSolomon Peachy 886a910e4a9SSolomon Peachy out: 887a910e4a9SSolomon Peachy return ret; 888a910e4a9SSolomon Peachy } 889a910e4a9SSolomon Peachy 890a910e4a9SSolomon Peachy /* If successful, LOCKS the TX queue! */ 891a910e4a9SSolomon Peachy static int __cw1200_flush(struct cw1200_common *priv, bool drop) 892a910e4a9SSolomon Peachy { 893a910e4a9SSolomon Peachy int i, ret; 894a910e4a9SSolomon Peachy 895a910e4a9SSolomon Peachy for (;;) { 896a910e4a9SSolomon Peachy /* TODO: correct flush handling is required when dev_stop. 897a910e4a9SSolomon Peachy * Temporary workaround: 2s 898a910e4a9SSolomon Peachy */ 899a910e4a9SSolomon Peachy if (drop) { 900a910e4a9SSolomon Peachy for (i = 0; i < 4; ++i) 901a910e4a9SSolomon Peachy cw1200_queue_clear(&priv->tx_queue[i]); 902a910e4a9SSolomon Peachy } else { 903a910e4a9SSolomon Peachy ret = wait_event_timeout( 904a910e4a9SSolomon Peachy priv->tx_queue_stats.wait_link_id_empty, 905a910e4a9SSolomon Peachy cw1200_queue_stats_is_empty( 906a910e4a9SSolomon Peachy &priv->tx_queue_stats, -1), 907a910e4a9SSolomon Peachy 2 * HZ); 908a910e4a9SSolomon Peachy } 909a910e4a9SSolomon Peachy 910a910e4a9SSolomon Peachy if (!drop && ret <= 0) { 911a910e4a9SSolomon Peachy ret = -ETIMEDOUT; 912a910e4a9SSolomon Peachy break; 913a910e4a9SSolomon Peachy } else { 914a910e4a9SSolomon Peachy ret = 0; 915a910e4a9SSolomon Peachy } 916a910e4a9SSolomon Peachy 917a910e4a9SSolomon Peachy wsm_lock_tx(priv); 918a910e4a9SSolomon Peachy if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) { 919a910e4a9SSolomon Peachy /* Highly unlikely: WSM requeued frames. */ 920a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 921a910e4a9SSolomon Peachy continue; 922a910e4a9SSolomon Peachy } 923a910e4a9SSolomon Peachy break; 924a910e4a9SSolomon Peachy } 925a910e4a9SSolomon Peachy return ret; 926a910e4a9SSolomon Peachy } 927a910e4a9SSolomon Peachy 92877be2c54SEmmanuel Grumbach void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 92977be2c54SEmmanuel Grumbach u32 queues, bool drop) 930a910e4a9SSolomon Peachy { 931a910e4a9SSolomon Peachy struct cw1200_common *priv = hw->priv; 932a910e4a9SSolomon Peachy 933a910e4a9SSolomon Peachy switch (priv->mode) { 934a910e4a9SSolomon Peachy case NL80211_IFTYPE_MONITOR: 935a910e4a9SSolomon Peachy drop = true; 936a910e4a9SSolomon Peachy break; 937a910e4a9SSolomon Peachy case NL80211_IFTYPE_AP: 938a910e4a9SSolomon Peachy if (!priv->enable_beacon) 939a910e4a9SSolomon Peachy drop = true; 940a910e4a9SSolomon Peachy break; 941a910e4a9SSolomon Peachy } 942a910e4a9SSolomon Peachy 943a910e4a9SSolomon Peachy if (!__cw1200_flush(priv, drop)) 944a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 945a910e4a9SSolomon Peachy 946a910e4a9SSolomon Peachy return; 947a910e4a9SSolomon Peachy } 948a910e4a9SSolomon Peachy 949a910e4a9SSolomon Peachy /* ******************************************************************** */ 950a910e4a9SSolomon Peachy /* WSM callbacks */ 951a910e4a9SSolomon Peachy 952a910e4a9SSolomon Peachy void cw1200_free_event_queue(struct cw1200_common *priv) 953a910e4a9SSolomon Peachy { 954a910e4a9SSolomon Peachy LIST_HEAD(list); 955a910e4a9SSolomon Peachy 956a910e4a9SSolomon Peachy spin_lock(&priv->event_queue_lock); 957a910e4a9SSolomon Peachy list_splice_init(&priv->event_queue, &list); 958a910e4a9SSolomon Peachy spin_unlock(&priv->event_queue_lock); 959a910e4a9SSolomon Peachy 960a910e4a9SSolomon Peachy __cw1200_free_event_queue(&list); 961a910e4a9SSolomon Peachy } 962a910e4a9SSolomon Peachy 963a910e4a9SSolomon Peachy void cw1200_event_handler(struct work_struct *work) 964a910e4a9SSolomon Peachy { 965a910e4a9SSolomon Peachy struct cw1200_common *priv = 966a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, event_handler); 967a910e4a9SSolomon Peachy struct cw1200_wsm_event *event; 968a910e4a9SSolomon Peachy LIST_HEAD(list); 969a910e4a9SSolomon Peachy 970a910e4a9SSolomon Peachy spin_lock(&priv->event_queue_lock); 971a910e4a9SSolomon Peachy list_splice_init(&priv->event_queue, &list); 972a910e4a9SSolomon Peachy spin_unlock(&priv->event_queue_lock); 973a910e4a9SSolomon Peachy 974a910e4a9SSolomon Peachy list_for_each_entry(event, &list, link) { 975a910e4a9SSolomon Peachy switch (event->evt.id) { 976a910e4a9SSolomon Peachy case WSM_EVENT_ERROR: 977a910e4a9SSolomon Peachy pr_err("Unhandled WSM Error from LMAC\n"); 978a910e4a9SSolomon Peachy break; 979a910e4a9SSolomon Peachy case WSM_EVENT_BSS_LOST: 980a910e4a9SSolomon Peachy pr_debug("[CQM] BSS lost.\n"); 981a910e4a9SSolomon Peachy cancel_work_sync(&priv->unjoin_work); 982a910e4a9SSolomon Peachy if (!down_trylock(&priv->scan.lock)) { 983a910e4a9SSolomon Peachy cw1200_cqm_bssloss_sm(priv, 1, 0, 0); 984a910e4a9SSolomon Peachy up(&priv->scan.lock); 985a910e4a9SSolomon Peachy } else { 986a910e4a9SSolomon Peachy /* Scan is in progress. Delay reporting. 987a910e4a9SSolomon Peachy * Scan complete will trigger bss_loss_work 988a910e4a9SSolomon Peachy */ 989a910e4a9SSolomon Peachy priv->delayed_link_loss = 1; 990a910e4a9SSolomon Peachy /* Also start a watchdog. */ 991a910e4a9SSolomon Peachy queue_delayed_work(priv->workqueue, 992a910e4a9SSolomon Peachy &priv->bss_loss_work, 5*HZ); 993a910e4a9SSolomon Peachy } 994a910e4a9SSolomon Peachy break; 995a910e4a9SSolomon Peachy case WSM_EVENT_BSS_REGAINED: 996a910e4a9SSolomon Peachy pr_debug("[CQM] BSS regained.\n"); 997a910e4a9SSolomon Peachy cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 998a910e4a9SSolomon Peachy cancel_work_sync(&priv->unjoin_work); 999a910e4a9SSolomon Peachy break; 1000a910e4a9SSolomon Peachy case WSM_EVENT_RADAR_DETECTED: 1001a910e4a9SSolomon Peachy wiphy_info(priv->hw->wiphy, "radar pulse detected\n"); 1002a910e4a9SSolomon Peachy break; 1003a910e4a9SSolomon Peachy case WSM_EVENT_RCPI_RSSI: 1004a910e4a9SSolomon Peachy { 1005a910e4a9SSolomon Peachy /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 1006a910e4a9SSolomon Peachy * RSSI = RCPI / 2 - 110 1007a910e4a9SSolomon Peachy */ 10088b3e7be4SSolomon Peachy int rcpi_rssi = (int)(event->evt.data & 0xFF); 1009a910e4a9SSolomon Peachy int cqm_evt; 1010a910e4a9SSolomon Peachy if (priv->cqm_use_rssi) 10118b3e7be4SSolomon Peachy rcpi_rssi = (s8)rcpi_rssi; 1012a910e4a9SSolomon Peachy else 10138b3e7be4SSolomon Peachy rcpi_rssi = rcpi_rssi / 2 - 110; 1014a910e4a9SSolomon Peachy 10158b3e7be4SSolomon Peachy cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ? 1016a910e4a9SSolomon Peachy NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : 1017a910e4a9SSolomon Peachy NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; 10188b3e7be4SSolomon Peachy pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi); 1019769f07d8SAndrzej Zaborowski ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, rcpi_rssi, 1020a910e4a9SSolomon Peachy GFP_KERNEL); 1021a910e4a9SSolomon Peachy break; 1022a910e4a9SSolomon Peachy } 1023a910e4a9SSolomon Peachy case WSM_EVENT_BT_INACTIVE: 1024a910e4a9SSolomon Peachy pr_warn("Unhandled BT INACTIVE from LMAC\n"); 1025a910e4a9SSolomon Peachy break; 1026a910e4a9SSolomon Peachy case WSM_EVENT_BT_ACTIVE: 1027a910e4a9SSolomon Peachy pr_warn("Unhandled BT ACTIVE from LMAC\n"); 1028a910e4a9SSolomon Peachy break; 1029a910e4a9SSolomon Peachy } 1030a910e4a9SSolomon Peachy } 1031a910e4a9SSolomon Peachy __cw1200_free_event_queue(&list); 1032a910e4a9SSolomon Peachy } 1033a910e4a9SSolomon Peachy 1034a910e4a9SSolomon Peachy void cw1200_bss_loss_work(struct work_struct *work) 1035a910e4a9SSolomon Peachy { 1036a910e4a9SSolomon Peachy struct cw1200_common *priv = 1037a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, bss_loss_work.work); 1038a910e4a9SSolomon Peachy 1039a910e4a9SSolomon Peachy pr_debug("[CQM] Reporting connection loss.\n"); 1040a910e4a9SSolomon Peachy wsm_lock_tx(priv); 1041a910e4a9SSolomon Peachy if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 1042a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1043a910e4a9SSolomon Peachy } 1044a910e4a9SSolomon Peachy 1045a910e4a9SSolomon Peachy void cw1200_bss_params_work(struct work_struct *work) 1046a910e4a9SSolomon Peachy { 1047a910e4a9SSolomon Peachy struct cw1200_common *priv = 1048a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, bss_params_work); 1049a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 1050a910e4a9SSolomon Peachy 1051a910e4a9SSolomon Peachy priv->bss_params.reset_beacon_loss = 1; 1052a910e4a9SSolomon Peachy wsm_set_bss_params(priv, &priv->bss_params); 1053a910e4a9SSolomon Peachy priv->bss_params.reset_beacon_loss = 0; 1054a910e4a9SSolomon Peachy 1055a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 1056a910e4a9SSolomon Peachy } 1057a910e4a9SSolomon Peachy 1058a910e4a9SSolomon Peachy /* ******************************************************************** */ 1059a910e4a9SSolomon Peachy /* Internal API */ 1060a910e4a9SSolomon Peachy 10618b3e7be4SSolomon Peachy /* This function is called to Parse the SDD file 1062a910e4a9SSolomon Peachy * to extract listen_interval and PTA related information 1063a910e4a9SSolomon Peachy * sdd is a TLV: u8 id, u8 len, u8 data[] 1064a910e4a9SSolomon Peachy */ 1065a910e4a9SSolomon Peachy static int cw1200_parse_sdd_file(struct cw1200_common *priv) 1066a910e4a9SSolomon Peachy { 1067a910e4a9SSolomon Peachy const u8 *p = priv->sdd->data; 1068a910e4a9SSolomon Peachy int ret = 0; 1069a910e4a9SSolomon Peachy 1070a910e4a9SSolomon Peachy while (p + 2 <= priv->sdd->data + priv->sdd->size) { 1071a910e4a9SSolomon Peachy if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) { 1072a910e4a9SSolomon Peachy pr_warn("Malformed sdd structure\n"); 1073a910e4a9SSolomon Peachy return -1; 1074a910e4a9SSolomon Peachy } 1075a910e4a9SSolomon Peachy switch (p[0]) { 1076a910e4a9SSolomon Peachy case SDD_PTA_CFG_ELT_ID: { 1077a910e4a9SSolomon Peachy u16 v; 1078a910e4a9SSolomon Peachy if (p[1] < 4) { 1079a910e4a9SSolomon Peachy pr_warn("SDD_PTA_CFG_ELT_ID malformed\n"); 1080a910e4a9SSolomon Peachy ret = -1; 1081a910e4a9SSolomon Peachy break; 1082a910e4a9SSolomon Peachy } 10837258416cSSolomon Peachy v = le16_to_cpu(*((__le16 *)(p + 2))); 1084a910e4a9SSolomon Peachy if (!v) /* non-zero means this is enabled */ 1085a910e4a9SSolomon Peachy break; 1086a910e4a9SSolomon Peachy 10877258416cSSolomon Peachy v = le16_to_cpu(*((__le16 *)(p + 4))); 1088a910e4a9SSolomon Peachy priv->conf_listen_interval = (v >> 7) & 0x1F; 1089a910e4a9SSolomon Peachy pr_debug("PTA found; Listen Interval %d\n", 1090a910e4a9SSolomon Peachy priv->conf_listen_interval); 1091a910e4a9SSolomon Peachy break; 1092a910e4a9SSolomon Peachy } 1093a910e4a9SSolomon Peachy case SDD_REFERENCE_FREQUENCY_ELT_ID: { 10947258416cSSolomon Peachy u16 clk = le16_to_cpu(*((__le16 *)(p + 2))); 1095a910e4a9SSolomon Peachy if (clk != priv->hw_refclk) 1096a910e4a9SSolomon Peachy pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n", 1097a910e4a9SSolomon Peachy clk, priv->hw_refclk); 1098a910e4a9SSolomon Peachy break; 1099a910e4a9SSolomon Peachy } 1100a910e4a9SSolomon Peachy default: 1101a910e4a9SSolomon Peachy break; 1102a910e4a9SSolomon Peachy } 1103a910e4a9SSolomon Peachy p += p[1] + 2; 1104a910e4a9SSolomon Peachy } 1105a910e4a9SSolomon Peachy 1106a910e4a9SSolomon Peachy if (!priv->bt_present) { 1107a910e4a9SSolomon Peachy pr_debug("PTA element NOT found.\n"); 1108a910e4a9SSolomon Peachy priv->conf_listen_interval = 0; 1109a910e4a9SSolomon Peachy } 1110a910e4a9SSolomon Peachy return ret; 1111a910e4a9SSolomon Peachy } 1112a910e4a9SSolomon Peachy 1113a910e4a9SSolomon Peachy int cw1200_setup_mac(struct cw1200_common *priv) 1114a910e4a9SSolomon Peachy { 1115a910e4a9SSolomon Peachy int ret = 0; 1116a910e4a9SSolomon Peachy 1117a910e4a9SSolomon Peachy /* NOTE: There is a bug in FW: it reports signal 1118a910e4a9SSolomon Peachy * as RSSI if RSSI subscription is enabled. 1119a910e4a9SSolomon Peachy * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. 1120a910e4a9SSolomon Peachy * 1121a910e4a9SSolomon Peachy * NOTE2: RSSI based reports have been switched to RCPI, since 1122a910e4a9SSolomon Peachy * FW has a bug and RSSI reported values are not stable, 1123f4bd758fSYangtao Li * what can lead to signal level oscilations in user-end applications 1124a910e4a9SSolomon Peachy */ 1125a910e4a9SSolomon Peachy struct wsm_rcpi_rssi_threshold threshold = { 1126a910e4a9SSolomon Peachy .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | 1127a910e4a9SSolomon Peachy WSM_RCPI_RSSI_DONT_USE_UPPER | 1128a910e4a9SSolomon Peachy WSM_RCPI_RSSI_DONT_USE_LOWER, 1129a910e4a9SSolomon Peachy .rollingAverageCount = 16, 1130a910e4a9SSolomon Peachy }; 1131a910e4a9SSolomon Peachy 1132a910e4a9SSolomon Peachy struct wsm_configuration cfg = { 1133a910e4a9SSolomon Peachy .dot11StationId = &priv->mac_addr[0], 1134a910e4a9SSolomon Peachy }; 1135a910e4a9SSolomon Peachy 1136a910e4a9SSolomon Peachy /* Remember the decission here to make sure, we will handle 1137a910e4a9SSolomon Peachy * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS 1138a910e4a9SSolomon Peachy */ 1139a910e4a9SSolomon Peachy if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) 1140a910e4a9SSolomon Peachy priv->cqm_use_rssi = true; 1141a910e4a9SSolomon Peachy 1142a910e4a9SSolomon Peachy if (!priv->sdd) { 1143a910e4a9SSolomon Peachy ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev); 1144a910e4a9SSolomon Peachy if (ret) { 1145a910e4a9SSolomon Peachy pr_err("Can't load sdd file %s.\n", priv->sdd_path); 1146a910e4a9SSolomon Peachy return ret; 1147a910e4a9SSolomon Peachy } 1148a910e4a9SSolomon Peachy cw1200_parse_sdd_file(priv); 1149a910e4a9SSolomon Peachy } 1150a910e4a9SSolomon Peachy 1151a910e4a9SSolomon Peachy cfg.dpdData = priv->sdd->data; 1152a910e4a9SSolomon Peachy cfg.dpdData_size = priv->sdd->size; 1153a910e4a9SSolomon Peachy ret = wsm_configuration(priv, &cfg); 1154a910e4a9SSolomon Peachy if (ret) 1155a910e4a9SSolomon Peachy return ret; 1156a910e4a9SSolomon Peachy 1157a910e4a9SSolomon Peachy /* Configure RSSI/SCPI reporting as RSSI. */ 1158a910e4a9SSolomon Peachy wsm_set_rcpi_rssi_threshold(priv, &threshold); 1159a910e4a9SSolomon Peachy 1160a910e4a9SSolomon Peachy return 0; 1161a910e4a9SSolomon Peachy } 1162a910e4a9SSolomon Peachy 1163a910e4a9SSolomon Peachy static void cw1200_join_complete(struct cw1200_common *priv) 1164a910e4a9SSolomon Peachy { 1165a910e4a9SSolomon Peachy pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status); 1166a910e4a9SSolomon Peachy 1167a910e4a9SSolomon Peachy priv->join_pending = false; 1168a910e4a9SSolomon Peachy if (priv->join_complete_status) { 1169a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 1170a910e4a9SSolomon Peachy cw1200_update_listening(priv, priv->listening); 1171a910e4a9SSolomon Peachy cw1200_do_unjoin(priv); 1172a910e4a9SSolomon Peachy ieee80211_connection_loss(priv->vif); 1173a910e4a9SSolomon Peachy } else { 1174a910e4a9SSolomon Peachy if (priv->mode == NL80211_IFTYPE_ADHOC) 1175a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_IBSS; 1176a910e4a9SSolomon Peachy else 1177a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PRE_STA; 1178a910e4a9SSolomon Peachy } 1179a910e4a9SSolomon Peachy wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */ 1180a910e4a9SSolomon Peachy } 1181a910e4a9SSolomon Peachy 1182a910e4a9SSolomon Peachy void cw1200_join_complete_work(struct work_struct *work) 1183a910e4a9SSolomon Peachy { 1184a910e4a9SSolomon Peachy struct cw1200_common *priv = 1185a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, join_complete_work); 1186a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 1187a910e4a9SSolomon Peachy cw1200_join_complete(priv); 1188a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 1189a910e4a9SSolomon Peachy } 1190a910e4a9SSolomon Peachy 1191a910e4a9SSolomon Peachy void cw1200_join_complete_cb(struct cw1200_common *priv, 1192a910e4a9SSolomon Peachy struct wsm_join_complete *arg) 1193a910e4a9SSolomon Peachy { 1194a910e4a9SSolomon Peachy pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n", 1195a910e4a9SSolomon Peachy arg->status); 1196a910e4a9SSolomon Peachy 1197a910e4a9SSolomon Peachy if (cancel_delayed_work(&priv->join_timeout)) { 1198a910e4a9SSolomon Peachy priv->join_complete_status = arg->status; 1199a910e4a9SSolomon Peachy queue_work(priv->workqueue, &priv->join_complete_work); 1200a910e4a9SSolomon Peachy } 1201a910e4a9SSolomon Peachy } 1202a910e4a9SSolomon Peachy 1203a910e4a9SSolomon Peachy /* MUST be called with tx_lock held! It will be unlocked for us. */ 1204a910e4a9SSolomon Peachy static void cw1200_do_join(struct cw1200_common *priv) 1205a910e4a9SSolomon Peachy { 1206a910e4a9SSolomon Peachy const u8 *bssid; 1207a910e4a9SSolomon Peachy struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; 1208a910e4a9SSolomon Peachy struct cfg80211_bss *bss = NULL; 1209a910e4a9SSolomon Peachy struct wsm_protected_mgmt_policy mgmt_policy; 1210a910e4a9SSolomon Peachy struct wsm_join join = { 1211a910e4a9SSolomon Peachy .mode = conf->ibss_joined ? 1212a910e4a9SSolomon Peachy WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, 1213a910e4a9SSolomon Peachy .preamble_type = WSM_JOIN_PREAMBLE_LONG, 1214a910e4a9SSolomon Peachy .probe_for_join = 1, 1215a910e4a9SSolomon Peachy .atim_window = 0, 1216a910e4a9SSolomon Peachy .basic_rate_set = cw1200_rate_mask_to_wsm(priv, 1217a910e4a9SSolomon Peachy conf->basic_rates), 1218a910e4a9SSolomon Peachy }; 1219a910e4a9SSolomon Peachy if (delayed_work_pending(&priv->join_timeout)) { 1220a910e4a9SSolomon Peachy pr_warn("[STA] - Join request already pending, skipping..\n"); 1221a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1222a910e4a9SSolomon Peachy return; 1223a910e4a9SSolomon Peachy } 1224a910e4a9SSolomon Peachy 1225a910e4a9SSolomon Peachy if (priv->join_status) 1226a910e4a9SSolomon Peachy cw1200_do_unjoin(priv); 1227a910e4a9SSolomon Peachy 1228a910e4a9SSolomon Peachy bssid = priv->vif->bss_conf.bssid; 1229a910e4a9SSolomon Peachy 12306eb18137SDedy Lansky bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0, 12316eb18137SDedy Lansky IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); 1232a910e4a9SSolomon Peachy 1233a910e4a9SSolomon Peachy if (!bss && !conf->ibss_joined) { 1234a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1235a910e4a9SSolomon Peachy return; 1236a910e4a9SSolomon Peachy } 1237a910e4a9SSolomon Peachy 1238a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 1239a910e4a9SSolomon Peachy 1240a910e4a9SSolomon Peachy /* Under the conf lock: check scan status and 1241a910e4a9SSolomon Peachy * bail out if it is in progress. 1242a910e4a9SSolomon Peachy */ 1243a910e4a9SSolomon Peachy if (atomic_read(&priv->scan.in_progress)) { 1244a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1245a910e4a9SSolomon Peachy goto done_put; 1246a910e4a9SSolomon Peachy } 1247a910e4a9SSolomon Peachy 1248a910e4a9SSolomon Peachy priv->join_pending = true; 1249a910e4a9SSolomon Peachy 1250a910e4a9SSolomon Peachy /* Sanity check basic rates */ 1251a910e4a9SSolomon Peachy if (!join.basic_rate_set) 1252a910e4a9SSolomon Peachy join.basic_rate_set = 7; 1253a910e4a9SSolomon Peachy 1254a910e4a9SSolomon Peachy /* Sanity check beacon interval */ 1255a910e4a9SSolomon Peachy if (!priv->beacon_int) 1256a910e4a9SSolomon Peachy priv->beacon_int = 1; 1257a910e4a9SSolomon Peachy 1258a910e4a9SSolomon Peachy join.beacon_interval = priv->beacon_int; 1259a910e4a9SSolomon Peachy 1260a910e4a9SSolomon Peachy /* BT Coex related changes */ 1261a910e4a9SSolomon Peachy if (priv->bt_present) { 1262a910e4a9SSolomon Peachy if (((priv->conf_listen_interval * 100) % 1263a910e4a9SSolomon Peachy priv->beacon_int) == 0) 1264a910e4a9SSolomon Peachy priv->listen_interval = 1265a910e4a9SSolomon Peachy ((priv->conf_listen_interval * 100) / 1266a910e4a9SSolomon Peachy priv->beacon_int); 1267a910e4a9SSolomon Peachy else 1268a910e4a9SSolomon Peachy priv->listen_interval = 1269a910e4a9SSolomon Peachy ((priv->conf_listen_interval * 100) / 1270a910e4a9SSolomon Peachy priv->beacon_int + 1); 1271a910e4a9SSolomon Peachy } 1272a910e4a9SSolomon Peachy 1273a910e4a9SSolomon Peachy if (priv->hw->conf.ps_dtim_period) 1274a910e4a9SSolomon Peachy priv->join_dtim_period = priv->hw->conf.ps_dtim_period; 1275a910e4a9SSolomon Peachy join.dtim_period = priv->join_dtim_period; 1276a910e4a9SSolomon Peachy 1277a910e4a9SSolomon Peachy join.channel_number = priv->channel->hw_value; 127857fbcce3SJohannes Berg join.band = (priv->channel->band == NL80211_BAND_5GHZ) ? 1279a910e4a9SSolomon Peachy WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; 1280a910e4a9SSolomon Peachy 1281a910e4a9SSolomon Peachy memcpy(join.bssid, bssid, sizeof(join.bssid)); 1282a910e4a9SSolomon Peachy 1283a910e4a9SSolomon Peachy pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n", 1284a910e4a9SSolomon Peachy join.bssid, 1285a910e4a9SSolomon Peachy join.dtim_period, priv->beacon_int); 1286a910e4a9SSolomon Peachy 1287a910e4a9SSolomon Peachy if (!conf->ibss_joined) { 1288a910e4a9SSolomon Peachy const u8 *ssidie; 1289a910e4a9SSolomon Peachy rcu_read_lock(); 1290a910e4a9SSolomon Peachy ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); 1291a910e4a9SSolomon Peachy if (ssidie) { 1292a910e4a9SSolomon Peachy join.ssid_len = ssidie[1]; 1293a910e4a9SSolomon Peachy memcpy(join.ssid, &ssidie[2], join.ssid_len); 1294a910e4a9SSolomon Peachy } 1295a910e4a9SSolomon Peachy rcu_read_unlock(); 1296a910e4a9SSolomon Peachy } 1297a910e4a9SSolomon Peachy 1298a910e4a9SSolomon Peachy if (priv->vif->p2p) { 1299a910e4a9SSolomon Peachy join.flags |= WSM_JOIN_FLAGS_P2P_GO; 1300a910e4a9SSolomon Peachy join.basic_rate_set = 1301a910e4a9SSolomon Peachy cw1200_rate_mask_to_wsm(priv, 0xFF0); 1302a910e4a9SSolomon Peachy } 1303a910e4a9SSolomon Peachy 1304a910e4a9SSolomon Peachy /* Enable asynchronous join calls */ 1305a910e4a9SSolomon Peachy if (!conf->ibss_joined) { 1306a910e4a9SSolomon Peachy join.flags |= WSM_JOIN_FLAGS_FORCE; 1307a910e4a9SSolomon Peachy join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND; 1308a910e4a9SSolomon Peachy } 1309a910e4a9SSolomon Peachy 1310a910e4a9SSolomon Peachy wsm_flush_tx(priv); 1311a910e4a9SSolomon Peachy 1312a910e4a9SSolomon Peachy /* Stay Awake for Join and Auth Timeouts and a bit more */ 1313a910e4a9SSolomon Peachy cw1200_pm_stay_awake(&priv->pm_state, 1314a910e4a9SSolomon Peachy CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT); 1315a910e4a9SSolomon Peachy 1316a910e4a9SSolomon Peachy cw1200_update_listening(priv, false); 1317a910e4a9SSolomon Peachy 1318a910e4a9SSolomon Peachy /* Turn on Block ACKs */ 1319a910e4a9SSolomon Peachy wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask, 1320a910e4a9SSolomon Peachy priv->ba_rx_tid_mask); 1321a910e4a9SSolomon Peachy 1322a910e4a9SSolomon Peachy /* Set up timeout */ 1323a910e4a9SSolomon Peachy if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) { 1324a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_JOINING; 1325a910e4a9SSolomon Peachy queue_delayed_work(priv->workqueue, 1326a910e4a9SSolomon Peachy &priv->join_timeout, 1327a910e4a9SSolomon Peachy CW1200_JOIN_TIMEOUT); 1328a910e4a9SSolomon Peachy } 1329a910e4a9SSolomon Peachy 1330a910e4a9SSolomon Peachy /* 802.11w protected mgmt frames */ 1331a910e4a9SSolomon Peachy mgmt_policy.protectedMgmtEnable = 0; 1332a910e4a9SSolomon Peachy mgmt_policy.unprotectedMgmtFramesAllowed = 1; 1333a910e4a9SSolomon Peachy mgmt_policy.encryptionForAuthFrame = 1; 1334a910e4a9SSolomon Peachy wsm_set_protected_mgmt_policy(priv, &mgmt_policy); 1335a910e4a9SSolomon Peachy 1336a910e4a9SSolomon Peachy /* Perform actual join */ 1337a910e4a9SSolomon Peachy if (wsm_join(priv, &join)) { 1338a910e4a9SSolomon Peachy pr_err("[STA] cw1200_join_work: wsm_join failed!\n"); 1339a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->join_timeout); 1340a910e4a9SSolomon Peachy cw1200_update_listening(priv, priv->listening); 1341a910e4a9SSolomon Peachy /* Tx lock still held, unjoin will clear it. */ 1342a910e4a9SSolomon Peachy if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 1343a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1344a910e4a9SSolomon Peachy } else { 1345a910e4a9SSolomon Peachy if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND)) 1346a910e4a9SSolomon Peachy cw1200_join_complete(priv); /* Will clear tx_lock */ 1347a910e4a9SSolomon Peachy 1348a910e4a9SSolomon Peachy /* Upload keys */ 1349a910e4a9SSolomon Peachy cw1200_upload_keys(priv); 1350a910e4a9SSolomon Peachy 1351a910e4a9SSolomon Peachy /* Due to beacon filtering it is possible that the 1352a910e4a9SSolomon Peachy * AP's beacon is not known for the mac80211 stack. 1353a910e4a9SSolomon Peachy * Disable filtering temporary to make sure the stack 1354a910e4a9SSolomon Peachy * receives at least one 1355a910e4a9SSolomon Peachy */ 1356a910e4a9SSolomon Peachy priv->disable_beacon_filter = true; 1357a910e4a9SSolomon Peachy } 1358a910e4a9SSolomon Peachy cw1200_update_filtering(priv); 1359a910e4a9SSolomon Peachy 1360a910e4a9SSolomon Peachy done_put: 1361a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 1362a910e4a9SSolomon Peachy if (bss) 1363a910e4a9SSolomon Peachy cfg80211_put_bss(priv->hw->wiphy, bss); 1364a910e4a9SSolomon Peachy } 1365a910e4a9SSolomon Peachy 1366a910e4a9SSolomon Peachy void cw1200_join_timeout(struct work_struct *work) 1367a910e4a9SSolomon Peachy { 1368a910e4a9SSolomon Peachy struct cw1200_common *priv = 1369a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, join_timeout.work); 1370a910e4a9SSolomon Peachy pr_debug("[WSM] Join timed out.\n"); 1371a910e4a9SSolomon Peachy wsm_lock_tx(priv); 1372a910e4a9SSolomon Peachy if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 1373a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1374a910e4a9SSolomon Peachy } 1375a910e4a9SSolomon Peachy 1376a910e4a9SSolomon Peachy static void cw1200_do_unjoin(struct cw1200_common *priv) 1377a910e4a9SSolomon Peachy { 1378a910e4a9SSolomon Peachy struct wsm_reset reset = { 1379a910e4a9SSolomon Peachy .reset_statistics = true, 1380a910e4a9SSolomon Peachy }; 1381a910e4a9SSolomon Peachy 1382a910e4a9SSolomon Peachy cancel_delayed_work_sync(&priv->join_timeout); 1383a910e4a9SSolomon Peachy 1384a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 1385a910e4a9SSolomon Peachy priv->join_pending = false; 1386a910e4a9SSolomon Peachy 1387a910e4a9SSolomon Peachy if (atomic_read(&priv->scan.in_progress)) { 1388a910e4a9SSolomon Peachy if (priv->delayed_unjoin) 1389a910e4a9SSolomon Peachy wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n"); 1390a910e4a9SSolomon Peachy else 1391a910e4a9SSolomon Peachy priv->delayed_unjoin = true; 1392a910e4a9SSolomon Peachy goto done; 1393a910e4a9SSolomon Peachy } 1394a910e4a9SSolomon Peachy 1395a910e4a9SSolomon Peachy priv->delayed_link_loss = false; 1396a910e4a9SSolomon Peachy 1397a910e4a9SSolomon Peachy if (!priv->join_status) 1398a910e4a9SSolomon Peachy goto done; 1399a910e4a9SSolomon Peachy 14005a6e0cf7SSolomon Peachy if (priv->join_status == CW1200_JOIN_STATUS_AP) 14015a6e0cf7SSolomon Peachy goto done; 1402a910e4a9SSolomon Peachy 1403a910e4a9SSolomon Peachy cancel_work_sync(&priv->update_filtering_work); 1404a910e4a9SSolomon Peachy cancel_work_sync(&priv->set_beacon_wakeup_period_work); 1405a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 1406a910e4a9SSolomon Peachy 1407a910e4a9SSolomon Peachy /* Unjoin is a reset. */ 1408a910e4a9SSolomon Peachy wsm_flush_tx(priv); 1409a910e4a9SSolomon Peachy wsm_keep_alive_period(priv, 0); 1410a910e4a9SSolomon Peachy wsm_reset(priv, &reset); 1411a910e4a9SSolomon Peachy wsm_set_output_power(priv, priv->output_power * 10); 1412a910e4a9SSolomon Peachy priv->join_dtim_period = 0; 1413a910e4a9SSolomon Peachy cw1200_setup_mac(priv); 1414a910e4a9SSolomon Peachy cw1200_free_event_queue(priv); 1415a910e4a9SSolomon Peachy cancel_work_sync(&priv->event_handler); 1416a910e4a9SSolomon Peachy cw1200_update_listening(priv, priv->listening); 1417a910e4a9SSolomon Peachy cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 1418a910e4a9SSolomon Peachy 1419a910e4a9SSolomon Peachy /* Disable Block ACKs */ 1420a910e4a9SSolomon Peachy wsm_set_block_ack_policy(priv, 0, 0); 1421a910e4a9SSolomon Peachy 1422a910e4a9SSolomon Peachy priv->disable_beacon_filter = false; 1423a910e4a9SSolomon Peachy cw1200_update_filtering(priv); 1424a910e4a9SSolomon Peachy memset(&priv->association_mode, 0, 1425a910e4a9SSolomon Peachy sizeof(priv->association_mode)); 1426a910e4a9SSolomon Peachy memset(&priv->bss_params, 0, sizeof(priv->bss_params)); 1427a910e4a9SSolomon Peachy priv->setbssparams_done = false; 1428a910e4a9SSolomon Peachy memset(&priv->firmware_ps_mode, 0, 1429a910e4a9SSolomon Peachy sizeof(priv->firmware_ps_mode)); 1430a910e4a9SSolomon Peachy 1431a910e4a9SSolomon Peachy pr_debug("[STA] Unjoin completed.\n"); 1432a910e4a9SSolomon Peachy 1433a910e4a9SSolomon Peachy done: 1434a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 1435a910e4a9SSolomon Peachy } 1436a910e4a9SSolomon Peachy 1437a910e4a9SSolomon Peachy void cw1200_unjoin_work(struct work_struct *work) 1438a910e4a9SSolomon Peachy { 1439a910e4a9SSolomon Peachy struct cw1200_common *priv = 1440a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, unjoin_work); 1441a910e4a9SSolomon Peachy 1442a910e4a9SSolomon Peachy cw1200_do_unjoin(priv); 1443a910e4a9SSolomon Peachy 1444a910e4a9SSolomon Peachy /* Tell the stack we're dead */ 1445a910e4a9SSolomon Peachy ieee80211_connection_loss(priv->vif); 1446a910e4a9SSolomon Peachy 1447a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1448a910e4a9SSolomon Peachy } 1449a910e4a9SSolomon Peachy 1450a910e4a9SSolomon Peachy int cw1200_enable_listening(struct cw1200_common *priv) 1451a910e4a9SSolomon Peachy { 1452a910e4a9SSolomon Peachy struct wsm_start start = { 1453a910e4a9SSolomon Peachy .mode = WSM_START_MODE_P2P_DEV, 1454a910e4a9SSolomon Peachy .band = WSM_PHY_BAND_2_4G, 1455a910e4a9SSolomon Peachy .beacon_interval = 100, 1456a910e4a9SSolomon Peachy .dtim_period = 1, 1457a910e4a9SSolomon Peachy .probe_delay = 0, 1458a910e4a9SSolomon Peachy .basic_rate_set = 0x0F, 1459a910e4a9SSolomon Peachy }; 1460a910e4a9SSolomon Peachy 1461a910e4a9SSolomon Peachy if (priv->channel) { 146257fbcce3SJohannes Berg start.band = priv->channel->band == NL80211_BAND_5GHZ ? 1463a910e4a9SSolomon Peachy WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; 1464a910e4a9SSolomon Peachy start.channel_number = priv->channel->hw_value; 1465a910e4a9SSolomon Peachy } else { 1466a910e4a9SSolomon Peachy start.band = WSM_PHY_BAND_2_4G; 1467a910e4a9SSolomon Peachy start.channel_number = 1; 1468a910e4a9SSolomon Peachy } 1469a910e4a9SSolomon Peachy 1470a910e4a9SSolomon Peachy return wsm_start(priv, &start); 1471a910e4a9SSolomon Peachy } 1472a910e4a9SSolomon Peachy 1473a910e4a9SSolomon Peachy int cw1200_disable_listening(struct cw1200_common *priv) 1474a910e4a9SSolomon Peachy { 1475a910e4a9SSolomon Peachy int ret; 1476a910e4a9SSolomon Peachy struct wsm_reset reset = { 1477a910e4a9SSolomon Peachy .reset_statistics = true, 1478a910e4a9SSolomon Peachy }; 1479a910e4a9SSolomon Peachy ret = wsm_reset(priv, &reset); 1480a910e4a9SSolomon Peachy return ret; 1481a910e4a9SSolomon Peachy } 1482a910e4a9SSolomon Peachy 1483a910e4a9SSolomon Peachy void cw1200_update_listening(struct cw1200_common *priv, bool enabled) 1484a910e4a9SSolomon Peachy { 1485a910e4a9SSolomon Peachy if (enabled) { 1486a910e4a9SSolomon Peachy if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) { 1487a910e4a9SSolomon Peachy if (!cw1200_enable_listening(priv)) 1488a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_MONITOR; 1489a910e4a9SSolomon Peachy wsm_set_probe_responder(priv, true); 1490a910e4a9SSolomon Peachy } 1491a910e4a9SSolomon Peachy } else { 1492a910e4a9SSolomon Peachy if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { 1493a910e4a9SSolomon Peachy if (!cw1200_disable_listening(priv)) 1494a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 1495a910e4a9SSolomon Peachy wsm_set_probe_responder(priv, false); 1496a910e4a9SSolomon Peachy } 1497a910e4a9SSolomon Peachy } 1498a910e4a9SSolomon Peachy } 1499a910e4a9SSolomon Peachy 1500a910e4a9SSolomon Peachy int cw1200_set_uapsd_param(struct cw1200_common *priv, 1501a910e4a9SSolomon Peachy const struct wsm_edca_params *arg) 1502a910e4a9SSolomon Peachy { 1503a910e4a9SSolomon Peachy int ret; 1504a910e4a9SSolomon Peachy u16 uapsd_flags = 0; 1505a910e4a9SSolomon Peachy 1506a910e4a9SSolomon Peachy /* Here's the mapping AC [queue, bit] 1507a910e4a9SSolomon Peachy * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] 1508a910e4a9SSolomon Peachy */ 1509a910e4a9SSolomon Peachy 1510a910e4a9SSolomon Peachy if (arg->uapsd_enable[0]) 1511a910e4a9SSolomon Peachy uapsd_flags |= 1 << 3; 1512a910e4a9SSolomon Peachy 1513a910e4a9SSolomon Peachy if (arg->uapsd_enable[1]) 1514a910e4a9SSolomon Peachy uapsd_flags |= 1 << 2; 1515a910e4a9SSolomon Peachy 1516a910e4a9SSolomon Peachy if (arg->uapsd_enable[2]) 1517a910e4a9SSolomon Peachy uapsd_flags |= 1 << 1; 1518a910e4a9SSolomon Peachy 1519a910e4a9SSolomon Peachy if (arg->uapsd_enable[3]) 1520a910e4a9SSolomon Peachy uapsd_flags |= 1; 1521a910e4a9SSolomon Peachy 1522a910e4a9SSolomon Peachy /* Currently pseudo U-APSD operation is not supported, so setting 1523a910e4a9SSolomon Peachy * MinAutoTriggerInterval, MaxAutoTriggerInterval and 1524a910e4a9SSolomon Peachy * AutoTriggerStep to 0 1525a910e4a9SSolomon Peachy */ 1526a910e4a9SSolomon Peachy 1527a910e4a9SSolomon Peachy priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags); 1528a910e4a9SSolomon Peachy priv->uapsd_info.min_auto_trigger_interval = 0; 1529a910e4a9SSolomon Peachy priv->uapsd_info.max_auto_trigger_interval = 0; 1530a910e4a9SSolomon Peachy priv->uapsd_info.auto_trigger_step = 0; 1531a910e4a9SSolomon Peachy 1532a910e4a9SSolomon Peachy ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); 1533a910e4a9SSolomon Peachy return ret; 1534a910e4a9SSolomon Peachy } 1535a910e4a9SSolomon Peachy 1536a910e4a9SSolomon Peachy /* ******************************************************************** */ 1537a910e4a9SSolomon Peachy /* AP API */ 1538a910e4a9SSolomon Peachy 1539a910e4a9SSolomon Peachy int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 1540a910e4a9SSolomon Peachy struct ieee80211_sta *sta) 1541a910e4a9SSolomon Peachy { 1542a910e4a9SSolomon Peachy struct cw1200_common *priv = hw->priv; 1543a910e4a9SSolomon Peachy struct cw1200_sta_priv *sta_priv = 1544a910e4a9SSolomon Peachy (struct cw1200_sta_priv *)&sta->drv_priv; 1545a910e4a9SSolomon Peachy struct cw1200_link_entry *entry; 1546a910e4a9SSolomon Peachy struct sk_buff *skb; 1547a910e4a9SSolomon Peachy 1548a910e4a9SSolomon Peachy if (priv->mode != NL80211_IFTYPE_AP) 1549a910e4a9SSolomon Peachy return 0; 1550a910e4a9SSolomon Peachy 1551a910e4a9SSolomon Peachy sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); 1552a910e4a9SSolomon Peachy if (WARN_ON(!sta_priv->link_id)) { 1553a910e4a9SSolomon Peachy wiphy_info(priv->hw->wiphy, 1554a910e4a9SSolomon Peachy "[AP] No more link IDs available.\n"); 1555a910e4a9SSolomon Peachy return -ENOENT; 1556a910e4a9SSolomon Peachy } 1557a910e4a9SSolomon Peachy 1558a910e4a9SSolomon Peachy entry = &priv->link_id_db[sta_priv->link_id - 1]; 1559a910e4a9SSolomon Peachy spin_lock_bh(&priv->ps_state_lock); 1560a910e4a9SSolomon Peachy if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == 1561a910e4a9SSolomon Peachy IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) 1562a910e4a9SSolomon Peachy priv->sta_asleep_mask |= BIT(sta_priv->link_id); 1563a910e4a9SSolomon Peachy entry->status = CW1200_LINK_HARD; 1564a910e4a9SSolomon Peachy while ((skb = skb_dequeue(&entry->rx_queue))) 1565a910e4a9SSolomon Peachy ieee80211_rx_irqsafe(priv->hw, skb); 1566a910e4a9SSolomon Peachy spin_unlock_bh(&priv->ps_state_lock); 1567a910e4a9SSolomon Peachy return 0; 1568a910e4a9SSolomon Peachy } 1569a910e4a9SSolomon Peachy 1570a910e4a9SSolomon Peachy int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 1571a910e4a9SSolomon Peachy struct ieee80211_sta *sta) 1572a910e4a9SSolomon Peachy { 1573a910e4a9SSolomon Peachy struct cw1200_common *priv = hw->priv; 1574a910e4a9SSolomon Peachy struct cw1200_sta_priv *sta_priv = 1575a910e4a9SSolomon Peachy (struct cw1200_sta_priv *)&sta->drv_priv; 1576a910e4a9SSolomon Peachy struct cw1200_link_entry *entry; 1577a910e4a9SSolomon Peachy 1578a910e4a9SSolomon Peachy if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) 1579a910e4a9SSolomon Peachy return 0; 1580a910e4a9SSolomon Peachy 1581a910e4a9SSolomon Peachy entry = &priv->link_id_db[sta_priv->link_id - 1]; 1582a910e4a9SSolomon Peachy spin_lock_bh(&priv->ps_state_lock); 1583a910e4a9SSolomon Peachy entry->status = CW1200_LINK_RESERVE; 1584a910e4a9SSolomon Peachy entry->timestamp = jiffies; 1585a910e4a9SSolomon Peachy wsm_lock_tx_async(priv); 1586a910e4a9SSolomon Peachy if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) 1587a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1588a910e4a9SSolomon Peachy spin_unlock_bh(&priv->ps_state_lock); 1589a910e4a9SSolomon Peachy flush_workqueue(priv->workqueue); 1590a910e4a9SSolomon Peachy return 0; 1591a910e4a9SSolomon Peachy } 1592a910e4a9SSolomon Peachy 1593a910e4a9SSolomon Peachy static void __cw1200_sta_notify(struct ieee80211_hw *dev, 1594a910e4a9SSolomon Peachy struct ieee80211_vif *vif, 1595a910e4a9SSolomon Peachy enum sta_notify_cmd notify_cmd, 1596a910e4a9SSolomon Peachy int link_id) 1597a910e4a9SSolomon Peachy { 1598a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 1599a910e4a9SSolomon Peachy u32 bit, prev; 1600a910e4a9SSolomon Peachy 1601a910e4a9SSolomon Peachy /* Zero link id means "for all link IDs" */ 1602a910e4a9SSolomon Peachy if (link_id) 1603a910e4a9SSolomon Peachy bit = BIT(link_id); 1604a910e4a9SSolomon Peachy else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) 1605a910e4a9SSolomon Peachy bit = 0; 1606a910e4a9SSolomon Peachy else 1607a910e4a9SSolomon Peachy bit = priv->link_id_map; 1608a910e4a9SSolomon Peachy prev = priv->sta_asleep_mask & bit; 1609a910e4a9SSolomon Peachy 1610a910e4a9SSolomon Peachy switch (notify_cmd) { 1611a910e4a9SSolomon Peachy case STA_NOTIFY_SLEEP: 1612a910e4a9SSolomon Peachy if (!prev) { 1613a910e4a9SSolomon Peachy if (priv->buffered_multicasts && 1614a910e4a9SSolomon Peachy !priv->sta_asleep_mask) 1615a910e4a9SSolomon Peachy queue_work(priv->workqueue, 1616a910e4a9SSolomon Peachy &priv->multicast_start_work); 1617a910e4a9SSolomon Peachy priv->sta_asleep_mask |= bit; 1618a910e4a9SSolomon Peachy } 1619a910e4a9SSolomon Peachy break; 1620a910e4a9SSolomon Peachy case STA_NOTIFY_AWAKE: 1621a910e4a9SSolomon Peachy if (prev) { 1622a910e4a9SSolomon Peachy priv->sta_asleep_mask &= ~bit; 1623a910e4a9SSolomon Peachy priv->pspoll_mask &= ~bit; 1624a910e4a9SSolomon Peachy if (priv->tx_multicast && link_id && 1625a910e4a9SSolomon Peachy !priv->sta_asleep_mask) 1626a910e4a9SSolomon Peachy queue_work(priv->workqueue, 1627a910e4a9SSolomon Peachy &priv->multicast_stop_work); 1628a910e4a9SSolomon Peachy cw1200_bh_wakeup(priv); 1629a910e4a9SSolomon Peachy } 1630a910e4a9SSolomon Peachy break; 1631a910e4a9SSolomon Peachy } 1632a910e4a9SSolomon Peachy } 1633a910e4a9SSolomon Peachy 1634a910e4a9SSolomon Peachy void cw1200_sta_notify(struct ieee80211_hw *dev, 1635a910e4a9SSolomon Peachy struct ieee80211_vif *vif, 1636a910e4a9SSolomon Peachy enum sta_notify_cmd notify_cmd, 1637a910e4a9SSolomon Peachy struct ieee80211_sta *sta) 1638a910e4a9SSolomon Peachy { 1639a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 1640a910e4a9SSolomon Peachy struct cw1200_sta_priv *sta_priv = 1641a910e4a9SSolomon Peachy (struct cw1200_sta_priv *)&sta->drv_priv; 1642a910e4a9SSolomon Peachy 1643a910e4a9SSolomon Peachy spin_lock_bh(&priv->ps_state_lock); 1644a910e4a9SSolomon Peachy __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); 1645a910e4a9SSolomon Peachy spin_unlock_bh(&priv->ps_state_lock); 1646a910e4a9SSolomon Peachy } 1647a910e4a9SSolomon Peachy 1648a910e4a9SSolomon Peachy static void cw1200_ps_notify(struct cw1200_common *priv, 1649a910e4a9SSolomon Peachy int link_id, bool ps) 1650a910e4a9SSolomon Peachy { 1651a910e4a9SSolomon Peachy if (link_id > CW1200_MAX_STA_IN_AP_MODE) 1652a910e4a9SSolomon Peachy return; 1653a910e4a9SSolomon Peachy 1654a910e4a9SSolomon Peachy pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n", 1655a910e4a9SSolomon Peachy ps ? "Stop" : "Start", 1656a910e4a9SSolomon Peachy link_id, priv->sta_asleep_mask); 1657a910e4a9SSolomon Peachy 1658a910e4a9SSolomon Peachy __cw1200_sta_notify(priv->hw, priv->vif, 1659a910e4a9SSolomon Peachy ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); 1660a910e4a9SSolomon Peachy } 1661a910e4a9SSolomon Peachy 1662a910e4a9SSolomon Peachy static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) 1663a910e4a9SSolomon Peachy { 1664a910e4a9SSolomon Peachy struct sk_buff *skb; 1665a910e4a9SSolomon Peachy struct wsm_update_ie update_ie = { 1666a910e4a9SSolomon Peachy .what = WSM_UPDATE_IE_BEACON, 1667a910e4a9SSolomon Peachy .count = 1, 1668a910e4a9SSolomon Peachy }; 1669a910e4a9SSolomon Peachy u16 tim_offset, tim_length; 1670a910e4a9SSolomon Peachy 1671a910e4a9SSolomon Peachy pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis"); 1672a910e4a9SSolomon Peachy 1673a910e4a9SSolomon Peachy skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, 1674a910e4a9SSolomon Peachy &tim_offset, &tim_length); 1675a910e4a9SSolomon Peachy if (!skb) { 1676a910e4a9SSolomon Peachy if (!__cw1200_flush(priv, true)) 1677a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1678a910e4a9SSolomon Peachy return -ENOENT; 1679a910e4a9SSolomon Peachy } 1680a910e4a9SSolomon Peachy 1681a910e4a9SSolomon Peachy if (tim_offset && tim_length >= 6) { 1682a910e4a9SSolomon Peachy /* Ignore DTIM count from mac80211: 1683a910e4a9SSolomon Peachy * firmware handles DTIM internally. 1684a910e4a9SSolomon Peachy */ 1685a910e4a9SSolomon Peachy skb->data[tim_offset + 2] = 0; 1686a910e4a9SSolomon Peachy 1687a910e4a9SSolomon Peachy /* Set/reset aid0 bit */ 1688a910e4a9SSolomon Peachy if (aid0_bit_set) 1689a910e4a9SSolomon Peachy skb->data[tim_offset + 4] |= 1; 1690a910e4a9SSolomon Peachy else 1691a910e4a9SSolomon Peachy skb->data[tim_offset + 4] &= ~1; 1692a910e4a9SSolomon Peachy } 1693a910e4a9SSolomon Peachy 1694a910e4a9SSolomon Peachy update_ie.ies = &skb->data[tim_offset]; 1695a910e4a9SSolomon Peachy update_ie.length = tim_length; 1696a910e4a9SSolomon Peachy wsm_update_ie(priv, &update_ie); 1697a910e4a9SSolomon Peachy 1698a910e4a9SSolomon Peachy dev_kfree_skb(skb); 1699a910e4a9SSolomon Peachy 1700a910e4a9SSolomon Peachy return 0; 1701a910e4a9SSolomon Peachy } 1702a910e4a9SSolomon Peachy 1703a910e4a9SSolomon Peachy void cw1200_set_tim_work(struct work_struct *work) 1704a910e4a9SSolomon Peachy { 1705a910e4a9SSolomon Peachy struct cw1200_common *priv = 1706a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, set_tim_work); 1707a910e4a9SSolomon Peachy (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); 1708a910e4a9SSolomon Peachy } 1709a910e4a9SSolomon Peachy 1710a910e4a9SSolomon Peachy int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, 1711a910e4a9SSolomon Peachy bool set) 1712a910e4a9SSolomon Peachy { 1713a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 1714a910e4a9SSolomon Peachy queue_work(priv->workqueue, &priv->set_tim_work); 1715a910e4a9SSolomon Peachy return 0; 1716a910e4a9SSolomon Peachy } 1717a910e4a9SSolomon Peachy 1718a910e4a9SSolomon Peachy void cw1200_set_cts_work(struct work_struct *work) 1719a910e4a9SSolomon Peachy { 1720a910e4a9SSolomon Peachy struct cw1200_common *priv = 1721a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, set_cts_work); 1722a910e4a9SSolomon Peachy 1723a910e4a9SSolomon Peachy u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; 1724a910e4a9SSolomon Peachy struct wsm_update_ie update_ie = { 1725a910e4a9SSolomon Peachy .what = WSM_UPDATE_IE_BEACON, 1726a910e4a9SSolomon Peachy .count = 1, 1727a910e4a9SSolomon Peachy .ies = erp_ie, 1728a910e4a9SSolomon Peachy .length = 3, 1729a910e4a9SSolomon Peachy }; 1730a910e4a9SSolomon Peachy u32 erp_info; 1731a910e4a9SSolomon Peachy __le32 use_cts_prot; 1732a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 1733a910e4a9SSolomon Peachy erp_info = priv->erp_info; 1734a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 1735a910e4a9SSolomon Peachy use_cts_prot = 1736a910e4a9SSolomon Peachy erp_info & WLAN_ERP_USE_PROTECTION ? 1737a910e4a9SSolomon Peachy __cpu_to_le32(1) : 0; 1738a910e4a9SSolomon Peachy 1739a910e4a9SSolomon Peachy erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; 1740a910e4a9SSolomon Peachy 1741a910e4a9SSolomon Peachy pr_debug("[STA] ERP information 0x%x\n", erp_info); 1742a910e4a9SSolomon Peachy 1743a910e4a9SSolomon Peachy wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, 1744a910e4a9SSolomon Peachy &use_cts_prot, sizeof(use_cts_prot)); 1745a910e4a9SSolomon Peachy wsm_update_ie(priv, &update_ie); 1746a910e4a9SSolomon Peachy 1747a910e4a9SSolomon Peachy return; 1748a910e4a9SSolomon Peachy } 1749a910e4a9SSolomon Peachy 1750a910e4a9SSolomon Peachy static int cw1200_set_btcoexinfo(struct cw1200_common *priv) 1751a910e4a9SSolomon Peachy { 1752a910e4a9SSolomon Peachy struct wsm_override_internal_txrate arg; 1753a910e4a9SSolomon Peachy int ret = 0; 1754a910e4a9SSolomon Peachy 1755a910e4a9SSolomon Peachy if (priv->mode == NL80211_IFTYPE_STATION) { 1756a910e4a9SSolomon Peachy /* Plumb PSPOLL and NULL template */ 1757a910e4a9SSolomon Peachy cw1200_upload_pspoll(priv); 1758a910e4a9SSolomon Peachy cw1200_upload_null(priv); 1759a910e4a9SSolomon Peachy cw1200_upload_qosnull(priv); 1760a910e4a9SSolomon Peachy } else { 1761a910e4a9SSolomon Peachy return 0; 1762a910e4a9SSolomon Peachy } 1763a910e4a9SSolomon Peachy 1764a910e4a9SSolomon Peachy memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); 1765a910e4a9SSolomon Peachy 1766a910e4a9SSolomon Peachy if (!priv->vif->p2p) { 1767a910e4a9SSolomon Peachy /* STATION mode */ 1768a910e4a9SSolomon Peachy if (priv->bss_params.operational_rate_set & ~0xF) { 1769a910e4a9SSolomon Peachy pr_debug("[STA] STA has ERP rates\n"); 1770a910e4a9SSolomon Peachy /* G or BG mode */ 1771a910e4a9SSolomon Peachy arg.internalTxRate = (__ffs( 1772a910e4a9SSolomon Peachy priv->bss_params.operational_rate_set & ~0xF)); 1773a910e4a9SSolomon Peachy } else { 1774a910e4a9SSolomon Peachy pr_debug("[STA] STA has non ERP rates\n"); 1775a910e4a9SSolomon Peachy /* B only mode */ 17767258416cSSolomon Peachy arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); 1777a910e4a9SSolomon Peachy } 17787258416cSSolomon Peachy arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); 1779a910e4a9SSolomon Peachy } else { 1780a910e4a9SSolomon Peachy /* P2P mode */ 1781a910e4a9SSolomon Peachy arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); 1782a910e4a9SSolomon Peachy arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); 1783a910e4a9SSolomon Peachy } 1784a910e4a9SSolomon Peachy 1785a910e4a9SSolomon Peachy pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", 1786a910e4a9SSolomon Peachy priv->mode, 1787a910e4a9SSolomon Peachy arg.internalTxRate, 1788a910e4a9SSolomon Peachy arg.nonErpInternalTxRate); 1789a910e4a9SSolomon Peachy 1790a910e4a9SSolomon Peachy ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, 1791a910e4a9SSolomon Peachy &arg, sizeof(arg)); 1792a910e4a9SSolomon Peachy 1793a910e4a9SSolomon Peachy return ret; 1794a910e4a9SSolomon Peachy } 1795a910e4a9SSolomon Peachy 1796a910e4a9SSolomon Peachy void cw1200_bss_info_changed(struct ieee80211_hw *dev, 1797a910e4a9SSolomon Peachy struct ieee80211_vif *vif, 1798a910e4a9SSolomon Peachy struct ieee80211_bss_conf *info, 1799a910e4a9SSolomon Peachy u32 changed) 1800a910e4a9SSolomon Peachy { 1801a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 1802a910e4a9SSolomon Peachy bool do_join = false; 1803a910e4a9SSolomon Peachy 1804a910e4a9SSolomon Peachy mutex_lock(&priv->conf_mutex); 1805a910e4a9SSolomon Peachy 1806a910e4a9SSolomon Peachy pr_debug("BSS CHANGED: %08x\n", changed); 1807a910e4a9SSolomon Peachy 1808a910e4a9SSolomon Peachy /* TODO: BSS_CHANGED_QOS */ 1809a910e4a9SSolomon Peachy /* TODO: BSS_CHANGED_TXPOWER */ 1810a910e4a9SSolomon Peachy 1811a910e4a9SSolomon Peachy if (changed & BSS_CHANGED_ARP_FILTER) { 1812a910e4a9SSolomon Peachy struct wsm_mib_arp_ipv4_filter filter = {0}; 1813a910e4a9SSolomon Peachy int i; 1814a910e4a9SSolomon Peachy 1815a910e4a9SSolomon Peachy pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", 1816a910e4a9SSolomon Peachy info->arp_addr_cnt); 1817a910e4a9SSolomon Peachy 1818a910e4a9SSolomon Peachy /* Currently only one IP address is supported by firmware. 1819a910e4a9SSolomon Peachy * In case of more IPs arp filtering will be disabled. 1820a910e4a9SSolomon Peachy */ 1821a910e4a9SSolomon Peachy if (info->arp_addr_cnt > 0 && 1822a910e4a9SSolomon Peachy info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { 1823a910e4a9SSolomon Peachy for (i = 0; i < info->arp_addr_cnt; i++) { 1824a910e4a9SSolomon Peachy filter.ipv4addrs[i] = info->arp_addr_list[i]; 1825a910e4a9SSolomon Peachy pr_debug("[STA] addr[%d]: 0x%X\n", 1826a910e4a9SSolomon Peachy i, filter.ipv4addrs[i]); 1827a910e4a9SSolomon Peachy } 1828a910e4a9SSolomon Peachy filter.enable = __cpu_to_le32(1); 1829a910e4a9SSolomon Peachy } 1830a910e4a9SSolomon Peachy 1831a910e4a9SSolomon Peachy pr_debug("[STA] arp ip filter enable: %d\n", 1832a910e4a9SSolomon Peachy __le32_to_cpu(filter.enable)); 1833a910e4a9SSolomon Peachy 1834a910e4a9SSolomon Peachy wsm_set_arp_ipv4_filter(priv, &filter); 1835a910e4a9SSolomon Peachy } 1836a910e4a9SSolomon Peachy 1837a910e4a9SSolomon Peachy if (changed & 1838a910e4a9SSolomon Peachy (BSS_CHANGED_BEACON | 1839a910e4a9SSolomon Peachy BSS_CHANGED_AP_PROBE_RESP | 1840a910e4a9SSolomon Peachy BSS_CHANGED_BSSID | 1841a910e4a9SSolomon Peachy BSS_CHANGED_SSID | 1842a910e4a9SSolomon Peachy BSS_CHANGED_IBSS)) { 1843a910e4a9SSolomon Peachy pr_debug("BSS_CHANGED_BEACON\n"); 1844a910e4a9SSolomon Peachy priv->beacon_int = info->beacon_int; 1845a910e4a9SSolomon Peachy cw1200_update_beaconing(priv); 1846a910e4a9SSolomon Peachy cw1200_upload_beacon(priv); 1847a910e4a9SSolomon Peachy } 1848a910e4a9SSolomon Peachy 1849a910e4a9SSolomon Peachy if (changed & BSS_CHANGED_BEACON_ENABLED) { 1850a910e4a9SSolomon Peachy pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon); 1851a910e4a9SSolomon Peachy 1852a910e4a9SSolomon Peachy if (priv->enable_beacon != info->enable_beacon) { 1853a910e4a9SSolomon Peachy cw1200_enable_beaconing(priv, info->enable_beacon); 1854a910e4a9SSolomon Peachy priv->enable_beacon = info->enable_beacon; 1855a910e4a9SSolomon Peachy } 1856a910e4a9SSolomon Peachy } 1857a910e4a9SSolomon Peachy 1858a910e4a9SSolomon Peachy if (changed & BSS_CHANGED_BEACON_INT) { 1859a910e4a9SSolomon Peachy pr_debug("CHANGED_BEACON_INT\n"); 1860a910e4a9SSolomon Peachy if (info->ibss_joined) 1861a910e4a9SSolomon Peachy do_join = true; 1862a910e4a9SSolomon Peachy else if (priv->join_status == CW1200_JOIN_STATUS_AP) 1863a910e4a9SSolomon Peachy cw1200_update_beaconing(priv); 1864a910e4a9SSolomon Peachy } 1865a910e4a9SSolomon Peachy 1866a910e4a9SSolomon Peachy /* assoc/disassoc, or maybe AID changed */ 1867a910e4a9SSolomon Peachy if (changed & BSS_CHANGED_ASSOC) { 1868a910e4a9SSolomon Peachy wsm_lock_tx(priv); 1869a910e4a9SSolomon Peachy priv->wep_default_key_id = -1; 1870a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 1871a910e4a9SSolomon Peachy } 1872a910e4a9SSolomon Peachy 1873a910e4a9SSolomon Peachy if (changed & BSS_CHANGED_BSSID) { 1874a910e4a9SSolomon Peachy pr_debug("BSS_CHANGED_BSSID\n"); 1875a910e4a9SSolomon Peachy do_join = true; 1876a910e4a9SSolomon Peachy } 1877a910e4a9SSolomon Peachy 1878a910e4a9SSolomon Peachy if (changed & 1879a910e4a9SSolomon Peachy (BSS_CHANGED_ASSOC | 1880a910e4a9SSolomon Peachy BSS_CHANGED_BSSID | 1881a910e4a9SSolomon Peachy BSS_CHANGED_IBSS | 1882a910e4a9SSolomon Peachy BSS_CHANGED_BASIC_RATES | 1883a910e4a9SSolomon Peachy BSS_CHANGED_HT)) { 1884a910e4a9SSolomon Peachy pr_debug("BSS_CHANGED_ASSOC\n"); 1885a910e4a9SSolomon Peachy if (info->assoc) { 1886a910e4a9SSolomon Peachy if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) { 1887a910e4a9SSolomon Peachy ieee80211_connection_loss(vif); 1888a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 1889a910e4a9SSolomon Peachy return; 1890a910e4a9SSolomon Peachy } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) { 1891a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_STA; 1892a910e4a9SSolomon Peachy } 1893a910e4a9SSolomon Peachy } else { 1894a910e4a9SSolomon Peachy do_join = true; 1895a910e4a9SSolomon Peachy } 1896a910e4a9SSolomon Peachy 1897a910e4a9SSolomon Peachy if (info->assoc || info->ibss_joined) { 1898a910e4a9SSolomon Peachy struct ieee80211_sta *sta = NULL; 18997258416cSSolomon Peachy __le32 htprot = 0; 1900a910e4a9SSolomon Peachy 1901a910e4a9SSolomon Peachy if (info->dtim_period) 1902a910e4a9SSolomon Peachy priv->join_dtim_period = info->dtim_period; 1903a910e4a9SSolomon Peachy priv->beacon_int = info->beacon_int; 1904a910e4a9SSolomon Peachy 1905a910e4a9SSolomon Peachy rcu_read_lock(); 1906a910e4a9SSolomon Peachy 1907a910e4a9SSolomon Peachy if (info->bssid && !info->ibss_joined) 1908a910e4a9SSolomon Peachy sta = ieee80211_find_sta(vif, info->bssid); 1909a910e4a9SSolomon Peachy if (sta) { 1910*046d2e7cSSriram R priv->ht_info.ht_cap = sta->deflink.ht_cap; 1911a910e4a9SSolomon Peachy priv->bss_params.operational_rate_set = 1912a910e4a9SSolomon Peachy cw1200_rate_mask_to_wsm(priv, 1913*046d2e7cSSriram R sta->deflink.supp_rates[priv->channel->band]); 1914a910e4a9SSolomon Peachy priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef); 1915a910e4a9SSolomon Peachy priv->ht_info.operation_mode = info->ht_operation_mode; 1916a910e4a9SSolomon Peachy } else { 1917a910e4a9SSolomon Peachy memset(&priv->ht_info, 0, 1918a910e4a9SSolomon Peachy sizeof(priv->ht_info)); 1919a910e4a9SSolomon Peachy priv->bss_params.operational_rate_set = -1; 1920a910e4a9SSolomon Peachy } 1921a910e4a9SSolomon Peachy rcu_read_unlock(); 1922a910e4a9SSolomon Peachy 1923a910e4a9SSolomon Peachy /* Non Greenfield stations present */ 1924a910e4a9SSolomon Peachy if (priv->ht_info.operation_mode & 1925a910e4a9SSolomon Peachy IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) 19267258416cSSolomon Peachy htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT); 1927a910e4a9SSolomon Peachy 1928a910e4a9SSolomon Peachy /* Set HT protection method */ 19297258416cSSolomon Peachy htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2); 1930a910e4a9SSolomon Peachy 1931a910e4a9SSolomon Peachy /* TODO: 1932a910e4a9SSolomon Peachy * STBC_param.dual_cts 1933a910e4a9SSolomon Peachy * STBC_param.LSIG_TXOP_FILL 1934a910e4a9SSolomon Peachy */ 1935a910e4a9SSolomon Peachy 1936a910e4a9SSolomon Peachy wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION, 19377258416cSSolomon Peachy &htprot, sizeof(htprot)); 1938a910e4a9SSolomon Peachy 1939a910e4a9SSolomon Peachy priv->association_mode.greenfield = 1940a910e4a9SSolomon Peachy cw1200_ht_greenfield(&priv->ht_info); 1941a910e4a9SSolomon Peachy priv->association_mode.flags = 1942a910e4a9SSolomon Peachy WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | 1943a910e4a9SSolomon Peachy WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | 1944a910e4a9SSolomon Peachy WSM_ASSOCIATION_MODE_USE_HT_MODE | 1945a910e4a9SSolomon Peachy WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | 1946a910e4a9SSolomon Peachy WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; 1947a910e4a9SSolomon Peachy priv->association_mode.preamble = 1948a910e4a9SSolomon Peachy info->use_short_preamble ? 1949a910e4a9SSolomon Peachy WSM_JOIN_PREAMBLE_SHORT : 1950a910e4a9SSolomon Peachy WSM_JOIN_PREAMBLE_LONG; 1951a910e4a9SSolomon Peachy priv->association_mode.basic_rate_set = __cpu_to_le32( 1952a910e4a9SSolomon Peachy cw1200_rate_mask_to_wsm(priv, 1953a910e4a9SSolomon Peachy info->basic_rates)); 1954a910e4a9SSolomon Peachy priv->association_mode.mpdu_start_spacing = 1955a910e4a9SSolomon Peachy cw1200_ht_ampdu_density(&priv->ht_info); 1956a910e4a9SSolomon Peachy 1957a910e4a9SSolomon Peachy cw1200_cqm_bssloss_sm(priv, 0, 0, 0); 1958a910e4a9SSolomon Peachy cancel_work_sync(&priv->unjoin_work); 1959a910e4a9SSolomon Peachy 1960a910e4a9SSolomon Peachy priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count; 1961a910e4a9SSolomon Peachy priv->bss_params.aid = info->aid; 1962a910e4a9SSolomon Peachy 1963a910e4a9SSolomon Peachy if (priv->join_dtim_period < 1) 1964a910e4a9SSolomon Peachy priv->join_dtim_period = 1; 1965a910e4a9SSolomon Peachy 1966a910e4a9SSolomon Peachy pr_debug("[STA] DTIM %d, interval: %d\n", 1967a910e4a9SSolomon Peachy priv->join_dtim_period, priv->beacon_int); 1968a910e4a9SSolomon Peachy pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", 1969a910e4a9SSolomon Peachy priv->association_mode.preamble, 1970a910e4a9SSolomon Peachy priv->association_mode.greenfield, 1971a910e4a9SSolomon Peachy priv->bss_params.aid, 1972a910e4a9SSolomon Peachy priv->bss_params.operational_rate_set, 1973a910e4a9SSolomon Peachy priv->association_mode.basic_rate_set); 1974a910e4a9SSolomon Peachy wsm_set_association_mode(priv, &priv->association_mode); 1975a910e4a9SSolomon Peachy 1976a910e4a9SSolomon Peachy if (!info->ibss_joined) { 1977a910e4a9SSolomon Peachy wsm_keep_alive_period(priv, 30 /* sec */); 1978a910e4a9SSolomon Peachy wsm_set_bss_params(priv, &priv->bss_params); 1979a910e4a9SSolomon Peachy priv->setbssparams_done = true; 1980a910e4a9SSolomon Peachy cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work); 1981a910e4a9SSolomon Peachy cw1200_set_pm(priv, &priv->powersave_mode); 1982a910e4a9SSolomon Peachy } 1983a910e4a9SSolomon Peachy if (priv->vif->p2p) { 1984a910e4a9SSolomon Peachy pr_debug("[STA] Setting p2p powersave configuration.\n"); 1985a910e4a9SSolomon Peachy wsm_set_p2p_ps_modeinfo(priv, 1986a910e4a9SSolomon Peachy &priv->p2p_ps_modeinfo); 1987a910e4a9SSolomon Peachy } 1988a910e4a9SSolomon Peachy if (priv->bt_present) 1989a910e4a9SSolomon Peachy cw1200_set_btcoexinfo(priv); 1990a910e4a9SSolomon Peachy } else { 1991a910e4a9SSolomon Peachy memset(&priv->association_mode, 0, 1992a910e4a9SSolomon Peachy sizeof(priv->association_mode)); 1993a910e4a9SSolomon Peachy memset(&priv->bss_params, 0, sizeof(priv->bss_params)); 1994a910e4a9SSolomon Peachy } 1995a910e4a9SSolomon Peachy } 1996a910e4a9SSolomon Peachy 1997a910e4a9SSolomon Peachy /* ERP Protection */ 1998a910e4a9SSolomon Peachy if (changed & (BSS_CHANGED_ASSOC | 1999a910e4a9SSolomon Peachy BSS_CHANGED_ERP_CTS_PROT | 2000a910e4a9SSolomon Peachy BSS_CHANGED_ERP_PREAMBLE)) { 2001a910e4a9SSolomon Peachy u32 prev_erp_info = priv->erp_info; 2002a910e4a9SSolomon Peachy if (info->use_cts_prot) 2003a910e4a9SSolomon Peachy priv->erp_info |= WLAN_ERP_USE_PROTECTION; 2004a910e4a9SSolomon Peachy else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) 2005a910e4a9SSolomon Peachy priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; 2006a910e4a9SSolomon Peachy 2007a910e4a9SSolomon Peachy if (info->use_short_preamble) 2008a910e4a9SSolomon Peachy priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; 2009a910e4a9SSolomon Peachy else 2010a910e4a9SSolomon Peachy priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; 2011a910e4a9SSolomon Peachy 2012a910e4a9SSolomon Peachy pr_debug("[STA] ERP Protection: %x\n", priv->erp_info); 2013a910e4a9SSolomon Peachy 2014a910e4a9SSolomon Peachy if (prev_erp_info != priv->erp_info) 2015a910e4a9SSolomon Peachy queue_work(priv->workqueue, &priv->set_cts_work); 2016a910e4a9SSolomon Peachy } 2017a910e4a9SSolomon Peachy 2018a910e4a9SSolomon Peachy /* ERP Slottime */ 2019a910e4a9SSolomon Peachy if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { 2020a910e4a9SSolomon Peachy __le32 slot_time = info->use_short_slot ? 2021a910e4a9SSolomon Peachy __cpu_to_le32(9) : __cpu_to_le32(20); 2022a910e4a9SSolomon Peachy pr_debug("[STA] Slot time: %d us.\n", 2023a910e4a9SSolomon Peachy __le32_to_cpu(slot_time)); 2024a910e4a9SSolomon Peachy wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, 2025a910e4a9SSolomon Peachy &slot_time, sizeof(slot_time)); 2026a910e4a9SSolomon Peachy } 2027a910e4a9SSolomon Peachy 2028a910e4a9SSolomon Peachy if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { 2029a910e4a9SSolomon Peachy struct wsm_rcpi_rssi_threshold threshold = { 2030a910e4a9SSolomon Peachy .rollingAverageCount = 8, 2031a910e4a9SSolomon Peachy }; 2032a910e4a9SSolomon Peachy pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n", 2033a910e4a9SSolomon Peachy info->cqm_rssi_thold, info->cqm_rssi_hyst); 2034a910e4a9SSolomon Peachy priv->cqm_rssi_thold = info->cqm_rssi_thold; 2035a910e4a9SSolomon Peachy priv->cqm_rssi_hyst = info->cqm_rssi_hyst; 2036a910e4a9SSolomon Peachy 2037a910e4a9SSolomon Peachy if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { 2038a910e4a9SSolomon Peachy /* RSSI subscription enabled */ 2039a910e4a9SSolomon Peachy /* TODO: It's not a correct way of setting threshold. 2040a910e4a9SSolomon Peachy * Upper and lower must be set equal here and adjusted 2041a910e4a9SSolomon Peachy * in callback. However current implementation is much 2042a910e4a9SSolomon Peachy * more relaible and stable. 2043a910e4a9SSolomon Peachy */ 2044a910e4a9SSolomon Peachy 2045a910e4a9SSolomon Peachy /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 2046a910e4a9SSolomon Peachy * RSSI = RCPI / 2 - 110 2047a910e4a9SSolomon Peachy */ 2048a910e4a9SSolomon Peachy if (priv->cqm_use_rssi) { 2049a910e4a9SSolomon Peachy threshold.upperThreshold = 2050a910e4a9SSolomon Peachy info->cqm_rssi_thold + info->cqm_rssi_hyst; 2051a910e4a9SSolomon Peachy threshold.lowerThreshold = 2052a910e4a9SSolomon Peachy info->cqm_rssi_thold; 2053a910e4a9SSolomon Peachy threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; 2054a910e4a9SSolomon Peachy } else { 2055a910e4a9SSolomon Peachy threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2; 2056a910e4a9SSolomon Peachy threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2; 2057a910e4a9SSolomon Peachy } 2058a910e4a9SSolomon Peachy threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; 2059a910e4a9SSolomon Peachy } else { 2060a910e4a9SSolomon Peachy /* There is a bug in FW, see sta.c. We have to enable 2061a910e4a9SSolomon Peachy * dummy subscription to get correct RSSI values. 2062a910e4a9SSolomon Peachy */ 2063a910e4a9SSolomon Peachy threshold.rssiRcpiMode |= 2064a910e4a9SSolomon Peachy WSM_RCPI_RSSI_THRESHOLD_ENABLE | 2065a910e4a9SSolomon Peachy WSM_RCPI_RSSI_DONT_USE_UPPER | 2066a910e4a9SSolomon Peachy WSM_RCPI_RSSI_DONT_USE_LOWER; 2067a910e4a9SSolomon Peachy if (priv->cqm_use_rssi) 2068a910e4a9SSolomon Peachy threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; 2069a910e4a9SSolomon Peachy } 2070a910e4a9SSolomon Peachy wsm_set_rcpi_rssi_threshold(priv, &threshold); 2071a910e4a9SSolomon Peachy } 2072a910e4a9SSolomon Peachy mutex_unlock(&priv->conf_mutex); 2073a910e4a9SSolomon Peachy 2074a910e4a9SSolomon Peachy if (do_join) { 2075a910e4a9SSolomon Peachy wsm_lock_tx(priv); 2076a910e4a9SSolomon Peachy cw1200_do_join(priv); /* Will unlock it for us */ 2077a910e4a9SSolomon Peachy } 2078a910e4a9SSolomon Peachy } 2079a910e4a9SSolomon Peachy 2080a910e4a9SSolomon Peachy void cw1200_multicast_start_work(struct work_struct *work) 2081a910e4a9SSolomon Peachy { 2082a910e4a9SSolomon Peachy struct cw1200_common *priv = 2083a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, multicast_start_work); 2084a910e4a9SSolomon Peachy long tmo = priv->join_dtim_period * 2085a910e4a9SSolomon Peachy (priv->beacon_int + 20) * HZ / 1024; 2086a910e4a9SSolomon Peachy 2087a910e4a9SSolomon Peachy cancel_work_sync(&priv->multicast_stop_work); 2088a910e4a9SSolomon Peachy 2089a910e4a9SSolomon Peachy if (!priv->aid0_bit_set) { 2090a910e4a9SSolomon Peachy wsm_lock_tx(priv); 2091a910e4a9SSolomon Peachy cw1200_set_tim_impl(priv, true); 2092a910e4a9SSolomon Peachy priv->aid0_bit_set = true; 2093a910e4a9SSolomon Peachy mod_timer(&priv->mcast_timeout, jiffies + tmo); 2094a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 2095a910e4a9SSolomon Peachy } 2096a910e4a9SSolomon Peachy } 2097a910e4a9SSolomon Peachy 2098a910e4a9SSolomon Peachy void cw1200_multicast_stop_work(struct work_struct *work) 2099a910e4a9SSolomon Peachy { 2100a910e4a9SSolomon Peachy struct cw1200_common *priv = 2101a910e4a9SSolomon Peachy container_of(work, struct cw1200_common, multicast_stop_work); 2102a910e4a9SSolomon Peachy 2103a910e4a9SSolomon Peachy if (priv->aid0_bit_set) { 2104a910e4a9SSolomon Peachy del_timer_sync(&priv->mcast_timeout); 2105a910e4a9SSolomon Peachy wsm_lock_tx(priv); 2106a910e4a9SSolomon Peachy priv->aid0_bit_set = false; 2107a910e4a9SSolomon Peachy cw1200_set_tim_impl(priv, false); 2108a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 2109a910e4a9SSolomon Peachy } 2110a910e4a9SSolomon Peachy } 2111a910e4a9SSolomon Peachy 2112e3dcf8bbSKees Cook void cw1200_mcast_timeout(struct timer_list *t) 2113a910e4a9SSolomon Peachy { 2114e3dcf8bbSKees Cook struct cw1200_common *priv = from_timer(priv, t, mcast_timeout); 2115a910e4a9SSolomon Peachy 2116a910e4a9SSolomon Peachy wiphy_warn(priv->hw->wiphy, 2117a910e4a9SSolomon Peachy "Multicast delivery timeout.\n"); 2118a910e4a9SSolomon Peachy spin_lock_bh(&priv->ps_state_lock); 2119a910e4a9SSolomon Peachy priv->tx_multicast = priv->aid0_bit_set && 2120a910e4a9SSolomon Peachy priv->buffered_multicasts; 2121a910e4a9SSolomon Peachy if (priv->tx_multicast) 2122a910e4a9SSolomon Peachy cw1200_bh_wakeup(priv); 2123a910e4a9SSolomon Peachy spin_unlock_bh(&priv->ps_state_lock); 2124a910e4a9SSolomon Peachy } 2125a910e4a9SSolomon Peachy 2126a910e4a9SSolomon Peachy int cw1200_ampdu_action(struct ieee80211_hw *hw, 2127a910e4a9SSolomon Peachy struct ieee80211_vif *vif, 212850ea05efSSara Sharon struct ieee80211_ampdu_params *params) 2129a910e4a9SSolomon Peachy { 2130a910e4a9SSolomon Peachy /* Aggregation is implemented fully in firmware, 2131a910e4a9SSolomon Peachy * including block ack negotiation. Do not allow 2132a910e4a9SSolomon Peachy * mac80211 stack to do anything: it interferes with 2133a910e4a9SSolomon Peachy * the firmware. 2134a910e4a9SSolomon Peachy */ 2135a910e4a9SSolomon Peachy 2136a910e4a9SSolomon Peachy /* Note that we still need this function stubbed. */ 2137a910e4a9SSolomon Peachy return -ENOTSUPP; 2138a910e4a9SSolomon Peachy } 2139a910e4a9SSolomon Peachy 2140a910e4a9SSolomon Peachy /* ******************************************************************** */ 2141a910e4a9SSolomon Peachy /* WSM callback */ 2142a910e4a9SSolomon Peachy void cw1200_suspend_resume(struct cw1200_common *priv, 2143a910e4a9SSolomon Peachy struct wsm_suspend_resume *arg) 2144a910e4a9SSolomon Peachy { 2145a910e4a9SSolomon Peachy pr_debug("[AP] %s: %s\n", 2146a910e4a9SSolomon Peachy arg->stop ? "stop" : "start", 2147a910e4a9SSolomon Peachy arg->multicast ? "broadcast" : "unicast"); 2148a910e4a9SSolomon Peachy 2149a910e4a9SSolomon Peachy if (arg->multicast) { 2150a910e4a9SSolomon Peachy bool cancel_tmo = false; 2151a910e4a9SSolomon Peachy spin_lock_bh(&priv->ps_state_lock); 2152a910e4a9SSolomon Peachy if (arg->stop) { 2153a910e4a9SSolomon Peachy priv->tx_multicast = false; 2154a910e4a9SSolomon Peachy } else { 2155a910e4a9SSolomon Peachy /* Firmware sends this indication every DTIM if there 2156a910e4a9SSolomon Peachy * is a STA in powersave connected. There is no reason 2157a910e4a9SSolomon Peachy * to suspend, following wakeup will consume much more 2158a910e4a9SSolomon Peachy * power than it could be saved. 2159a910e4a9SSolomon Peachy */ 2160a910e4a9SSolomon Peachy cw1200_pm_stay_awake(&priv->pm_state, 2161a910e4a9SSolomon Peachy priv->join_dtim_period * 2162a910e4a9SSolomon Peachy (priv->beacon_int + 20) * HZ / 1024); 2163a910e4a9SSolomon Peachy priv->tx_multicast = (priv->aid0_bit_set && 2164a910e4a9SSolomon Peachy priv->buffered_multicasts); 2165a910e4a9SSolomon Peachy if (priv->tx_multicast) { 2166a910e4a9SSolomon Peachy cancel_tmo = true; 2167a910e4a9SSolomon Peachy cw1200_bh_wakeup(priv); 2168a910e4a9SSolomon Peachy } 2169a910e4a9SSolomon Peachy } 2170a910e4a9SSolomon Peachy spin_unlock_bh(&priv->ps_state_lock); 2171a910e4a9SSolomon Peachy if (cancel_tmo) 2172a910e4a9SSolomon Peachy del_timer_sync(&priv->mcast_timeout); 2173a910e4a9SSolomon Peachy } else { 2174a910e4a9SSolomon Peachy spin_lock_bh(&priv->ps_state_lock); 2175a910e4a9SSolomon Peachy cw1200_ps_notify(priv, arg->link_id, arg->stop); 2176a910e4a9SSolomon Peachy spin_unlock_bh(&priv->ps_state_lock); 2177a910e4a9SSolomon Peachy if (!arg->stop) 2178a910e4a9SSolomon Peachy cw1200_bh_wakeup(priv); 2179a910e4a9SSolomon Peachy } 2180a910e4a9SSolomon Peachy return; 2181a910e4a9SSolomon Peachy } 2182a910e4a9SSolomon Peachy 2183a910e4a9SSolomon Peachy /* ******************************************************************** */ 2184a910e4a9SSolomon Peachy /* AP privates */ 2185a910e4a9SSolomon Peachy 2186a910e4a9SSolomon Peachy static int cw1200_upload_beacon(struct cw1200_common *priv) 2187a910e4a9SSolomon Peachy { 2188a910e4a9SSolomon Peachy int ret = 0; 2189a910e4a9SSolomon Peachy struct ieee80211_mgmt *mgmt; 2190a910e4a9SSolomon Peachy struct wsm_template_frame frame = { 2191a910e4a9SSolomon Peachy .frame_type = WSM_FRAME_TYPE_BEACON, 2192a910e4a9SSolomon Peachy }; 2193a910e4a9SSolomon Peachy 2194a910e4a9SSolomon Peachy u16 tim_offset; 2195a910e4a9SSolomon Peachy u16 tim_len; 2196a910e4a9SSolomon Peachy 2197a910e4a9SSolomon Peachy if (priv->mode == NL80211_IFTYPE_STATION || 2198a910e4a9SSolomon Peachy priv->mode == NL80211_IFTYPE_MONITOR || 2199a910e4a9SSolomon Peachy priv->mode == NL80211_IFTYPE_UNSPECIFIED) 2200a910e4a9SSolomon Peachy goto done; 2201a910e4a9SSolomon Peachy 2202a910e4a9SSolomon Peachy if (priv->vif->p2p) 2203a910e4a9SSolomon Peachy frame.rate = WSM_TRANSMIT_RATE_6; 2204a910e4a9SSolomon Peachy 2205a910e4a9SSolomon Peachy frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, 2206a910e4a9SSolomon Peachy &tim_offset, &tim_len); 2207a910e4a9SSolomon Peachy if (!frame.skb) 2208a910e4a9SSolomon Peachy return -ENOMEM; 2209a910e4a9SSolomon Peachy 2210a910e4a9SSolomon Peachy ret = wsm_set_template_frame(priv, &frame); 2211a910e4a9SSolomon Peachy 2212a910e4a9SSolomon Peachy if (ret) 2213a910e4a9SSolomon Peachy goto done; 2214a910e4a9SSolomon Peachy 2215a910e4a9SSolomon Peachy /* TODO: Distill probe resp; remove TIM 2216a910e4a9SSolomon Peachy * and any other beacon-specific IEs 2217a910e4a9SSolomon Peachy */ 2218a910e4a9SSolomon Peachy mgmt = (void *)frame.skb->data; 2219a910e4a9SSolomon Peachy mgmt->frame_control = 2220a910e4a9SSolomon Peachy __cpu_to_le16(IEEE80211_FTYPE_MGMT | 2221a910e4a9SSolomon Peachy IEEE80211_STYPE_PROBE_RESP); 2222a910e4a9SSolomon Peachy 2223a910e4a9SSolomon Peachy frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; 2224a910e4a9SSolomon Peachy if (priv->vif->p2p) { 2225a910e4a9SSolomon Peachy ret = wsm_set_probe_responder(priv, true); 2226a910e4a9SSolomon Peachy } else { 2227a910e4a9SSolomon Peachy ret = wsm_set_template_frame(priv, &frame); 2228a910e4a9SSolomon Peachy wsm_set_probe_responder(priv, false); 2229a910e4a9SSolomon Peachy } 2230a910e4a9SSolomon Peachy 2231a910e4a9SSolomon Peachy done: 2232a910e4a9SSolomon Peachy dev_kfree_skb(frame.skb); 2233a910e4a9SSolomon Peachy 2234a910e4a9SSolomon Peachy return ret; 2235a910e4a9SSolomon Peachy } 2236a910e4a9SSolomon Peachy 2237a910e4a9SSolomon Peachy static int cw1200_upload_pspoll(struct cw1200_common *priv) 2238a910e4a9SSolomon Peachy { 2239a910e4a9SSolomon Peachy int ret = 0; 2240a910e4a9SSolomon Peachy struct wsm_template_frame frame = { 2241a910e4a9SSolomon Peachy .frame_type = WSM_FRAME_TYPE_PS_POLL, 2242a910e4a9SSolomon Peachy .rate = 0xFF, 2243a910e4a9SSolomon Peachy }; 2244a910e4a9SSolomon Peachy 2245a910e4a9SSolomon Peachy 2246a910e4a9SSolomon Peachy frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); 2247a910e4a9SSolomon Peachy if (!frame.skb) 2248a910e4a9SSolomon Peachy return -ENOMEM; 2249a910e4a9SSolomon Peachy 2250a910e4a9SSolomon Peachy ret = wsm_set_template_frame(priv, &frame); 2251a910e4a9SSolomon Peachy 2252a910e4a9SSolomon Peachy dev_kfree_skb(frame.skb); 2253a910e4a9SSolomon Peachy 2254a910e4a9SSolomon Peachy return ret; 2255a910e4a9SSolomon Peachy } 2256a910e4a9SSolomon Peachy 2257a910e4a9SSolomon Peachy static int cw1200_upload_null(struct cw1200_common *priv) 2258a910e4a9SSolomon Peachy { 2259a910e4a9SSolomon Peachy int ret = 0; 2260a910e4a9SSolomon Peachy struct wsm_template_frame frame = { 2261a910e4a9SSolomon Peachy .frame_type = WSM_FRAME_TYPE_NULL, 2262a910e4a9SSolomon Peachy .rate = 0xFF, 2263a910e4a9SSolomon Peachy }; 2264a910e4a9SSolomon Peachy 22657b6ddeafSJohannes Berg frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); 2266a910e4a9SSolomon Peachy if (!frame.skb) 2267a910e4a9SSolomon Peachy return -ENOMEM; 2268a910e4a9SSolomon Peachy 2269a910e4a9SSolomon Peachy ret = wsm_set_template_frame(priv, &frame); 2270a910e4a9SSolomon Peachy 2271a910e4a9SSolomon Peachy dev_kfree_skb(frame.skb); 2272a910e4a9SSolomon Peachy 2273a910e4a9SSolomon Peachy return ret; 2274a910e4a9SSolomon Peachy } 2275a910e4a9SSolomon Peachy 2276a910e4a9SSolomon Peachy static int cw1200_upload_qosnull(struct cw1200_common *priv) 2277a910e4a9SSolomon Peachy { 2278a910e4a9SSolomon Peachy /* TODO: This needs to be implemented 2279a910e4a9SSolomon Peachy 2280a910e4a9SSolomon Peachy struct wsm_template_frame frame = { 2281a910e4a9SSolomon Peachy .frame_type = WSM_FRAME_TYPE_QOS_NULL, 2282a910e4a9SSolomon Peachy .rate = 0xFF, 2283a910e4a9SSolomon Peachy }; 2284a910e4a9SSolomon Peachy 2285a910e4a9SSolomon Peachy frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif); 2286a910e4a9SSolomon Peachy if (!frame.skb) 2287a910e4a9SSolomon Peachy return -ENOMEM; 2288a910e4a9SSolomon Peachy 2289a910e4a9SSolomon Peachy ret = wsm_set_template_frame(priv, &frame); 2290a910e4a9SSolomon Peachy 2291a910e4a9SSolomon Peachy dev_kfree_skb(frame.skb); 2292a910e4a9SSolomon Peachy 2293a910e4a9SSolomon Peachy */ 229469253b61SPeter Senna Tschudin return 0; 2295a910e4a9SSolomon Peachy } 2296a910e4a9SSolomon Peachy 2297a910e4a9SSolomon Peachy static int cw1200_enable_beaconing(struct cw1200_common *priv, 2298a910e4a9SSolomon Peachy bool enable) 2299a910e4a9SSolomon Peachy { 2300a910e4a9SSolomon Peachy struct wsm_beacon_transmit transmit = { 2301a910e4a9SSolomon Peachy .enable_beaconing = enable, 2302a910e4a9SSolomon Peachy }; 2303a910e4a9SSolomon Peachy 2304a910e4a9SSolomon Peachy return wsm_beacon_transmit(priv, &transmit); 2305a910e4a9SSolomon Peachy } 2306a910e4a9SSolomon Peachy 2307a910e4a9SSolomon Peachy static int cw1200_start_ap(struct cw1200_common *priv) 2308a910e4a9SSolomon Peachy { 2309a910e4a9SSolomon Peachy int ret; 2310a910e4a9SSolomon Peachy struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; 2311a910e4a9SSolomon Peachy struct wsm_start start = { 2312a910e4a9SSolomon Peachy .mode = priv->vif->p2p ? 2313a910e4a9SSolomon Peachy WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, 231457fbcce3SJohannes Berg .band = (priv->channel->band == NL80211_BAND_5GHZ) ? 2315a910e4a9SSolomon Peachy WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, 2316a910e4a9SSolomon Peachy .channel_number = priv->channel->hw_value, 2317a910e4a9SSolomon Peachy .beacon_interval = conf->beacon_int, 2318a910e4a9SSolomon Peachy .dtim_period = conf->dtim_period, 2319a910e4a9SSolomon Peachy .preamble = conf->use_short_preamble ? 2320a910e4a9SSolomon Peachy WSM_JOIN_PREAMBLE_SHORT : 2321a910e4a9SSolomon Peachy WSM_JOIN_PREAMBLE_LONG, 2322a910e4a9SSolomon Peachy .probe_delay = 100, 2323a910e4a9SSolomon Peachy .basic_rate_set = cw1200_rate_mask_to_wsm(priv, 2324a910e4a9SSolomon Peachy conf->basic_rates), 2325a910e4a9SSolomon Peachy }; 2326a910e4a9SSolomon Peachy struct wsm_operational_mode mode = { 2327a910e4a9SSolomon Peachy .power_mode = cw1200_power_mode, 2328a910e4a9SSolomon Peachy .disable_more_flag_usage = true, 2329a910e4a9SSolomon Peachy }; 2330a910e4a9SSolomon Peachy 2331a910e4a9SSolomon Peachy memset(start.ssid, 0, sizeof(start.ssid)); 2332a910e4a9SSolomon Peachy if (!conf->hidden_ssid) { 2333a910e4a9SSolomon Peachy start.ssid_len = conf->ssid_len; 2334a910e4a9SSolomon Peachy memcpy(start.ssid, conf->ssid, start.ssid_len); 2335a910e4a9SSolomon Peachy } 2336a910e4a9SSolomon Peachy 2337a910e4a9SSolomon Peachy priv->beacon_int = conf->beacon_int; 2338a910e4a9SSolomon Peachy priv->join_dtim_period = conf->dtim_period; 2339a910e4a9SSolomon Peachy 2340a910e4a9SSolomon Peachy memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); 2341a910e4a9SSolomon Peachy 2342a910e4a9SSolomon Peachy pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", 2343a910e4a9SSolomon Peachy start.channel_number, start.band, 2344a910e4a9SSolomon Peachy start.beacon_interval, start.dtim_period, 2345a910e4a9SSolomon Peachy start.basic_rate_set, 2346a910e4a9SSolomon Peachy start.ssid_len, start.ssid); 2347a910e4a9SSolomon Peachy ret = wsm_start(priv, &start); 2348a910e4a9SSolomon Peachy if (!ret) 2349a910e4a9SSolomon Peachy ret = cw1200_upload_keys(priv); 2350a910e4a9SSolomon Peachy if (!ret && priv->vif->p2p) { 2351a910e4a9SSolomon Peachy pr_debug("[AP] Setting p2p powersave configuration.\n"); 2352a910e4a9SSolomon Peachy wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo); 2353a910e4a9SSolomon Peachy } 2354a910e4a9SSolomon Peachy if (!ret) { 2355a910e4a9SSolomon Peachy wsm_set_block_ack_policy(priv, 0, 0); 2356a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_AP; 2357a910e4a9SSolomon Peachy cw1200_update_filtering(priv); 2358a910e4a9SSolomon Peachy } 2359a910e4a9SSolomon Peachy wsm_set_operational_mode(priv, &mode); 2360a910e4a9SSolomon Peachy return ret; 2361a910e4a9SSolomon Peachy } 2362a910e4a9SSolomon Peachy 2363a910e4a9SSolomon Peachy static int cw1200_update_beaconing(struct cw1200_common *priv) 2364a910e4a9SSolomon Peachy { 2365a910e4a9SSolomon Peachy struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; 2366a910e4a9SSolomon Peachy struct wsm_reset reset = { 2367a910e4a9SSolomon Peachy .link_id = 0, 2368a910e4a9SSolomon Peachy .reset_statistics = true, 2369a910e4a9SSolomon Peachy }; 2370a910e4a9SSolomon Peachy 2371a910e4a9SSolomon Peachy if (priv->mode == NL80211_IFTYPE_AP) { 2372a910e4a9SSolomon Peachy /* TODO: check if changed channel, band */ 2373a910e4a9SSolomon Peachy if (priv->join_status != CW1200_JOIN_STATUS_AP || 2374a910e4a9SSolomon Peachy priv->beacon_int != conf->beacon_int) { 2375a910e4a9SSolomon Peachy pr_debug("ap restarting\n"); 2376a910e4a9SSolomon Peachy wsm_lock_tx(priv); 2377a910e4a9SSolomon Peachy if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) 2378a910e4a9SSolomon Peachy wsm_reset(priv, &reset); 2379a910e4a9SSolomon Peachy priv->join_status = CW1200_JOIN_STATUS_PASSIVE; 2380a910e4a9SSolomon Peachy cw1200_start_ap(priv); 2381a910e4a9SSolomon Peachy wsm_unlock_tx(priv); 2382a910e4a9SSolomon Peachy } else 2383a910e4a9SSolomon Peachy pr_debug("ap started join_status: %d\n", 2384a910e4a9SSolomon Peachy priv->join_status); 2385a910e4a9SSolomon Peachy } 2386a910e4a9SSolomon Peachy return 0; 2387a910e4a9SSolomon Peachy } 2388