1fb9987d0SSujith /* 2fb9987d0SSujith * Copyright (c) 2010 Atheros Communications Inc. 3fb9987d0SSujith * 4fb9987d0SSujith * Permission to use, copy, modify, and/or distribute this software for any 5fb9987d0SSujith * purpose with or without fee is hereby granted, provided that the above 6fb9987d0SSujith * copyright notice and this permission notice appear in all copies. 7fb9987d0SSujith * 8fb9987d0SSujith * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9fb9987d0SSujith * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10fb9987d0SSujith * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11fb9987d0SSujith * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12fb9987d0SSujith * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13fb9987d0SSujith * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14fb9987d0SSujith * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15fb9987d0SSujith */ 16fb9987d0SSujith 17fb9987d0SSujith #include "htc.h" 18fb9987d0SSujith 19fb9987d0SSujith #ifdef CONFIG_ATH9K_HTC_DEBUGFS 20fb9987d0SSujith static struct dentry *ath9k_debugfs_root; 21fb9987d0SSujith #endif 22fb9987d0SSujith 23fb9987d0SSujith /*************/ 24fb9987d0SSujith /* Utilities */ 25fb9987d0SSujith /*************/ 26fb9987d0SSujith 27fb9987d0SSujith /* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */ 28fb9987d0SSujith static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, 29fb9987d0SSujith struct ath9k_channel *ichan) 30fb9987d0SSujith { 31fb9987d0SSujith enum htc_phymode mode; 32fb9987d0SSujith 33fb9987d0SSujith mode = HTC_MODE_AUTO; 34fb9987d0SSujith 35fb9987d0SSujith switch (ichan->chanmode) { 36fb9987d0SSujith case CHANNEL_G: 37fb9987d0SSujith case CHANNEL_G_HT20: 38fb9987d0SSujith case CHANNEL_G_HT40PLUS: 39fb9987d0SSujith case CHANNEL_G_HT40MINUS: 40fb9987d0SSujith mode = HTC_MODE_11NG; 41fb9987d0SSujith break; 42fb9987d0SSujith case CHANNEL_A: 43fb9987d0SSujith case CHANNEL_A_HT20: 44fb9987d0SSujith case CHANNEL_A_HT40PLUS: 45fb9987d0SSujith case CHANNEL_A_HT40MINUS: 46fb9987d0SSujith mode = HTC_MODE_11NA; 47fb9987d0SSujith break; 48fb9987d0SSujith default: 49fb9987d0SSujith break; 50fb9987d0SSujith } 51fb9987d0SSujith 52fb9987d0SSujith return mode; 53fb9987d0SSujith } 54fb9987d0SSujith 55f933ebedSSujith Manoharan bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, 56bde748a4SVivek Natarajan enum ath9k_power_mode mode) 57bde748a4SVivek Natarajan { 58bde748a4SVivek Natarajan bool ret; 59bde748a4SVivek Natarajan 60bde748a4SVivek Natarajan mutex_lock(&priv->htc_pm_lock); 61bde748a4SVivek Natarajan ret = ath9k_hw_setpower(priv->ah, mode); 62bde748a4SVivek Natarajan mutex_unlock(&priv->htc_pm_lock); 63bde748a4SVivek Natarajan 64bde748a4SVivek Natarajan return ret; 65bde748a4SVivek Natarajan } 66bde748a4SVivek Natarajan 67bde748a4SVivek Natarajan void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv) 68bde748a4SVivek Natarajan { 69bde748a4SVivek Natarajan mutex_lock(&priv->htc_pm_lock); 70bde748a4SVivek Natarajan if (++priv->ps_usecount != 1) 71bde748a4SVivek Natarajan goto unlock; 72bde748a4SVivek Natarajan ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE); 73bde748a4SVivek Natarajan 74bde748a4SVivek Natarajan unlock: 75bde748a4SVivek Natarajan mutex_unlock(&priv->htc_pm_lock); 76bde748a4SVivek Natarajan } 77bde748a4SVivek Natarajan 78bde748a4SVivek Natarajan void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv) 79bde748a4SVivek Natarajan { 80bde748a4SVivek Natarajan mutex_lock(&priv->htc_pm_lock); 81bde748a4SVivek Natarajan if (--priv->ps_usecount != 0) 82bde748a4SVivek Natarajan goto unlock; 83bde748a4SVivek Natarajan 848a8572a8SVivek Natarajan if (priv->ps_idle) 858a8572a8SVivek Natarajan ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP); 868a8572a8SVivek Natarajan else if (priv->ps_enabled) 87bde748a4SVivek Natarajan ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP); 888a8572a8SVivek Natarajan 89bde748a4SVivek Natarajan unlock: 90bde748a4SVivek Natarajan mutex_unlock(&priv->htc_pm_lock); 91bde748a4SVivek Natarajan } 92bde748a4SVivek Natarajan 93bde748a4SVivek Natarajan void ath9k_ps_work(struct work_struct *work) 94bde748a4SVivek Natarajan { 95bde748a4SVivek Natarajan struct ath9k_htc_priv *priv = 96bde748a4SVivek Natarajan container_of(work, struct ath9k_htc_priv, 97bde748a4SVivek Natarajan ps_work); 98bde748a4SVivek Natarajan ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); 99bde748a4SVivek Natarajan 100bde748a4SVivek Natarajan /* The chip wakes up after receiving the first beacon 101bde748a4SVivek Natarajan while network sleep is enabled. For the driver to 102bde748a4SVivek Natarajan be in sync with the hw, set the chip to awake and 103bde748a4SVivek Natarajan only then set it to sleep. 104bde748a4SVivek Natarajan */ 105bde748a4SVivek Natarajan ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); 106bde748a4SVivek Natarajan } 107bde748a4SVivek Natarajan 1087c277349SSujith Manoharan static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) 1097c277349SSujith Manoharan { 1107c277349SSujith Manoharan struct ath9k_htc_priv *priv = data; 1117c277349SSujith Manoharan struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 1127c277349SSujith Manoharan 1137c277349SSujith Manoharan if (bss_conf->assoc) { 1147c277349SSujith Manoharan priv->rearm_ani = true; 1157c277349SSujith Manoharan priv->reconfig_beacon = true; 1167c277349SSujith Manoharan } 1177c277349SSujith Manoharan } 1187c277349SSujith Manoharan 1197c277349SSujith Manoharan static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv) 1207c277349SSujith Manoharan { 1217c277349SSujith Manoharan priv->rearm_ani = false; 1227c277349SSujith Manoharan priv->reconfig_beacon = false; 1237c277349SSujith Manoharan 1247c277349SSujith Manoharan ieee80211_iterate_active_interfaces_atomic(priv->hw, 1257c277349SSujith Manoharan ath9k_htc_vif_iter, priv); 1267c277349SSujith Manoharan if (priv->rearm_ani) 1277c277349SSujith Manoharan ath_start_ani(priv); 1287c277349SSujith Manoharan 1297c277349SSujith Manoharan if (priv->reconfig_beacon) { 1307c277349SSujith Manoharan ath9k_htc_ps_wakeup(priv); 1317c277349SSujith Manoharan ath9k_htc_beacon_reconfig(priv); 1327c277349SSujith Manoharan ath9k_htc_ps_restore(priv); 1337c277349SSujith Manoharan } 1347c277349SSujith Manoharan } 1357c277349SSujith Manoharan 136585895cdSSujith Manoharan static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif) 137585895cdSSujith Manoharan { 138585895cdSSujith Manoharan struct ath9k_vif_iter_data *iter_data = data; 139585895cdSSujith Manoharan int i; 140585895cdSSujith Manoharan 141585895cdSSujith Manoharan for (i = 0; i < ETH_ALEN; i++) 142585895cdSSujith Manoharan iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); 143585895cdSSujith Manoharan } 144585895cdSSujith Manoharan 145585895cdSSujith Manoharan static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, 146585895cdSSujith Manoharan struct ieee80211_vif *vif) 147585895cdSSujith Manoharan { 148585895cdSSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah); 149585895cdSSujith Manoharan struct ath9k_vif_iter_data iter_data; 150585895cdSSujith Manoharan 151585895cdSSujith Manoharan /* 152585895cdSSujith Manoharan * Use the hardware MAC address as reference, the hardware uses it 153585895cdSSujith Manoharan * together with the BSSID mask when matching addresses. 154585895cdSSujith Manoharan */ 155585895cdSSujith Manoharan iter_data.hw_macaddr = common->macaddr; 156585895cdSSujith Manoharan memset(&iter_data.mask, 0xff, ETH_ALEN); 157585895cdSSujith Manoharan 158585895cdSSujith Manoharan if (vif) 159585895cdSSujith Manoharan ath9k_htc_bssid_iter(&iter_data, vif->addr, vif); 160585895cdSSujith Manoharan 161585895cdSSujith Manoharan /* Get list of all active MAC addresses */ 162585895cdSSujith Manoharan ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter, 163585895cdSSujith Manoharan &iter_data); 164585895cdSSujith Manoharan 165585895cdSSujith Manoharan memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); 166585895cdSSujith Manoharan ath_hw_setbssidmask(common); 167585895cdSSujith Manoharan } 168585895cdSSujith Manoharan 16973908674SSujith Manoharan void ath9k_htc_reset(struct ath9k_htc_priv *priv) 17073908674SSujith Manoharan { 17173908674SSujith Manoharan struct ath_hw *ah = priv->ah; 17273908674SSujith Manoharan struct ath_common *common = ath9k_hw_common(ah); 17373908674SSujith Manoharan struct ieee80211_channel *channel = priv->hw->conf.channel; 1744e3ae387SRajkumar Manoharan struct ath9k_hw_cal_data *caldata = NULL; 17573908674SSujith Manoharan enum htc_phymode mode; 17673908674SSujith Manoharan __be16 htc_mode; 17773908674SSujith Manoharan u8 cmd_rsp; 17873908674SSujith Manoharan int ret; 17973908674SSujith Manoharan 18073908674SSujith Manoharan mutex_lock(&priv->mutex); 18173908674SSujith Manoharan ath9k_htc_ps_wakeup(priv); 18273908674SSujith Manoharan 18373908674SSujith Manoharan cancel_delayed_work_sync(&priv->ath9k_ani_work); 18473908674SSujith Manoharan ieee80211_stop_queues(priv->hw); 18573908674SSujith Manoharan htc_stop(priv->htc); 18673908674SSujith Manoharan WMI_CMD(WMI_DISABLE_INTR_CMDID); 18773908674SSujith Manoharan WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 18873908674SSujith Manoharan WMI_CMD(WMI_STOP_RECV_CMDID); 18973908674SSujith Manoharan 1904e3ae387SRajkumar Manoharan caldata = &priv->caldata; 19173908674SSujith Manoharan ret = ath9k_hw_reset(ah, ah->curchan, caldata, false); 19273908674SSujith Manoharan if (ret) { 19373908674SSujith Manoharan ath_err(common, 19473908674SSujith Manoharan "Unable to reset device (%u Mhz) reset status %d\n", 19573908674SSujith Manoharan channel->center_freq, ret); 19673908674SSujith Manoharan } 19773908674SSujith Manoharan 198b2a5c3dfSRajkumar Manoharan ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, 199b2a5c3dfSRajkumar Manoharan &priv->curtxpow); 20073908674SSujith Manoharan 20173908674SSujith Manoharan WMI_CMD(WMI_START_RECV_CMDID); 20273908674SSujith Manoharan ath9k_host_rx_init(priv); 20373908674SSujith Manoharan 20473908674SSujith Manoharan mode = ath9k_htc_get_curmode(priv, ah->curchan); 20573908674SSujith Manoharan htc_mode = cpu_to_be16(mode); 20673908674SSujith Manoharan WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); 20773908674SSujith Manoharan 20873908674SSujith Manoharan WMI_CMD(WMI_ENABLE_INTR_CMDID); 20973908674SSujith Manoharan htc_start(priv->htc); 2107c277349SSujith Manoharan ath9k_htc_vif_reconfig(priv); 21173908674SSujith Manoharan ieee80211_wake_queues(priv->hw); 21273908674SSujith Manoharan 21373908674SSujith Manoharan ath9k_htc_ps_restore(priv); 21473908674SSujith Manoharan mutex_unlock(&priv->mutex); 21573908674SSujith Manoharan } 21673908674SSujith Manoharan 217fb9987d0SSujith static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, 218fb9987d0SSujith struct ieee80211_hw *hw, 219fb9987d0SSujith struct ath9k_channel *hchan) 220fb9987d0SSujith { 221fb9987d0SSujith struct ath_hw *ah = priv->ah; 222fb9987d0SSujith struct ath_common *common = ath9k_hw_common(ah); 223fb9987d0SSujith struct ieee80211_conf *conf = &common->hw->conf; 224039a0721SSujith Manoharan bool fastcc; 225fb9987d0SSujith struct ieee80211_channel *channel = hw->conf.channel; 2268354dd3eSVivek Natarajan struct ath9k_hw_cal_data *caldata = NULL; 227fb9987d0SSujith enum htc_phymode mode; 2287f1f5a00SSujith __be16 htc_mode; 229fb9987d0SSujith u8 cmd_rsp; 230fb9987d0SSujith int ret; 231fb9987d0SSujith 232fb9987d0SSujith if (priv->op_flags & OP_INVALID) 233fb9987d0SSujith return -EIO; 234fb9987d0SSujith 235039a0721SSujith Manoharan fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); 236fb9987d0SSujith 237bde748a4SVivek Natarajan ath9k_htc_ps_wakeup(priv); 238fb9987d0SSujith htc_stop(priv->htc); 239fb9987d0SSujith WMI_CMD(WMI_DISABLE_INTR_CMDID); 240fb9987d0SSujith WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 241fb9987d0SSujith WMI_CMD(WMI_STOP_RECV_CMDID); 242fb9987d0SSujith 243226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 2447cf1f2ddSRajkumar Manoharan "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", 245fb9987d0SSujith priv->ah->curchan->channel, 2467cf1f2ddSRajkumar Manoharan channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), 2477cf1f2ddSRajkumar Manoharan fastcc); 248fb9987d0SSujith 2494e3ae387SRajkumar Manoharan if (!fastcc) 2504e3ae387SRajkumar Manoharan caldata = &priv->caldata; 25120bd2a09SFelix Fietkau ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); 252fb9987d0SSujith if (ret) { 2533800276aSJoe Perches ath_err(common, 2543800276aSJoe Perches "Unable to reset channel (%u Mhz) reset status %d\n", 2553800276aSJoe Perches channel->center_freq, ret); 256fb9987d0SSujith goto err; 257fb9987d0SSujith } 258fb9987d0SSujith 259b2a5c3dfSRajkumar Manoharan ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, 260b2a5c3dfSRajkumar Manoharan &priv->curtxpow); 261fb9987d0SSujith 262fb9987d0SSujith WMI_CMD(WMI_START_RECV_CMDID); 263fb9987d0SSujith if (ret) 264fb9987d0SSujith goto err; 265fb9987d0SSujith 266fb9987d0SSujith ath9k_host_rx_init(priv); 267fb9987d0SSujith 268fb9987d0SSujith mode = ath9k_htc_get_curmode(priv, hchan); 269fb9987d0SSujith htc_mode = cpu_to_be16(mode); 270fb9987d0SSujith WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); 271fb9987d0SSujith if (ret) 272fb9987d0SSujith goto err; 273fb9987d0SSujith 274fb9987d0SSujith WMI_CMD(WMI_ENABLE_INTR_CMDID); 275fb9987d0SSujith if (ret) 276fb9987d0SSujith goto err; 277fb9987d0SSujith 278fb9987d0SSujith htc_start(priv->htc); 279fb9987d0SSujith err: 280bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 281fb9987d0SSujith return ret; 282fb9987d0SSujith } 283fb9987d0SSujith 284a97b478cSSujith Manoharan /* 285a97b478cSSujith Manoharan * Monitor mode handling is a tad complicated because the firmware requires 286a97b478cSSujith Manoharan * an interface to be created exclusively, while mac80211 doesn't associate 287a97b478cSSujith Manoharan * an interface with the mode. 288a97b478cSSujith Manoharan * 289a97b478cSSujith Manoharan * So, for now, only one monitor interface can be configured. 290a97b478cSSujith Manoharan */ 291cc721287SSujith Manoharan static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) 29281fc2a33SRajkumar Manoharan { 29381fc2a33SRajkumar Manoharan struct ath_common *common = ath9k_hw_common(priv->ah); 29481fc2a33SRajkumar Manoharan struct ath9k_htc_target_vif hvif; 29581fc2a33SRajkumar Manoharan int ret = 0; 29681fc2a33SRajkumar Manoharan u8 cmd_rsp; 29781fc2a33SRajkumar Manoharan 298cc721287SSujith Manoharan memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); 299cc721287SSujith Manoharan memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); 300a97b478cSSujith Manoharan hvif.index = priv->mon_vif_idx; 301cc721287SSujith Manoharan WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); 302cc721287SSujith Manoharan priv->nvifs--; 303a97b478cSSujith Manoharan priv->vif_slot &= ~(1 << priv->mon_vif_idx); 304cc721287SSujith Manoharan } 305cc721287SSujith Manoharan 306cc721287SSujith Manoharan static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) 307cc721287SSujith Manoharan { 308cc721287SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah); 309cc721287SSujith Manoharan struct ath9k_htc_target_vif hvif; 310cc721287SSujith Manoharan struct ath9k_htc_target_sta tsta; 311a97b478cSSujith Manoharan int ret = 0, sta_idx; 312cc721287SSujith Manoharan u8 cmd_rsp; 313cc721287SSujith Manoharan 314a97b478cSSujith Manoharan if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) || 315a97b478cSSujith Manoharan (priv->nstations >= ATH9K_HTC_MAX_STA)) { 316a97b478cSSujith Manoharan ret = -ENOBUFS; 317a97b478cSSujith Manoharan goto err_vif; 318a97b478cSSujith Manoharan } 31981fc2a33SRajkumar Manoharan 320a97b478cSSujith Manoharan sta_idx = ffz(priv->sta_slot); 321a97b478cSSujith Manoharan if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) { 322a97b478cSSujith Manoharan ret = -ENOBUFS; 323a97b478cSSujith Manoharan goto err_vif; 324a97b478cSSujith Manoharan } 325cc721287SSujith Manoharan 326cc721287SSujith Manoharan /* 327cc721287SSujith Manoharan * Add an interface. 328cc721287SSujith Manoharan */ 32981fc2a33SRajkumar Manoharan memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); 33081fc2a33SRajkumar Manoharan memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); 33181fc2a33SRajkumar Manoharan 33281fc2a33SRajkumar Manoharan hvif.opmode = cpu_to_be32(HTC_M_MONITOR); 333a97b478cSSujith Manoharan hvif.index = ffz(priv->vif_slot); 33481fc2a33SRajkumar Manoharan 33581fc2a33SRajkumar Manoharan WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); 33681fc2a33SRajkumar Manoharan if (ret) 337a97b478cSSujith Manoharan goto err_vif; 338a97b478cSSujith Manoharan 339a97b478cSSujith Manoharan /* 340a97b478cSSujith Manoharan * Assign the monitor interface index as a special case here. 341a97b478cSSujith Manoharan * This is needed when the interface is brought down. 342a97b478cSSujith Manoharan */ 343a97b478cSSujith Manoharan priv->mon_vif_idx = hvif.index; 344a97b478cSSujith Manoharan priv->vif_slot |= (1 << hvif.index); 345a97b478cSSujith Manoharan 346a97b478cSSujith Manoharan /* 347a97b478cSSujith Manoharan * Set the hardware mode to monitor only if there are no 348a97b478cSSujith Manoharan * other interfaces. 349a97b478cSSujith Manoharan */ 350a97b478cSSujith Manoharan if (!priv->nvifs) 351a97b478cSSujith Manoharan priv->ah->opmode = NL80211_IFTYPE_MONITOR; 35281fc2a33SRajkumar Manoharan 35381fc2a33SRajkumar Manoharan priv->nvifs++; 354cc721287SSujith Manoharan 355cc721287SSujith Manoharan /* 356cc721287SSujith Manoharan * Associate a station with the interface for packet injection. 357cc721287SSujith Manoharan */ 358cc721287SSujith Manoharan memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); 359cc721287SSujith Manoharan 360cc721287SSujith Manoharan memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN); 361cc721287SSujith Manoharan 362cc721287SSujith Manoharan tsta.is_vif_sta = 1; 363a97b478cSSujith Manoharan tsta.sta_index = sta_idx; 364cc721287SSujith Manoharan tsta.vif_index = hvif.index; 365cc721287SSujith Manoharan tsta.maxampdu = 0xffff; 366cc721287SSujith Manoharan 367cc721287SSujith Manoharan WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); 368cc721287SSujith Manoharan if (ret) { 369cc721287SSujith Manoharan ath_err(common, "Unable to add station entry for monitor mode\n"); 370a97b478cSSujith Manoharan goto err_sta; 371cc721287SSujith Manoharan } 372cc721287SSujith Manoharan 373a97b478cSSujith Manoharan priv->sta_slot |= (1 << sta_idx); 374cc721287SSujith Manoharan priv->nstations++; 375a97b478cSSujith Manoharan priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx; 37655de80d6SSujith Manoharan priv->ah->is_monitoring = true; 37755de80d6SSujith Manoharan 378a97b478cSSujith Manoharan ath_dbg(common, ATH_DBG_CONFIG, 379a97b478cSSujith Manoharan "Attached a monitor interface at idx: %d, sta idx: %d\n", 380a97b478cSSujith Manoharan priv->mon_vif_idx, sta_idx); 381a97b478cSSujith Manoharan 38281fc2a33SRajkumar Manoharan return 0; 383cc721287SSujith Manoharan 384a97b478cSSujith Manoharan err_sta: 385cc721287SSujith Manoharan /* 386cc721287SSujith Manoharan * Remove the interface from the target. 387cc721287SSujith Manoharan */ 388cc721287SSujith Manoharan __ath9k_htc_remove_monitor_interface(priv); 389a97b478cSSujith Manoharan err_vif: 390a97b478cSSujith Manoharan ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n"); 391a97b478cSSujith Manoharan 392cc721287SSujith Manoharan return ret; 39381fc2a33SRajkumar Manoharan } 39481fc2a33SRajkumar Manoharan 39581fc2a33SRajkumar Manoharan static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) 39681fc2a33SRajkumar Manoharan { 39781fc2a33SRajkumar Manoharan struct ath_common *common = ath9k_hw_common(priv->ah); 39881fc2a33SRajkumar Manoharan int ret = 0; 399cc721287SSujith Manoharan u8 cmd_rsp, sta_idx; 40081fc2a33SRajkumar Manoharan 401cc721287SSujith Manoharan __ath9k_htc_remove_monitor_interface(priv); 40281fc2a33SRajkumar Manoharan 403a97b478cSSujith Manoharan sta_idx = priv->vif_sta_pos[priv->mon_vif_idx]; 404cc721287SSujith Manoharan 405cc721287SSujith Manoharan WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); 406cc721287SSujith Manoharan if (ret) { 407cc721287SSujith Manoharan ath_err(common, "Unable to remove station entry for monitor mode\n"); 40881fc2a33SRajkumar Manoharan return ret; 40981fc2a33SRajkumar Manoharan } 41081fc2a33SRajkumar Manoharan 411a97b478cSSujith Manoharan priv->sta_slot &= ~(1 << sta_idx); 412cc721287SSujith Manoharan priv->nstations--; 41355de80d6SSujith Manoharan priv->ah->is_monitoring = false; 414cc721287SSujith Manoharan 415a97b478cSSujith Manoharan ath_dbg(common, ATH_DBG_CONFIG, 416a97b478cSSujith Manoharan "Removed a monitor interface at idx: %d, sta idx: %d\n", 417a97b478cSSujith Manoharan priv->mon_vif_idx, sta_idx); 418a97b478cSSujith Manoharan 419cc721287SSujith Manoharan return 0; 420cc721287SSujith Manoharan } 421cc721287SSujith Manoharan 422fb9987d0SSujith static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, 423fb9987d0SSujith struct ieee80211_vif *vif, 424fb9987d0SSujith struct ieee80211_sta *sta) 425fb9987d0SSujith { 426fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 427fb9987d0SSujith struct ath9k_htc_target_sta tsta; 428fb9987d0SSujith struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; 429fb9987d0SSujith struct ath9k_htc_sta *ista; 430a97b478cSSujith Manoharan int ret, sta_idx; 431fb9987d0SSujith u8 cmd_rsp; 432fb9987d0SSujith 433fb9987d0SSujith if (priv->nstations >= ATH9K_HTC_MAX_STA) 434fb9987d0SSujith return -ENOBUFS; 435fb9987d0SSujith 436a97b478cSSujith Manoharan sta_idx = ffz(priv->sta_slot); 437a97b478cSSujith Manoharan if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) 438a97b478cSSujith Manoharan return -ENOBUFS; 439a97b478cSSujith Manoharan 440fb9987d0SSujith memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); 441fb9987d0SSujith 442fb9987d0SSujith if (sta) { 443fb9987d0SSujith ista = (struct ath9k_htc_sta *) sta->drv_priv; 444fb9987d0SSujith memcpy(&tsta.macaddr, sta->addr, ETH_ALEN); 445fb9987d0SSujith memcpy(&tsta.bssid, common->curbssid, ETH_ALEN); 446fb9987d0SSujith tsta.associd = common->curaid; 447fb9987d0SSujith tsta.is_vif_sta = 0; 448fb9987d0SSujith tsta.valid = true; 449a97b478cSSujith Manoharan ista->index = sta_idx; 450fb9987d0SSujith } else { 451fb9987d0SSujith memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); 452fb9987d0SSujith tsta.is_vif_sta = 1; 453fb9987d0SSujith } 454fb9987d0SSujith 455a97b478cSSujith Manoharan tsta.sta_index = sta_idx; 456fb9987d0SSujith tsta.vif_index = avp->index; 457fb9987d0SSujith tsta.maxampdu = 0xffff; 458fb9987d0SSujith if (sta && sta->ht_cap.ht_supported) 459fb9987d0SSujith tsta.flags = cpu_to_be16(ATH_HTC_STA_HT); 460fb9987d0SSujith 461fb9987d0SSujith WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); 462fb9987d0SSujith if (ret) { 463fb9987d0SSujith if (sta) 4643800276aSJoe Perches ath_err(common, 4653800276aSJoe Perches "Unable to add station entry for: %pM\n", 4663800276aSJoe Perches sta->addr); 467fb9987d0SSujith return ret; 468fb9987d0SSujith } 469fb9987d0SSujith 470a97b478cSSujith Manoharan if (sta) { 471226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 472fb9987d0SSujith "Added a station entry for: %pM (idx: %d)\n", 473fb9987d0SSujith sta->addr, tsta.sta_index); 474a97b478cSSujith Manoharan } else { 475a97b478cSSujith Manoharan ath_dbg(common, ATH_DBG_CONFIG, 476a97b478cSSujith Manoharan "Added a station entry for VIF %d (idx: %d)\n", 477a97b478cSSujith Manoharan avp->index, tsta.sta_index); 478a97b478cSSujith Manoharan } 479fb9987d0SSujith 480a97b478cSSujith Manoharan priv->sta_slot |= (1 << sta_idx); 481fb9987d0SSujith priv->nstations++; 482a97b478cSSujith Manoharan if (!sta) 483a97b478cSSujith Manoharan priv->vif_sta_pos[avp->index] = sta_idx; 484a97b478cSSujith Manoharan 485fb9987d0SSujith return 0; 486fb9987d0SSujith } 487fb9987d0SSujith 488fb9987d0SSujith static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, 489fb9987d0SSujith struct ieee80211_vif *vif, 490fb9987d0SSujith struct ieee80211_sta *sta) 491fb9987d0SSujith { 492fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 493a97b478cSSujith Manoharan struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; 494fb9987d0SSujith struct ath9k_htc_sta *ista; 495fb9987d0SSujith int ret; 496fb9987d0SSujith u8 cmd_rsp, sta_idx; 497fb9987d0SSujith 498fb9987d0SSujith if (sta) { 499fb9987d0SSujith ista = (struct ath9k_htc_sta *) sta->drv_priv; 500fb9987d0SSujith sta_idx = ista->index; 501fb9987d0SSujith } else { 502a97b478cSSujith Manoharan sta_idx = priv->vif_sta_pos[avp->index]; 503fb9987d0SSujith } 504fb9987d0SSujith 505fb9987d0SSujith WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); 506fb9987d0SSujith if (ret) { 507fb9987d0SSujith if (sta) 5083800276aSJoe Perches ath_err(common, 509fb9987d0SSujith "Unable to remove station entry for: %pM\n", 510fb9987d0SSujith sta->addr); 511fb9987d0SSujith return ret; 512fb9987d0SSujith } 513fb9987d0SSujith 514a97b478cSSujith Manoharan if (sta) { 515226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 516fb9987d0SSujith "Removed a station entry for: %pM (idx: %d)\n", 517fb9987d0SSujith sta->addr, sta_idx); 518a97b478cSSujith Manoharan } else { 519a97b478cSSujith Manoharan ath_dbg(common, ATH_DBG_CONFIG, 520a97b478cSSujith Manoharan "Removed a station entry for VIF %d (idx: %d)\n", 521a97b478cSSujith Manoharan avp->index, sta_idx); 522a97b478cSSujith Manoharan } 523fb9987d0SSujith 524a97b478cSSujith Manoharan priv->sta_slot &= ~(1 << sta_idx); 525fb9987d0SSujith priv->nstations--; 526a97b478cSSujith Manoharan 527fb9987d0SSujith return 0; 528fb9987d0SSujith } 529fb9987d0SSujith 53055de80d6SSujith Manoharan int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv) 531fb9987d0SSujith { 532fb9987d0SSujith struct ath9k_htc_cap_target tcap; 533fb9987d0SSujith int ret; 534fb9987d0SSujith u8 cmd_rsp; 535fb9987d0SSujith 536fb9987d0SSujith memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target)); 537fb9987d0SSujith 538fb9987d0SSujith /* FIXME: Values are hardcoded */ 539fb9987d0SSujith tcap.flags = 0x240c40; 540fb9987d0SSujith tcap.flags_ext = 0x80601000; 541fb9987d0SSujith tcap.ampdu_limit = 0xffff0000; 542fb9987d0SSujith tcap.ampdu_subframes = 20; 54329d9075eSSujith tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask; 544fb9987d0SSujith tcap.protmode = 1; 54529d9075eSSujith tcap.tx_chainmask = priv->ah->caps.tx_chainmask; 546fb9987d0SSujith 547fb9987d0SSujith WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap); 548fb9987d0SSujith 549fb9987d0SSujith return ret; 550fb9987d0SSujith } 551fb9987d0SSujith 5520d425a7dSSujith static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv, 5530d425a7dSSujith struct ieee80211_sta *sta, 5540d425a7dSSujith struct ath9k_htc_target_rate *trate) 555fb9987d0SSujith { 556fb9987d0SSujith struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; 557fb9987d0SSujith struct ieee80211_supported_band *sband; 558fb9987d0SSujith u32 caps = 0; 5590d425a7dSSujith int i, j; 560fb9987d0SSujith 561ea46e644SSujith sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band]; 562fb9987d0SSujith 563fb9987d0SSujith for (i = 0, j = 0; i < sband->n_bitrates; i++) { 564fb9987d0SSujith if (sta->supp_rates[sband->band] & BIT(i)) { 5650d425a7dSSujith trate->rates.legacy_rates.rs_rates[j] 566fb9987d0SSujith = (sband->bitrates[i].bitrate * 2) / 10; 567fb9987d0SSujith j++; 568fb9987d0SSujith } 569fb9987d0SSujith } 5700d425a7dSSujith trate->rates.legacy_rates.rs_nrates = j; 571fb9987d0SSujith 572fb9987d0SSujith if (sta->ht_cap.ht_supported) { 573fb9987d0SSujith for (i = 0, j = 0; i < 77; i++) { 574fb9987d0SSujith if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) 5750d425a7dSSujith trate->rates.ht_rates.rs_rates[j++] = i; 576fb9987d0SSujith if (j == ATH_HTC_RATE_MAX) 577fb9987d0SSujith break; 578fb9987d0SSujith } 5790d425a7dSSujith trate->rates.ht_rates.rs_nrates = j; 580fb9987d0SSujith 581fb9987d0SSujith caps = WLAN_RC_HT_FLAG; 58229d9075eSSujith if (sta->ht_cap.mcs.rx_mask[1]) 58329d9075eSSujith caps |= WLAN_RC_DS_FLAG; 58471ba186cSVivek Natarajan if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && 58571ba186cSVivek Natarajan (conf_is_ht40(&priv->hw->conf))) 586fb9987d0SSujith caps |= WLAN_RC_40_FLAG; 587b4dec5e8SSujith if (conf_is_ht40(&priv->hw->conf) && 588b4dec5e8SSujith (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) 589fb9987d0SSujith caps |= WLAN_RC_SGI_FLAG; 590b4dec5e8SSujith else if (conf_is_ht20(&priv->hw->conf) && 591b4dec5e8SSujith (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)) 592b4dec5e8SSujith caps |= WLAN_RC_SGI_FLAG; 593fb9987d0SSujith } 594fb9987d0SSujith 5950d425a7dSSujith trate->sta_index = ista->index; 5960d425a7dSSujith trate->isnew = 1; 5970d425a7dSSujith trate->capflags = cpu_to_be32(caps); 5980d425a7dSSujith } 599fb9987d0SSujith 6000d425a7dSSujith static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv, 6010d425a7dSSujith struct ath9k_htc_target_rate *trate) 6020d425a7dSSujith { 6030d425a7dSSujith struct ath_common *common = ath9k_hw_common(priv->ah); 6040d425a7dSSujith int ret; 6050d425a7dSSujith u8 cmd_rsp; 6060d425a7dSSujith 6070d425a7dSSujith WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate); 608fb9987d0SSujith if (ret) { 6093800276aSJoe Perches ath_err(common, 610fb9987d0SSujith "Unable to initialize Rate information on target\n"); 6110d425a7dSSujith } 6120d425a7dSSujith 613fb9987d0SSujith return ret; 614fb9987d0SSujith } 615fb9987d0SSujith 6160d425a7dSSujith static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv, 6170d425a7dSSujith struct ieee80211_sta *sta) 618fb9987d0SSujith { 619fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 6200d425a7dSSujith struct ath9k_htc_target_rate trate; 621fb9987d0SSujith int ret; 622fb9987d0SSujith 6230d425a7dSSujith memset(&trate, 0, sizeof(struct ath9k_htc_target_rate)); 6240d425a7dSSujith ath9k_htc_setup_rate(priv, sta, &trate); 6250d425a7dSSujith ret = ath9k_htc_send_rate_cmd(priv, &trate); 6260d425a7dSSujith if (!ret) 627226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 6280d425a7dSSujith "Updated target sta: %pM, rate caps: 0x%X\n", 6290d425a7dSSujith sta->addr, be32_to_cpu(trate.capflags)); 630fb9987d0SSujith } 631fb9987d0SSujith 6322c76ef89SSujith static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv, 6332c76ef89SSujith struct ieee80211_vif *vif, 6342c76ef89SSujith struct ieee80211_bss_conf *bss_conf) 6352c76ef89SSujith { 6362c76ef89SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 6372c76ef89SSujith struct ath9k_htc_target_rate trate; 6382c76ef89SSujith struct ieee80211_sta *sta; 6392c76ef89SSujith int ret; 6402c76ef89SSujith 6412c76ef89SSujith memset(&trate, 0, sizeof(struct ath9k_htc_target_rate)); 6422c76ef89SSujith 6432c76ef89SSujith rcu_read_lock(); 6442c76ef89SSujith sta = ieee80211_find_sta(vif, bss_conf->bssid); 6452c76ef89SSujith if (!sta) { 6462c76ef89SSujith rcu_read_unlock(); 6472c76ef89SSujith return; 6482c76ef89SSujith } 6492c76ef89SSujith ath9k_htc_setup_rate(priv, sta, &trate); 6502c76ef89SSujith rcu_read_unlock(); 6512c76ef89SSujith 6522c76ef89SSujith ret = ath9k_htc_send_rate_cmd(priv, &trate); 6532c76ef89SSujith if (!ret) 654226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 6552c76ef89SSujith "Updated target sta: %pM, rate caps: 0x%X\n", 6562c76ef89SSujith bss_conf->bssid, be32_to_cpu(trate.capflags)); 6572c76ef89SSujith } 6582c76ef89SSujith 6599edd9520SLuis R. Rodriguez static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv, 660fb9987d0SSujith struct ieee80211_vif *vif, 661d7ca2139SSujith struct ieee80211_sta *sta, 6629edd9520SLuis R. Rodriguez enum ieee80211_ampdu_mlme_action action, 6639edd9520SLuis R. Rodriguez u16 tid) 664fb9987d0SSujith { 665fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 666fb9987d0SSujith struct ath9k_htc_target_aggr aggr; 667277a64d1SDan Carpenter struct ath9k_htc_sta *ista; 668fb9987d0SSujith int ret = 0; 669fb9987d0SSujith u8 cmd_rsp; 670fb9987d0SSujith 6710730d114SDan Carpenter if (tid >= ATH9K_HTC_MAX_TID) 672fb9987d0SSujith return -EINVAL; 673fb9987d0SSujith 674fb9987d0SSujith memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr)); 675ef98c3cdSSujith ista = (struct ath9k_htc_sta *) sta->drv_priv; 676fb9987d0SSujith 677ef98c3cdSSujith aggr.sta_index = ista->index; 678d7ca2139SSujith aggr.tidno = tid & 0xf; 679d7ca2139SSujith aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false; 680ef98c3cdSSujith 681fb9987d0SSujith WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr); 682fb9987d0SSujith if (ret) 683226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 684fb9987d0SSujith "Unable to %s TX aggregation for (%pM, %d)\n", 685d7ca2139SSujith (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid); 686fb9987d0SSujith else 687226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 688d7ca2139SSujith "%s TX aggregation for (%pM, %d)\n", 689d7ca2139SSujith (aggr.aggr_enable) ? "Starting" : "Stopping", 690d7ca2139SSujith sta->addr, tid); 691d7ca2139SSujith 692d7ca2139SSujith spin_lock_bh(&priv->tx_lock); 693d7ca2139SSujith ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP; 694d7ca2139SSujith spin_unlock_bh(&priv->tx_lock); 695fb9987d0SSujith 696fb9987d0SSujith return ret; 697fb9987d0SSujith } 698fb9987d0SSujith 699fb9987d0SSujith /*********/ 700fb9987d0SSujith /* DEBUG */ 701fb9987d0SSujith /*********/ 702fb9987d0SSujith 703fb9987d0SSujith #ifdef CONFIG_ATH9K_HTC_DEBUGFS 704fb9987d0SSujith 705fb9987d0SSujith static int ath9k_debugfs_open(struct inode *inode, struct file *file) 706fb9987d0SSujith { 707fb9987d0SSujith file->private_data = inode->i_private; 708fb9987d0SSujith return 0; 709fb9987d0SSujith } 710fb9987d0SSujith 711fb9987d0SSujith static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, 712fb9987d0SSujith size_t count, loff_t *ppos) 713fb9987d0SSujith { 71457674308SJoe Perches struct ath9k_htc_priv *priv = file->private_data; 715fb9987d0SSujith struct ath9k_htc_target_stats cmd_rsp; 716fb9987d0SSujith char buf[512]; 717fb9987d0SSujith unsigned int len = 0; 718fb9987d0SSujith int ret = 0; 719fb9987d0SSujith 720fb9987d0SSujith memset(&cmd_rsp, 0, sizeof(cmd_rsp)); 721fb9987d0SSujith 722fb9987d0SSujith WMI_CMD(WMI_TGT_STATS_CMDID); 723fb9987d0SSujith if (ret) 724fb9987d0SSujith return -EINVAL; 725fb9987d0SSujith 726fb9987d0SSujith 727fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 728fb9987d0SSujith "%19s : %10u\n", "TX Short Retries", 729fb9987d0SSujith be32_to_cpu(cmd_rsp.tx_shortretry)); 730fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 731fb9987d0SSujith "%19s : %10u\n", "TX Long Retries", 732fb9987d0SSujith be32_to_cpu(cmd_rsp.tx_longretry)); 733fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 734fb9987d0SSujith "%19s : %10u\n", "TX Xretries", 735fb9987d0SSujith be32_to_cpu(cmd_rsp.tx_xretries)); 736fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 737fb9987d0SSujith "%19s : %10u\n", "TX Unaggr. Xretries", 738fb9987d0SSujith be32_to_cpu(cmd_rsp.ht_txunaggr_xretry)); 739fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 740fb9987d0SSujith "%19s : %10u\n", "TX Xretries (HT)", 741fb9987d0SSujith be32_to_cpu(cmd_rsp.ht_tx_xretries)); 742fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 743fb9987d0SSujith "%19s : %10u\n", "TX Rate", priv->debug.txrate); 744fb9987d0SSujith 7459746010bSDan Carpenter if (len > sizeof(buf)) 7469746010bSDan Carpenter len = sizeof(buf); 7479746010bSDan Carpenter 748fb9987d0SSujith return simple_read_from_buffer(user_buf, count, ppos, buf, len); 749fb9987d0SSujith } 750fb9987d0SSujith 751fb9987d0SSujith static const struct file_operations fops_tgt_stats = { 752fb9987d0SSujith .read = read_file_tgt_stats, 753fb9987d0SSujith .open = ath9k_debugfs_open, 7546038f373SArnd Bergmann .owner = THIS_MODULE, 7556038f373SArnd Bergmann .llseek = default_llseek, 756fb9987d0SSujith }; 757fb9987d0SSujith 758fb9987d0SSujith static ssize_t read_file_xmit(struct file *file, char __user *user_buf, 759fb9987d0SSujith size_t count, loff_t *ppos) 760fb9987d0SSujith { 76157674308SJoe Perches struct ath9k_htc_priv *priv = file->private_data; 762fb9987d0SSujith char buf[512]; 763fb9987d0SSujith unsigned int len = 0; 764fb9987d0SSujith 765fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 766fb9987d0SSujith "%20s : %10u\n", "Buffers queued", 767fb9987d0SSujith priv->debug.tx_stats.buf_queued); 768fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 769fb9987d0SSujith "%20s : %10u\n", "Buffers completed", 770fb9987d0SSujith priv->debug.tx_stats.buf_completed); 771fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 772fb9987d0SSujith "%20s : %10u\n", "SKBs queued", 773fb9987d0SSujith priv->debug.tx_stats.skb_queued); 774fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 775fb9987d0SSujith "%20s : %10u\n", "SKBs completed", 776fb9987d0SSujith priv->debug.tx_stats.skb_completed); 777eac8e385SSujith len += snprintf(buf + len, sizeof(buf) - len, 778eac8e385SSujith "%20s : %10u\n", "SKBs dropped", 779eac8e385SSujith priv->debug.tx_stats.skb_dropped); 780fb9987d0SSujith 7812edb4583SSujith len += snprintf(buf + len, sizeof(buf) - len, 7822edb4583SSujith "%20s : %10u\n", "BE queued", 7832edb4583SSujith priv->debug.tx_stats.queue_stats[WME_AC_BE]); 7842edb4583SSujith len += snprintf(buf + len, sizeof(buf) - len, 7852edb4583SSujith "%20s : %10u\n", "BK queued", 7862edb4583SSujith priv->debug.tx_stats.queue_stats[WME_AC_BK]); 7872edb4583SSujith len += snprintf(buf + len, sizeof(buf) - len, 7882edb4583SSujith "%20s : %10u\n", "VI queued", 7892edb4583SSujith priv->debug.tx_stats.queue_stats[WME_AC_VI]); 7902edb4583SSujith len += snprintf(buf + len, sizeof(buf) - len, 7912edb4583SSujith "%20s : %10u\n", "VO queued", 7922edb4583SSujith priv->debug.tx_stats.queue_stats[WME_AC_VO]); 7932edb4583SSujith 7949746010bSDan Carpenter if (len > sizeof(buf)) 7959746010bSDan Carpenter len = sizeof(buf); 7969746010bSDan Carpenter 797fb9987d0SSujith return simple_read_from_buffer(user_buf, count, ppos, buf, len); 798fb9987d0SSujith } 799fb9987d0SSujith 800fb9987d0SSujith static const struct file_operations fops_xmit = { 801fb9987d0SSujith .read = read_file_xmit, 802fb9987d0SSujith .open = ath9k_debugfs_open, 8036038f373SArnd Bergmann .owner = THIS_MODULE, 8046038f373SArnd Bergmann .llseek = default_llseek, 805fb9987d0SSujith }; 806fb9987d0SSujith 807fb9987d0SSujith static ssize_t read_file_recv(struct file *file, char __user *user_buf, 808fb9987d0SSujith size_t count, loff_t *ppos) 809fb9987d0SSujith { 81057674308SJoe Perches struct ath9k_htc_priv *priv = file->private_data; 811fb9987d0SSujith char buf[512]; 812fb9987d0SSujith unsigned int len = 0; 813fb9987d0SSujith 814fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 815fb9987d0SSujith "%20s : %10u\n", "SKBs allocated", 816fb9987d0SSujith priv->debug.rx_stats.skb_allocated); 817fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 818fb9987d0SSujith "%20s : %10u\n", "SKBs completed", 819fb9987d0SSujith priv->debug.rx_stats.skb_completed); 820fb9987d0SSujith len += snprintf(buf + len, sizeof(buf) - len, 821fb9987d0SSujith "%20s : %10u\n", "SKBs Dropped", 822fb9987d0SSujith priv->debug.rx_stats.skb_dropped); 823fb9987d0SSujith 8249746010bSDan Carpenter if (len > sizeof(buf)) 8259746010bSDan Carpenter len = sizeof(buf); 8269746010bSDan Carpenter 827fb9987d0SSujith return simple_read_from_buffer(user_buf, count, ppos, buf, len); 828fb9987d0SSujith } 829fb9987d0SSujith 830fb9987d0SSujith static const struct file_operations fops_recv = { 831fb9987d0SSujith .read = read_file_recv, 832fb9987d0SSujith .open = ath9k_debugfs_open, 8336038f373SArnd Bergmann .owner = THIS_MODULE, 8346038f373SArnd Bergmann .llseek = default_llseek, 835fb9987d0SSujith }; 836fb9987d0SSujith 837e1572c5eSSujith int ath9k_htc_init_debug(struct ath_hw *ah) 838fb9987d0SSujith { 839fb9987d0SSujith struct ath_common *common = ath9k_hw_common(ah); 840fb9987d0SSujith struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; 841fb9987d0SSujith 842fb9987d0SSujith if (!ath9k_debugfs_root) 843fb9987d0SSujith return -ENOENT; 844fb9987d0SSujith 845fb9987d0SSujith priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy), 846fb9987d0SSujith ath9k_debugfs_root); 847fb9987d0SSujith if (!priv->debug.debugfs_phy) 848fb9987d0SSujith goto err; 849fb9987d0SSujith 850fb9987d0SSujith priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR, 851fb9987d0SSujith priv->debug.debugfs_phy, 852fb9987d0SSujith priv, &fops_tgt_stats); 853fb9987d0SSujith if (!priv->debug.debugfs_tgt_stats) 854fb9987d0SSujith goto err; 855fb9987d0SSujith 856fb9987d0SSujith 857fb9987d0SSujith priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR, 858fb9987d0SSujith priv->debug.debugfs_phy, 859fb9987d0SSujith priv, &fops_xmit); 860fb9987d0SSujith if (!priv->debug.debugfs_xmit) 861fb9987d0SSujith goto err; 862fb9987d0SSujith 863fb9987d0SSujith priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR, 864fb9987d0SSujith priv->debug.debugfs_phy, 865fb9987d0SSujith priv, &fops_recv); 866fb9987d0SSujith if (!priv->debug.debugfs_recv) 867fb9987d0SSujith goto err; 868fb9987d0SSujith 869fb9987d0SSujith return 0; 870fb9987d0SSujith 871fb9987d0SSujith err: 872e1572c5eSSujith ath9k_htc_exit_debug(ah); 873fb9987d0SSujith return -ENOMEM; 874fb9987d0SSujith } 875fb9987d0SSujith 876e1572c5eSSujith void ath9k_htc_exit_debug(struct ath_hw *ah) 877fb9987d0SSujith { 878fb9987d0SSujith struct ath_common *common = ath9k_hw_common(ah); 879fb9987d0SSujith struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; 880fb9987d0SSujith 881fb9987d0SSujith debugfs_remove(priv->debug.debugfs_recv); 882fb9987d0SSujith debugfs_remove(priv->debug.debugfs_xmit); 883fb9987d0SSujith debugfs_remove(priv->debug.debugfs_tgt_stats); 884fb9987d0SSujith debugfs_remove(priv->debug.debugfs_phy); 885fb9987d0SSujith } 886fb9987d0SSujith 887e1572c5eSSujith int ath9k_htc_debug_create_root(void) 888fb9987d0SSujith { 889fb9987d0SSujith ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); 890fb9987d0SSujith if (!ath9k_debugfs_root) 891fb9987d0SSujith return -ENOENT; 892fb9987d0SSujith 893fb9987d0SSujith return 0; 894fb9987d0SSujith } 895fb9987d0SSujith 896e1572c5eSSujith void ath9k_htc_debug_remove_root(void) 897fb9987d0SSujith { 898fb9987d0SSujith debugfs_remove(ath9k_debugfs_root); 899fb9987d0SSujith ath9k_debugfs_root = NULL; 900fb9987d0SSujith } 901fb9987d0SSujith 902fb9987d0SSujith #endif /* CONFIG_ATH9K_HTC_DEBUGFS */ 903fb9987d0SSujith 904fb9987d0SSujith /*******/ 905fb9987d0SSujith /* ANI */ 906fb9987d0SSujith /*******/ 907fb9987d0SSujith 90873908674SSujith Manoharan void ath_start_ani(struct ath9k_htc_priv *priv) 909fb9987d0SSujith { 910fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 911fb9987d0SSujith unsigned long timestamp = jiffies_to_msecs(jiffies); 912fb9987d0SSujith 913fb9987d0SSujith common->ani.longcal_timer = timestamp; 914fb9987d0SSujith common->ani.shortcal_timer = timestamp; 915fb9987d0SSujith common->ani.checkani_timer = timestamp; 916fb9987d0SSujith 917fb9987d0SSujith ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, 918fb9987d0SSujith msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); 919fb9987d0SSujith } 920fb9987d0SSujith 921fb9987d0SSujith void ath9k_ani_work(struct work_struct *work) 922fb9987d0SSujith { 923fb9987d0SSujith struct ath9k_htc_priv *priv = 924fb9987d0SSujith container_of(work, struct ath9k_htc_priv, 925fb9987d0SSujith ath9k_ani_work.work); 926fb9987d0SSujith struct ath_hw *ah = priv->ah; 927fb9987d0SSujith struct ath_common *common = ath9k_hw_common(ah); 928fb9987d0SSujith bool longcal = false; 929fb9987d0SSujith bool shortcal = false; 930fb9987d0SSujith bool aniflag = false; 931fb9987d0SSujith unsigned int timestamp = jiffies_to_msecs(jiffies); 932fb9987d0SSujith u32 cal_interval, short_cal_interval; 933fb9987d0SSujith 934fb9987d0SSujith short_cal_interval = ATH_STA_SHORT_CALINTERVAL; 935fb9987d0SSujith 936bde748a4SVivek Natarajan /* Only calibrate if awake */ 937bde748a4SVivek Natarajan if (ah->power_mode != ATH9K_PM_AWAKE) 938bde748a4SVivek Natarajan goto set_timer; 939bde748a4SVivek Natarajan 940fb9987d0SSujith /* Long calibration runs independently of short calibration. */ 941fb9987d0SSujith if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { 942fb9987d0SSujith longcal = true; 943226afe68SJoe Perches ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); 944fb9987d0SSujith common->ani.longcal_timer = timestamp; 945fb9987d0SSujith } 946fb9987d0SSujith 947fb9987d0SSujith /* Short calibration applies only while caldone is false */ 948fb9987d0SSujith if (!common->ani.caldone) { 949fb9987d0SSujith if ((timestamp - common->ani.shortcal_timer) >= 950fb9987d0SSujith short_cal_interval) { 951fb9987d0SSujith shortcal = true; 952226afe68SJoe Perches ath_dbg(common, ATH_DBG_ANI, 953fb9987d0SSujith "shortcal @%lu\n", jiffies); 954fb9987d0SSujith common->ani.shortcal_timer = timestamp; 955fb9987d0SSujith common->ani.resetcal_timer = timestamp; 956fb9987d0SSujith } 957fb9987d0SSujith } else { 958fb9987d0SSujith if ((timestamp - common->ani.resetcal_timer) >= 959fb9987d0SSujith ATH_RESTART_CALINTERVAL) { 960fb9987d0SSujith common->ani.caldone = ath9k_hw_reset_calvalid(ah); 961fb9987d0SSujith if (common->ani.caldone) 962fb9987d0SSujith common->ani.resetcal_timer = timestamp; 963fb9987d0SSujith } 964fb9987d0SSujith } 965fb9987d0SSujith 966fb9987d0SSujith /* Verify whether we must check ANI */ 967fb9987d0SSujith if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { 968fb9987d0SSujith aniflag = true; 969fb9987d0SSujith common->ani.checkani_timer = timestamp; 970fb9987d0SSujith } 971fb9987d0SSujith 972fb9987d0SSujith /* Skip all processing if there's nothing to do. */ 973fb9987d0SSujith if (longcal || shortcal || aniflag) { 974bde748a4SVivek Natarajan 975bde748a4SVivek Natarajan ath9k_htc_ps_wakeup(priv); 976bde748a4SVivek Natarajan 977fb9987d0SSujith /* Call ANI routine if necessary */ 978fb9987d0SSujith if (aniflag) 979fb9987d0SSujith ath9k_hw_ani_monitor(ah, ah->curchan); 980fb9987d0SSujith 981fb9987d0SSujith /* Perform calibration if necessary */ 98235ecfe03SFelix Fietkau if (longcal || shortcal) 983fb9987d0SSujith common->ani.caldone = 984fb9987d0SSujith ath9k_hw_calibrate(ah, ah->curchan, 985fb9987d0SSujith common->rx_chainmask, 986fb9987d0SSujith longcal); 987fb9987d0SSujith 988bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 989fb9987d0SSujith } 990fb9987d0SSujith 991bde748a4SVivek Natarajan set_timer: 992fb9987d0SSujith /* 993fb9987d0SSujith * Set timer interval based on previous results. 994fb9987d0SSujith * The interval must be the shortest necessary to satisfy ANI, 995fb9987d0SSujith * short calibration and long calibration. 996fb9987d0SSujith */ 997fb9987d0SSujith cal_interval = ATH_LONG_CALINTERVAL; 998fb9987d0SSujith if (priv->ah->config.enable_ani) 999fb9987d0SSujith cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); 1000fb9987d0SSujith if (!common->ani.caldone) 1001fb9987d0SSujith cal_interval = min(cal_interval, (u32)short_cal_interval); 1002fb9987d0SSujith 1003fb9987d0SSujith ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, 1004fb9987d0SSujith msecs_to_jiffies(cal_interval)); 1005fb9987d0SSujith } 1006fb9987d0SSujith 1007fb9987d0SSujith /**********************/ 1008fb9987d0SSujith /* mac80211 Callbacks */ 1009fb9987d0SSujith /**********************/ 1010fb9987d0SSujith 1011fb9987d0SSujith static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) 1012fb9987d0SSujith { 1013fb9987d0SSujith struct ieee80211_hdr *hdr; 1014fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 10157757dfedSSujith int padpos, padsize, ret; 1016fb9987d0SSujith 1017fb9987d0SSujith hdr = (struct ieee80211_hdr *) skb->data; 1018fb9987d0SSujith 1019fb9987d0SSujith /* Add the padding after the header if this is not already done */ 1020fb9987d0SSujith padpos = ath9k_cmn_padpos(hdr->frame_control); 1021fb9987d0SSujith padsize = padpos & 3; 1022fb9987d0SSujith if (padsize && skb->len > padpos) { 1023fb9987d0SSujith if (skb_headroom(skb) < padsize) 1024fb9987d0SSujith return -1; 1025fb9987d0SSujith skb_push(skb, padsize); 1026fb9987d0SSujith memmove(skb->data, skb->data + padsize, padpos); 1027fb9987d0SSujith } 1028fb9987d0SSujith 10297757dfedSSujith ret = ath9k_htc_tx_start(priv, skb); 10307757dfedSSujith if (ret != 0) { 10317757dfedSSujith if (ret == -ENOMEM) { 1032226afe68SJoe Perches ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, 10337757dfedSSujith "Stopping TX queues\n"); 10347757dfedSSujith ieee80211_stop_queues(hw); 10357757dfedSSujith spin_lock_bh(&priv->tx_lock); 10367757dfedSSujith priv->tx_queues_stop = true; 10377757dfedSSujith spin_unlock_bh(&priv->tx_lock); 10387757dfedSSujith } else { 1039226afe68SJoe Perches ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, 1040226afe68SJoe Perches "Tx failed\n"); 10417757dfedSSujith } 1042fb9987d0SSujith goto fail_tx; 1043fb9987d0SSujith } 1044fb9987d0SSujith 1045fb9987d0SSujith return 0; 1046fb9987d0SSujith 1047fb9987d0SSujith fail_tx: 1048fb9987d0SSujith dev_kfree_skb_any(skb); 1049fb9987d0SSujith return 0; 1050fb9987d0SSujith } 1051fb9987d0SSujith 1052881ac6a5SSujith static int ath9k_htc_start(struct ieee80211_hw *hw) 1053fb9987d0SSujith { 1054fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1055fb9987d0SSujith struct ath_hw *ah = priv->ah; 1056fb9987d0SSujith struct ath_common *common = ath9k_hw_common(ah); 1057fb9987d0SSujith struct ieee80211_channel *curchan = hw->conf.channel; 1058fb9987d0SSujith struct ath9k_channel *init_channel; 1059fb9987d0SSujith int ret = 0; 1060fb9987d0SSujith enum htc_phymode mode; 10617f1f5a00SSujith __be16 htc_mode; 1062fb9987d0SSujith u8 cmd_rsp; 1063fb9987d0SSujith 1064881ac6a5SSujith mutex_lock(&priv->mutex); 1065881ac6a5SSujith 1066226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 1067fb9987d0SSujith "Starting driver with initial channel: %d MHz\n", 1068fb9987d0SSujith curchan->center_freq); 1069fb9987d0SSujith 107021d5130bSSujith /* Ensure that HW is awake before flushing RX */ 107121d5130bSSujith ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); 107221d5130bSSujith WMI_CMD(WMI_FLUSH_RECV_CMDID); 107321d5130bSSujith 1074fb9987d0SSujith /* setup initial channel */ 1075fb9987d0SSujith init_channel = ath9k_cmn_get_curchannel(hw, ah); 1076fb9987d0SSujith 1077fb9987d0SSujith ath9k_hw_htc_resetinit(ah); 107820bd2a09SFelix Fietkau ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); 1079fb9987d0SSujith if (ret) { 10803800276aSJoe Perches ath_err(common, 10813800276aSJoe Perches "Unable to reset hardware; reset status %d (freq %u MHz)\n", 10823800276aSJoe Perches ret, curchan->center_freq); 1083881ac6a5SSujith mutex_unlock(&priv->mutex); 10848a8572a8SVivek Natarajan return ret; 1085fb9987d0SSujith } 1086fb9987d0SSujith 1087b2a5c3dfSRajkumar Manoharan ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, 1088b2a5c3dfSRajkumar Manoharan &priv->curtxpow); 1089fb9987d0SSujith 1090fb9987d0SSujith mode = ath9k_htc_get_curmode(priv, init_channel); 1091fb9987d0SSujith htc_mode = cpu_to_be16(mode); 1092fb9987d0SSujith WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); 1093fb9987d0SSujith WMI_CMD(WMI_ATH_INIT_CMDID); 1094fb9987d0SSujith WMI_CMD(WMI_START_RECV_CMDID); 1095fb9987d0SSujith 1096fb9987d0SSujith ath9k_host_rx_init(priv); 1097fb9987d0SSujith 10981057b750SSujith Manoharan ret = ath9k_htc_update_cap_target(priv); 10991057b750SSujith Manoharan if (ret) 11001057b750SSujith Manoharan ath_dbg(common, ATH_DBG_CONFIG, 11011057b750SSujith Manoharan "Failed to update capability in target\n"); 11021057b750SSujith Manoharan 1103fb9987d0SSujith priv->op_flags &= ~OP_INVALID; 1104fb9987d0SSujith htc_start(priv->htc); 1105fb9987d0SSujith 11067757dfedSSujith spin_lock_bh(&priv->tx_lock); 11077757dfedSSujith priv->tx_queues_stop = false; 11087757dfedSSujith spin_unlock_bh(&priv->tx_lock); 11097757dfedSSujith 11107757dfedSSujith ieee80211_wake_queues(hw); 11117757dfedSSujith 111221cb9879SVivek Natarajan if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) { 111321cb9879SVivek Natarajan ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, 111421cb9879SVivek Natarajan AR_STOMP_LOW_WLAN_WGHT); 111521cb9879SVivek Natarajan ath9k_hw_btcoex_enable(ah); 111621cb9879SVivek Natarajan ath_htc_resume_btcoex_work(priv); 111721cb9879SVivek Natarajan } 11188a8572a8SVivek Natarajan mutex_unlock(&priv->mutex); 11198a8572a8SVivek Natarajan 11208a8572a8SVivek Natarajan return ret; 11218a8572a8SVivek Natarajan } 11228a8572a8SVivek Natarajan 1123881ac6a5SSujith static void ath9k_htc_stop(struct ieee80211_hw *hw) 1124fb9987d0SSujith { 1125fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1126fb9987d0SSujith struct ath_hw *ah = priv->ah; 1127fb9987d0SSujith struct ath_common *common = ath9k_hw_common(ah); 1128fb9987d0SSujith int ret = 0; 1129fb9987d0SSujith u8 cmd_rsp; 1130fb9987d0SSujith 1131881ac6a5SSujith mutex_lock(&priv->mutex); 1132881ac6a5SSujith 1133fb9987d0SSujith if (priv->op_flags & OP_INVALID) { 1134226afe68SJoe Perches ath_dbg(common, ATH_DBG_ANY, "Device not present\n"); 1135881ac6a5SSujith mutex_unlock(&priv->mutex); 1136fb9987d0SSujith return; 1137fb9987d0SSujith } 1138fb9987d0SSujith 1139bde748a4SVivek Natarajan ath9k_htc_ps_wakeup(priv); 1140fb9987d0SSujith htc_stop(priv->htc); 1141fb9987d0SSujith WMI_CMD(WMI_DISABLE_INTR_CMDID); 1142fb9987d0SSujith WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 1143fb9987d0SSujith WMI_CMD(WMI_STOP_RECV_CMDID); 1144ea888357SStanislaw Gruszka 1145ea888357SStanislaw Gruszka tasklet_kill(&priv->swba_tasklet); 1146ea888357SStanislaw Gruszka tasklet_kill(&priv->rx_tasklet); 1147ea888357SStanislaw Gruszka tasklet_kill(&priv->tx_tasklet); 1148ea888357SStanislaw Gruszka 1149fb9987d0SSujith skb_queue_purge(&priv->tx_queue); 1150fb9987d0SSujith 1151ea888357SStanislaw Gruszka mutex_unlock(&priv->mutex); 1152ea888357SStanislaw Gruszka 1153ea888357SStanislaw Gruszka /* Cancel all the running timers/work .. */ 1154ea888357SStanislaw Gruszka cancel_work_sync(&priv->fatal_work); 1155ea888357SStanislaw Gruszka cancel_work_sync(&priv->ps_work); 1156ea888357SStanislaw Gruszka cancel_delayed_work_sync(&priv->ath9k_led_blink_work); 115745655baaSRajkumar Manoharan cancel_delayed_work_sync(&priv->ath9k_ani_work); 1158ea888357SStanislaw Gruszka ath9k_led_stop_brightness(priv); 1159ea888357SStanislaw Gruszka 1160ea888357SStanislaw Gruszka mutex_lock(&priv->mutex); 1161ea888357SStanislaw Gruszka 116221cb9879SVivek Natarajan if (ah->btcoex_hw.enabled) { 116321cb9879SVivek Natarajan ath9k_hw_btcoex_disable(ah); 116421cb9879SVivek Natarajan if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) 116521cb9879SVivek Natarajan ath_htc_cancel_btcoex_work(priv); 116621cb9879SVivek Natarajan } 116721cb9879SVivek Natarajan 1168a97b478cSSujith Manoharan /* Remove a monitor interface if it's present. */ 1169a97b478cSSujith Manoharan if (priv->ah->is_monitoring) 1170a97b478cSSujith Manoharan ath9k_htc_remove_monitor_interface(priv); 1171a97b478cSSujith Manoharan 1172e9201f09SSujith ath9k_hw_phy_disable(ah); 1173e9201f09SSujith ath9k_hw_disable(ah); 1174e9201f09SSujith ath9k_htc_ps_restore(priv); 1175e9201f09SSujith ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); 1176e9201f09SSujith 1177fb9987d0SSujith priv->op_flags |= OP_INVALID; 1178fb9987d0SSujith 1179226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n"); 11808a8572a8SVivek Natarajan mutex_unlock(&priv->mutex); 11818a8572a8SVivek Natarajan } 11828a8572a8SVivek Natarajan 1183fb9987d0SSujith static int ath9k_htc_add_interface(struct ieee80211_hw *hw, 1184fb9987d0SSujith struct ieee80211_vif *vif) 1185fb9987d0SSujith { 1186fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1187fb9987d0SSujith struct ath9k_htc_vif *avp = (void *)vif->drv_priv; 1188fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 1189fb9987d0SSujith struct ath9k_htc_target_vif hvif; 1190fb9987d0SSujith int ret = 0; 1191fb9987d0SSujith u8 cmd_rsp; 1192fb9987d0SSujith 1193fb9987d0SSujith mutex_lock(&priv->mutex); 1194fb9987d0SSujith 1195a97b478cSSujith Manoharan if (priv->nvifs >= ATH9K_HTC_MAX_VIF) { 1196fb9987d0SSujith ret = -ENOBUFS; 1197ab77c70aSSujith Manoharan mutex_unlock(&priv->mutex); 1198ab77c70aSSujith Manoharan return ret; 1199fb9987d0SSujith } 1200fb9987d0SSujith 1201bde748a4SVivek Natarajan ath9k_htc_ps_wakeup(priv); 1202fb9987d0SSujith memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); 1203fb9987d0SSujith memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); 1204fb9987d0SSujith 1205fb9987d0SSujith switch (vif->type) { 1206fb9987d0SSujith case NL80211_IFTYPE_STATION: 1207fb9987d0SSujith hvif.opmode = cpu_to_be32(HTC_M_STA); 1208fb9987d0SSujith break; 1209fb9987d0SSujith case NL80211_IFTYPE_ADHOC: 1210fb9987d0SSujith hvif.opmode = cpu_to_be32(HTC_M_IBSS); 1211fb9987d0SSujith break; 1212fb9987d0SSujith default: 12133800276aSJoe Perches ath_err(common, 1214fb9987d0SSujith "Interface type %d not yet supported\n", vif->type); 1215fb9987d0SSujith ret = -EOPNOTSUPP; 1216fb9987d0SSujith goto out; 1217fb9987d0SSujith } 1218fb9987d0SSujith 1219fb9987d0SSujith /* Index starts from zero on the target */ 1220a97b478cSSujith Manoharan avp->index = hvif.index = ffz(priv->vif_slot); 1221fb9987d0SSujith hvif.rtsthreshold = cpu_to_be16(2304); 1222fb9987d0SSujith WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); 1223fb9987d0SSujith if (ret) 1224fb9987d0SSujith goto out; 1225fb9987d0SSujith 1226fb9987d0SSujith /* 1227fb9987d0SSujith * We need a node in target to tx mgmt frames 1228fb9987d0SSujith * before association. 1229fb9987d0SSujith */ 1230fb9987d0SSujith ret = ath9k_htc_add_station(priv, vif, NULL); 1231ab77c70aSSujith Manoharan if (ret) { 1232ab77c70aSSujith Manoharan WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); 1233fb9987d0SSujith goto out; 1234ab77c70aSSujith Manoharan } 1235fb9987d0SSujith 1236585895cdSSujith Manoharan ath9k_htc_set_bssid_mask(priv, vif); 1237585895cdSSujith Manoharan 1238a97b478cSSujith Manoharan priv->ah->opmode = vif->type; 1239a97b478cSSujith Manoharan priv->vif_slot |= (1 << avp->index); 1240ab77c70aSSujith Manoharan priv->nvifs++; 1241fb9987d0SSujith priv->vif = vif; 1242a97b478cSSujith Manoharan 1243a97b478cSSujith Manoharan ath_dbg(common, ATH_DBG_CONFIG, 1244a97b478cSSujith Manoharan "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); 1245a97b478cSSujith Manoharan 1246fb9987d0SSujith out: 1247bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 1248fb9987d0SSujith mutex_unlock(&priv->mutex); 1249cb551df2SSujith 1250fb9987d0SSujith return ret; 1251fb9987d0SSujith } 1252fb9987d0SSujith 1253fb9987d0SSujith static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, 1254fb9987d0SSujith struct ieee80211_vif *vif) 1255fb9987d0SSujith { 1256fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1257fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 1258fb9987d0SSujith struct ath9k_htc_vif *avp = (void *)vif->drv_priv; 1259fb9987d0SSujith struct ath9k_htc_target_vif hvif; 1260fb9987d0SSujith int ret = 0; 1261fb9987d0SSujith u8 cmd_rsp; 1262fb9987d0SSujith 1263fb9987d0SSujith mutex_lock(&priv->mutex); 1264cb551df2SSujith ath9k_htc_ps_wakeup(priv); 1265fb9987d0SSujith 1266fb9987d0SSujith memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); 1267fb9987d0SSujith memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); 1268fb9987d0SSujith hvif.index = avp->index; 1269fb9987d0SSujith WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); 1270fb9987d0SSujith priv->nvifs--; 1271a97b478cSSujith Manoharan priv->vif_slot &= ~(1 << avp->index); 1272fb9987d0SSujith 1273fb9987d0SSujith ath9k_htc_remove_station(priv, vif, NULL); 1274fb9987d0SSujith priv->vif = NULL; 1275fb9987d0SSujith 1276a97b478cSSujith Manoharan ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index); 1277a97b478cSSujith Manoharan 1278cb551df2SSujith ath9k_htc_ps_restore(priv); 1279fb9987d0SSujith mutex_unlock(&priv->mutex); 1280fb9987d0SSujith } 1281fb9987d0SSujith 1282fb9987d0SSujith static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) 1283fb9987d0SSujith { 1284fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1285fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 1286fb9987d0SSujith struct ieee80211_conf *conf = &hw->conf; 1287fb9987d0SSujith 1288fb9987d0SSujith mutex_lock(&priv->mutex); 1289fb9987d0SSujith 12908a8572a8SVivek Natarajan if (changed & IEEE80211_CONF_CHANGE_IDLE) { 12918a8572a8SVivek Natarajan bool enable_radio = false; 12928a8572a8SVivek Natarajan bool idle = !!(conf->flags & IEEE80211_CONF_IDLE); 12938a8572a8SVivek Natarajan 129423367769SSujith mutex_lock(&priv->htc_pm_lock); 12958a8572a8SVivek Natarajan if (!idle && priv->ps_idle) 12968a8572a8SVivek Natarajan enable_radio = true; 12978a8572a8SVivek Natarajan priv->ps_idle = idle; 129823367769SSujith mutex_unlock(&priv->htc_pm_lock); 12998a8572a8SVivek Natarajan 13008a8572a8SVivek Natarajan if (enable_radio) { 1301226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 13028a8572a8SVivek Natarajan "not-idle: enabling radio\n"); 130323367769SSujith ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); 130423367769SSujith ath9k_htc_radio_enable(hw); 13058a8572a8SVivek Natarajan } 13068a8572a8SVivek Natarajan } 13078a8572a8SVivek Natarajan 130855de80d6SSujith Manoharan /* 130955de80d6SSujith Manoharan * Monitor interface should be added before 131055de80d6SSujith Manoharan * IEEE80211_CONF_CHANGE_CHANNEL is handled. 131155de80d6SSujith Manoharan */ 131255de80d6SSujith Manoharan if (changed & IEEE80211_CONF_CHANGE_MONITOR) { 1313a97b478cSSujith Manoharan if ((conf->flags & IEEE80211_CONF_MONITOR) && 1314a97b478cSSujith Manoharan !priv->ah->is_monitoring) 1315a97b478cSSujith Manoharan ath9k_htc_add_monitor_interface(priv); 1316a97b478cSSujith Manoharan else if (priv->ah->is_monitoring) 1317a97b478cSSujith Manoharan ath9k_htc_remove_monitor_interface(priv); 131855de80d6SSujith Manoharan } 131955de80d6SSujith Manoharan 1320fb9987d0SSujith if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { 1321fb9987d0SSujith struct ieee80211_channel *curchan = hw->conf.channel; 1322fb9987d0SSujith int pos = curchan->hw_value; 1323fb9987d0SSujith 1324226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", 1325fb9987d0SSujith curchan->center_freq); 1326fb9987d0SSujith 1327babcbc29SFelix Fietkau ath9k_cmn_update_ichannel(&priv->ah->channels[pos], 1328babcbc29SFelix Fietkau hw->conf.channel, 1329babcbc29SFelix Fietkau hw->conf.channel_type); 1330fb9987d0SSujith 1331fb9987d0SSujith if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { 13323800276aSJoe Perches ath_err(common, "Unable to set channel\n"); 1333fb9987d0SSujith mutex_unlock(&priv->mutex); 1334fb9987d0SSujith return -EINVAL; 1335fb9987d0SSujith } 1336fb9987d0SSujith 1337fb9987d0SSujith } 1338692d6b17SSujith Manoharan 1339bde748a4SVivek Natarajan if (changed & IEEE80211_CONF_CHANGE_PS) { 1340bde748a4SVivek Natarajan if (conf->flags & IEEE80211_CONF_PS) { 1341bde748a4SVivek Natarajan ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); 1342bde748a4SVivek Natarajan priv->ps_enabled = true; 1343bde748a4SVivek Natarajan } else { 1344bde748a4SVivek Natarajan priv->ps_enabled = false; 1345bde748a4SVivek Natarajan cancel_work_sync(&priv->ps_work); 1346bde748a4SVivek Natarajan ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); 1347bde748a4SVivek Natarajan } 1348bde748a4SVivek Natarajan } 1349fb9987d0SSujith 1350692d6b17SSujith Manoharan if (changed & IEEE80211_CONF_CHANGE_POWER) { 1351692d6b17SSujith Manoharan priv->txpowlimit = 2 * conf->power_level; 1352b2a5c3dfSRajkumar Manoharan ath9k_cmn_update_txpow(priv->ah, priv->curtxpow, 1353b2a5c3dfSRajkumar Manoharan priv->txpowlimit, &priv->curtxpow); 1354692d6b17SSujith Manoharan } 1355692d6b17SSujith Manoharan 135623367769SSujith if (changed & IEEE80211_CONF_CHANGE_IDLE) { 135723367769SSujith mutex_lock(&priv->htc_pm_lock); 135823367769SSujith if (!priv->ps_idle) { 135923367769SSujith mutex_unlock(&priv->htc_pm_lock); 136023367769SSujith goto out; 136123367769SSujith } 136223367769SSujith mutex_unlock(&priv->htc_pm_lock); 136323367769SSujith 1364226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 13658a8572a8SVivek Natarajan "idle: disabling radio\n"); 1366881ac6a5SSujith ath9k_htc_radio_disable(hw); 13678a8572a8SVivek Natarajan } 13688a8572a8SVivek Natarajan 136923367769SSujith out: 1370fb9987d0SSujith mutex_unlock(&priv->mutex); 1371fb9987d0SSujith return 0; 1372fb9987d0SSujith } 1373fb9987d0SSujith 1374fb9987d0SSujith #define SUPPORTED_FILTERS \ 1375fb9987d0SSujith (FIF_PROMISC_IN_BSS | \ 1376fb9987d0SSujith FIF_ALLMULTI | \ 1377fb9987d0SSujith FIF_CONTROL | \ 1378fb9987d0SSujith FIF_PSPOLL | \ 1379fb9987d0SSujith FIF_OTHER_BSS | \ 1380fb9987d0SSujith FIF_BCN_PRBRESP_PROMISC | \ 138194a40c0cSRajkumar Manoharan FIF_PROBE_REQ | \ 1382fb9987d0SSujith FIF_FCSFAIL) 1383fb9987d0SSujith 1384fb9987d0SSujith static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, 1385fb9987d0SSujith unsigned int changed_flags, 1386fb9987d0SSujith unsigned int *total_flags, 1387fb9987d0SSujith u64 multicast) 1388fb9987d0SSujith { 1389fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1390fb9987d0SSujith u32 rfilt; 1391fb9987d0SSujith 1392fb9987d0SSujith mutex_lock(&priv->mutex); 1393bde748a4SVivek Natarajan ath9k_htc_ps_wakeup(priv); 1394cb551df2SSujith 1395fb9987d0SSujith changed_flags &= SUPPORTED_FILTERS; 1396fb9987d0SSujith *total_flags &= SUPPORTED_FILTERS; 1397fb9987d0SSujith 1398fb9987d0SSujith priv->rxfilter = *total_flags; 13990995d110SSujith rfilt = ath9k_htc_calcrxfilter(priv); 1400fb9987d0SSujith ath9k_hw_setrxfilter(priv->ah, rfilt); 1401fb9987d0SSujith 1402226afe68SJoe Perches ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG, 1403fb9987d0SSujith "Set HW RX filter: 0x%x\n", rfilt); 1404fb9987d0SSujith 1405bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 1406fb9987d0SSujith mutex_unlock(&priv->mutex); 1407fb9987d0SSujith } 1408fb9987d0SSujith 1409abd984e6SSujith static int ath9k_htc_sta_add(struct ieee80211_hw *hw, 1410fb9987d0SSujith struct ieee80211_vif *vif, 1411fb9987d0SSujith struct ieee80211_sta *sta) 1412fb9987d0SSujith { 1413fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1414fb9987d0SSujith int ret; 1415fb9987d0SSujith 141605a30f9cSSujith.Manoharan@atheros.com mutex_lock(&priv->mutex); 1417cb551df2SSujith ath9k_htc_ps_wakeup(priv); 1418fb9987d0SSujith ret = ath9k_htc_add_station(priv, vif, sta); 1419fb9987d0SSujith if (!ret) 14200d425a7dSSujith ath9k_htc_init_rate(priv, sta); 1421cb551df2SSujith ath9k_htc_ps_restore(priv); 142205a30f9cSSujith.Manoharan@atheros.com mutex_unlock(&priv->mutex); 1423abd984e6SSujith 1424abd984e6SSujith return ret; 1425abd984e6SSujith } 1426abd984e6SSujith 1427abd984e6SSujith static int ath9k_htc_sta_remove(struct ieee80211_hw *hw, 1428abd984e6SSujith struct ieee80211_vif *vif, 1429abd984e6SSujith struct ieee80211_sta *sta) 1430abd984e6SSujith { 1431abd984e6SSujith struct ath9k_htc_priv *priv = hw->priv; 1432abd984e6SSujith int ret; 1433abd984e6SSujith 1434abd984e6SSujith mutex_lock(&priv->mutex); 1435abd984e6SSujith ath9k_htc_ps_wakeup(priv); 1436abd984e6SSujith ret = ath9k_htc_remove_station(priv, vif, sta); 1437abd984e6SSujith ath9k_htc_ps_restore(priv); 1438abd984e6SSujith mutex_unlock(&priv->mutex); 1439abd984e6SSujith 1440abd984e6SSujith return ret; 1441fb9987d0SSujith } 1442fb9987d0SSujith 1443fb9987d0SSujith static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, 1444fb9987d0SSujith const struct ieee80211_tx_queue_params *params) 1445fb9987d0SSujith { 1446fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1447fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 1448fb9987d0SSujith struct ath9k_tx_queue_info qi; 1449fb9987d0SSujith int ret = 0, qnum; 1450fb9987d0SSujith 1451fb9987d0SSujith if (queue >= WME_NUM_AC) 1452fb9987d0SSujith return 0; 1453fb9987d0SSujith 1454fb9987d0SSujith mutex_lock(&priv->mutex); 1455cb551df2SSujith ath9k_htc_ps_wakeup(priv); 1456fb9987d0SSujith 1457fb9987d0SSujith memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); 1458fb9987d0SSujith 1459fb9987d0SSujith qi.tqi_aifs = params->aifs; 1460fb9987d0SSujith qi.tqi_cwmin = params->cw_min; 1461fb9987d0SSujith qi.tqi_cwmax = params->cw_max; 1462fb9987d0SSujith qi.tqi_burstTime = params->txop; 1463fb9987d0SSujith 1464fb9987d0SSujith qnum = get_hw_qnum(queue, priv->hwq_map); 1465fb9987d0SSujith 1466226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 1467226afe68SJoe Perches "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", 1468fb9987d0SSujith queue, qnum, params->aifs, params->cw_min, 1469fb9987d0SSujith params->cw_max, params->txop); 1470fb9987d0SSujith 1471e1572c5eSSujith ret = ath_htc_txq_update(priv, qnum, &qi); 1472764580f5SSujith if (ret) { 14733800276aSJoe Perches ath_err(common, "TXQ Update failed\n"); 1474764580f5SSujith goto out; 1475764580f5SSujith } 1476fb9987d0SSujith 1477764580f5SSujith if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) && 1478e8c35a77SFelix Fietkau (qnum == priv->hwq_map[WME_AC_BE])) 1479764580f5SSujith ath9k_htc_beaconq_config(priv); 1480764580f5SSujith out: 1481cb551df2SSujith ath9k_htc_ps_restore(priv); 1482fb9987d0SSujith mutex_unlock(&priv->mutex); 1483fb9987d0SSujith 1484fb9987d0SSujith return ret; 1485fb9987d0SSujith } 1486fb9987d0SSujith 1487fb9987d0SSujith static int ath9k_htc_set_key(struct ieee80211_hw *hw, 1488fb9987d0SSujith enum set_key_cmd cmd, 1489fb9987d0SSujith struct ieee80211_vif *vif, 1490fb9987d0SSujith struct ieee80211_sta *sta, 1491fb9987d0SSujith struct ieee80211_key_conf *key) 1492fb9987d0SSujith { 1493fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1494fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah); 1495fb9987d0SSujith int ret = 0; 1496fb9987d0SSujith 1497e1572c5eSSujith if (htc_modparam_nohwcrypt) 1498fb9987d0SSujith return -ENOSPC; 1499fb9987d0SSujith 1500fb9987d0SSujith mutex_lock(&priv->mutex); 1501226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n"); 1502bde748a4SVivek Natarajan ath9k_htc_ps_wakeup(priv); 1503fb9987d0SSujith 1504fb9987d0SSujith switch (cmd) { 1505fb9987d0SSujith case SET_KEY: 1506040e539eSBruno Randolf ret = ath_key_config(common, vif, sta, key); 1507fb9987d0SSujith if (ret >= 0) { 1508fb9987d0SSujith key->hw_key_idx = ret; 1509fb9987d0SSujith /* push IV and Michael MIC generation to stack */ 1510fb9987d0SSujith key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; 151197359d12SJohannes Berg if (key->cipher == WLAN_CIPHER_SUITE_TKIP) 1512fb9987d0SSujith key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; 151397359d12SJohannes Berg if (priv->ah->sw_mgmt_crypto && 151497359d12SJohannes Berg key->cipher == WLAN_CIPHER_SUITE_CCMP) 1515fb9987d0SSujith key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; 1516fb9987d0SSujith ret = 0; 1517fb9987d0SSujith } 1518fb9987d0SSujith break; 1519fb9987d0SSujith case DISABLE_KEY: 1520040e539eSBruno Randolf ath_key_delete(common, key); 1521fb9987d0SSujith break; 1522fb9987d0SSujith default: 1523fb9987d0SSujith ret = -EINVAL; 1524fb9987d0SSujith } 1525fb9987d0SSujith 1526bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 1527fb9987d0SSujith mutex_unlock(&priv->mutex); 1528fb9987d0SSujith 1529fb9987d0SSujith return ret; 1530fb9987d0SSujith } 1531fb9987d0SSujith 1532fb9987d0SSujith static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, 1533fb9987d0SSujith struct ieee80211_vif *vif, 1534fb9987d0SSujith struct ieee80211_bss_conf *bss_conf, 1535fb9987d0SSujith u32 changed) 1536fb9987d0SSujith { 1537fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1538fb9987d0SSujith struct ath_hw *ah = priv->ah; 1539fb9987d0SSujith struct ath_common *common = ath9k_hw_common(ah); 1540fb9987d0SSujith 1541fb9987d0SSujith mutex_lock(&priv->mutex); 1542bde748a4SVivek Natarajan ath9k_htc_ps_wakeup(priv); 1543fb9987d0SSujith 1544fb9987d0SSujith if (changed & BSS_CHANGED_ASSOC) { 1545fb9987d0SSujith common->curaid = bss_conf->assoc ? 1546fb9987d0SSujith bss_conf->aid : 0; 1547226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", 1548fb9987d0SSujith bss_conf->assoc); 1549fb9987d0SSujith 15507c277349SSujith Manoharan if (bss_conf->assoc) 1551fb9987d0SSujith ath_start_ani(priv); 15527c277349SSujith Manoharan else 1553fb9987d0SSujith cancel_delayed_work_sync(&priv->ath9k_ani_work); 1554fb9987d0SSujith } 1555fb9987d0SSujith 1556fb9987d0SSujith if (changed & BSS_CHANGED_BSSID) { 1557fb9987d0SSujith /* Set BSSID */ 1558fb9987d0SSujith memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); 1559fb9987d0SSujith ath9k_hw_write_associd(ah); 1560fb9987d0SSujith 1561226afe68SJoe Perches ath_dbg(common, ATH_DBG_CONFIG, 1562fb9987d0SSujith "BSSID: %pM aid: 0x%x\n", 1563fb9987d0SSujith common->curbssid, common->curaid); 1564fb9987d0SSujith } 1565fb9987d0SSujith 1566fb9987d0SSujith if ((changed & BSS_CHANGED_BEACON_INT) || 1567fb9987d0SSujith (changed & BSS_CHANGED_BEACON) || 1568fb9987d0SSujith ((changed & BSS_CHANGED_BEACON_ENABLED) && 1569fb9987d0SSujith bss_conf->enable_beacon)) { 1570fb9987d0SSujith priv->op_flags |= OP_ENABLE_BEACON; 15711c3652a5SVivek Natarajan ath9k_htc_beacon_config(priv, vif); 1572fb9987d0SSujith } 1573fb9987d0SSujith 1574fb9987d0SSujith if ((changed & BSS_CHANGED_BEACON_ENABLED) && 1575fb9987d0SSujith !bss_conf->enable_beacon) { 1576fb9987d0SSujith priv->op_flags &= ~OP_ENABLE_BEACON; 15771c3652a5SVivek Natarajan ath9k_htc_beacon_config(priv, vif); 1578fb9987d0SSujith } 1579fb9987d0SSujith 1580fb9987d0SSujith if (changed & BSS_CHANGED_ERP_SLOT) { 1581fb9987d0SSujith if (bss_conf->use_short_slot) 1582fb9987d0SSujith ah->slottime = 9; 1583fb9987d0SSujith else 1584fb9987d0SSujith ah->slottime = 20; 1585fb9987d0SSujith 1586fb9987d0SSujith ath9k_hw_init_global_settings(ah); 1587fb9987d0SSujith } 1588fb9987d0SSujith 15892c76ef89SSujith if (changed & BSS_CHANGED_HT) 15902c76ef89SSujith ath9k_htc_update_rate(priv, vif, bss_conf); 15912c76ef89SSujith 1592bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 1593fb9987d0SSujith mutex_unlock(&priv->mutex); 1594fb9987d0SSujith } 1595fb9987d0SSujith 1596fb9987d0SSujith static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) 1597fb9987d0SSujith { 1598fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1599fb9987d0SSujith u64 tsf; 1600fb9987d0SSujith 1601fb9987d0SSujith mutex_lock(&priv->mutex); 1602cb551df2SSujith ath9k_htc_ps_wakeup(priv); 1603fb9987d0SSujith tsf = ath9k_hw_gettsf64(priv->ah); 1604cb551df2SSujith ath9k_htc_ps_restore(priv); 1605fb9987d0SSujith mutex_unlock(&priv->mutex); 1606fb9987d0SSujith 1607fb9987d0SSujith return tsf; 1608fb9987d0SSujith } 1609fb9987d0SSujith 1610fb9987d0SSujith static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) 1611fb9987d0SSujith { 1612fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1613fb9987d0SSujith 1614fb9987d0SSujith mutex_lock(&priv->mutex); 1615cb551df2SSujith ath9k_htc_ps_wakeup(priv); 1616fb9987d0SSujith ath9k_hw_settsf64(priv->ah, tsf); 1617cb551df2SSujith ath9k_htc_ps_restore(priv); 1618fb9987d0SSujith mutex_unlock(&priv->mutex); 1619fb9987d0SSujith } 1620fb9987d0SSujith 1621fb9987d0SSujith static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw) 1622fb9987d0SSujith { 1623fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1624fb9987d0SSujith 1625fb9987d0SSujith mutex_lock(&priv->mutex); 1626cb551df2SSujith ath9k_htc_ps_wakeup(priv); 1627fb9987d0SSujith ath9k_hw_reset_tsf(priv->ah); 1628bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 1629cb551df2SSujith mutex_unlock(&priv->mutex); 1630fb9987d0SSujith } 1631fb9987d0SSujith 1632fb9987d0SSujith static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, 1633fb9987d0SSujith struct ieee80211_vif *vif, 1634fb9987d0SSujith enum ieee80211_ampdu_mlme_action action, 1635fb9987d0SSujith struct ieee80211_sta *sta, 16360b01f030SJohannes Berg u16 tid, u16 *ssn, u8 buf_size) 1637fb9987d0SSujith { 1638fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1639fb9987d0SSujith struct ath9k_htc_sta *ista; 1640d7ca2139SSujith int ret = 0; 1641fb9987d0SSujith 1642*87df8957SSujith Manoharan mutex_lock(&priv->mutex); 1643*87df8957SSujith Manoharan 1644fb9987d0SSujith switch (action) { 1645fb9987d0SSujith case IEEE80211_AMPDU_RX_START: 1646fb9987d0SSujith break; 1647fb9987d0SSujith case IEEE80211_AMPDU_RX_STOP: 1648fb9987d0SSujith break; 1649fb9987d0SSujith case IEEE80211_AMPDU_TX_START: 1650d7ca2139SSujith ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); 1651d7ca2139SSujith if (!ret) 1652d7ca2139SSujith ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); 1653d7ca2139SSujith break; 1654fb9987d0SSujith case IEEE80211_AMPDU_TX_STOP: 1655d7ca2139SSujith ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); 1656d7ca2139SSujith ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 1657fb9987d0SSujith break; 1658fb9987d0SSujith case IEEE80211_AMPDU_TX_OPERATIONAL: 1659fb9987d0SSujith ista = (struct ath9k_htc_sta *) sta->drv_priv; 1660d7ca2139SSujith spin_lock_bh(&priv->tx_lock); 1661fb9987d0SSujith ista->tid_state[tid] = AGGR_OPERATIONAL; 1662d7ca2139SSujith spin_unlock_bh(&priv->tx_lock); 1663fb9987d0SSujith break; 1664fb9987d0SSujith default: 16653800276aSJoe Perches ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); 1666fb9987d0SSujith } 1667fb9987d0SSujith 1668*87df8957SSujith Manoharan mutex_unlock(&priv->mutex); 1669*87df8957SSujith Manoharan 1670d7ca2139SSujith return ret; 1671fb9987d0SSujith } 1672fb9987d0SSujith 1673fb9987d0SSujith static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) 1674fb9987d0SSujith { 1675fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1676fb9987d0SSujith 1677fb9987d0SSujith mutex_lock(&priv->mutex); 1678fb9987d0SSujith spin_lock_bh(&priv->beacon_lock); 1679fb9987d0SSujith priv->op_flags |= OP_SCANNING; 1680fb9987d0SSujith spin_unlock_bh(&priv->beacon_lock); 1681bde748a4SVivek Natarajan cancel_work_sync(&priv->ps_work); 1682fb9987d0SSujith cancel_delayed_work_sync(&priv->ath9k_ani_work); 1683fb9987d0SSujith mutex_unlock(&priv->mutex); 1684fb9987d0SSujith } 1685fb9987d0SSujith 1686fb9987d0SSujith static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) 1687fb9987d0SSujith { 1688fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1689fb9987d0SSujith 1690fb9987d0SSujith mutex_lock(&priv->mutex); 1691fb9987d0SSujith spin_lock_bh(&priv->beacon_lock); 1692fb9987d0SSujith priv->op_flags &= ~OP_SCANNING; 1693fb9987d0SSujith spin_unlock_bh(&priv->beacon_lock); 16947c277349SSujith Manoharan ath9k_htc_ps_wakeup(priv); 16957c277349SSujith Manoharan ath9k_htc_vif_reconfig(priv); 1696bde748a4SVivek Natarajan ath9k_htc_ps_restore(priv); 1697cb551df2SSujith mutex_unlock(&priv->mutex); 1698fb9987d0SSujith } 1699fb9987d0SSujith 1700fb9987d0SSujith static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) 1701fb9987d0SSujith { 1702fb9987d0SSujith return 0; 1703fb9987d0SSujith } 1704fb9987d0SSujith 1705fb9987d0SSujith static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw, 1706fb9987d0SSujith u8 coverage_class) 1707fb9987d0SSujith { 1708fb9987d0SSujith struct ath9k_htc_priv *priv = hw->priv; 1709fb9987d0SSujith 1710fb9987d0SSujith mutex_lock(&priv->mutex); 1711cb551df2SSujith ath9k_htc_ps_wakeup(priv); 1712fb9987d0SSujith priv->ah->coverage_class = coverage_class; 1713fb9987d0SSujith ath9k_hw_init_global_settings(priv->ah); 1714cb551df2SSujith ath9k_htc_ps_restore(priv); 1715fb9987d0SSujith mutex_unlock(&priv->mutex); 1716fb9987d0SSujith } 1717fb9987d0SSujith 1718fb9987d0SSujith struct ieee80211_ops ath9k_htc_ops = { 1719fb9987d0SSujith .tx = ath9k_htc_tx, 1720fb9987d0SSujith .start = ath9k_htc_start, 1721fb9987d0SSujith .stop = ath9k_htc_stop, 1722fb9987d0SSujith .add_interface = ath9k_htc_add_interface, 1723fb9987d0SSujith .remove_interface = ath9k_htc_remove_interface, 1724fb9987d0SSujith .config = ath9k_htc_config, 1725fb9987d0SSujith .configure_filter = ath9k_htc_configure_filter, 1726abd984e6SSujith .sta_add = ath9k_htc_sta_add, 1727abd984e6SSujith .sta_remove = ath9k_htc_sta_remove, 1728fb9987d0SSujith .conf_tx = ath9k_htc_conf_tx, 1729fb9987d0SSujith .bss_info_changed = ath9k_htc_bss_info_changed, 1730fb9987d0SSujith .set_key = ath9k_htc_set_key, 1731fb9987d0SSujith .get_tsf = ath9k_htc_get_tsf, 1732fb9987d0SSujith .set_tsf = ath9k_htc_set_tsf, 1733fb9987d0SSujith .reset_tsf = ath9k_htc_reset_tsf, 1734fb9987d0SSujith .ampdu_action = ath9k_htc_ampdu_action, 1735fb9987d0SSujith .sw_scan_start = ath9k_htc_sw_scan_start, 1736fb9987d0SSujith .sw_scan_complete = ath9k_htc_sw_scan_complete, 1737fb9987d0SSujith .set_rts_threshold = ath9k_htc_set_rts_threshold, 1738fb9987d0SSujith .rfkill_poll = ath9k_htc_rfkill_poll_state, 1739fb9987d0SSujith .set_coverage_class = ath9k_htc_set_coverage_class, 1740fb9987d0SSujith }; 1741