static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret = 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ista = (struct ath9k_htc_sta *) sta->drv_priv; spin_lock_bh(&priv->tx.tx_lock); ista->tid_state[tid] = AGGR_OPERATIONAL; spin_unlock_bh(&priv->tx.tx_lock); break; default: ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static int ath9k_htc_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); int ret = 0; if (htc_modparam_nohwcrypt) return -ENOSPC; mutex_lock(&priv->mutex); ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n"); ath9k_htc_ps_wakeup(priv); switch (cmd) { case SET_KEY: ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (priv->ah->sw_mgmt_crypto && key->cipher == WLAN_CIPHER_SUITE_CCMP) key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; ret = 0; } break; case DISABLE_KEY: ath_key_delete(common, key); break; default: ret = -EINVAL; } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_tx_queue_info qi; int ret = 0, qnum; if (queue >= IEEE80211_NUM_ACS) return 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); qi.tqi_aifs = params->aifs; qi.tqi_cwmin = params->cw_min; qi.tqi_cwmax = params->cw_max; qi.tqi_burstTime = params->txop * 32; qnum = get_hw_qnum(queue, priv->hwq_map); ath_dbg(common, CONFIG, "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", queue, qnum, params->aifs, params->cw_min, params->cw_max, params->txop); ret = ath_htc_txq_update(priv, qnum, &qi); if (ret) { ath_err(common, "TXQ Update failed\n"); goto out; } if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) && (qnum == priv->hwq_map[IEEE80211_AC_BE])) ath9k_htc_beaconq_config(priv); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static int ath9k_htc_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; int ret; cancel_work_sync(&ista->rc_update_work); mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); htc_sta_drain(priv->htc, ista->index); ret = ath9k_htc_remove_station(priv, vif, sta); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); hvif.index = avp->index; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); priv->nvifs--; priv->vif_slot &= ~(1 << avp->index); ath9k_htc_remove_station(priv, vif, NULL); priv->vif = NULL; DEC_VIF(priv, vif->type); ath9k_htc_set_opmode(priv); /* * Stop ANI only if there are no associated station interfaces. */ if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) { priv->rearm_ani = false; ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_vif_iter, priv); if (!priv->rearm_ani) ath9k_htc_stop_ani(priv); } ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); }
static int ath9k_htc_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; int ret; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); ret = ath9k_htc_add_station(priv, vif, sta); if (!ret) { INIT_WORK(&ista->rc_update_work, ath9k_htc_sta_rc_update_work); ista->htc_priv = priv; ath9k_htc_init_rate(priv, sta); } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct ath9k_htc_priv *priv = hw->priv; u32 rfilt; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); changed_flags &= SUPPORTED_FILTERS; *total_flags &= SUPPORTED_FILTERS; priv->rxfilter = *total_flags; rfilt = ath9k_htc_calcrxfilter(priv); ath9k_hw_setrxfilter(priv->ah, rfilt); ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", rfilt); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); }
static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); int slottime; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & BSS_CHANGED_ASSOC) { ath_dbg(common, CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); bss_conf->assoc ? priv->num_sta_assoc_vif++ : priv->num_sta_assoc_vif--; if (!bss_conf->assoc) clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); if (priv->ah->opmode == NL80211_IFTYPE_STATION) { ath9k_htc_choose_set_bssid(priv); if (bss_conf->assoc && (priv->num_sta_assoc_vif == 1)) ath9k_htc_start_ani(priv); else if (priv->num_sta_assoc_vif == 0) ath9k_htc_stop_ani(priv); } } if (changed & BSS_CHANGED_IBSS) { if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { common->curaid = bss_conf->aid; memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); ath9k_htc_set_bssid(priv); } } if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) { ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n", bss_conf->bssid); ath9k_htc_set_tsfadjust(priv, vif); priv->cur_beacon_conf.enable_beacon = 1; ath9k_htc_beacon_config(priv, vif); } if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { /* * Disable SWBA interrupt only if there are no * concurrent AP/mesh or IBSS interfaces. */ if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) || priv->num_ibss_vif) { ath_dbg(common, CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); priv->cur_beacon_conf.enable_beacon = 0; ath9k_htc_beacon_config(priv, vif); } } if (changed & BSS_CHANGED_BEACON_INT) { /* * Reset the HW TSF for the first AP or mesh interface. */ if (priv->nvifs == 1 && ((priv->ah->opmode == NL80211_IFTYPE_AP && vif->type == NL80211_IFTYPE_AP && priv->num_ap_vif == 1) || (priv->ah->opmode == NL80211_IFTYPE_MESH_POINT && vif->type == NL80211_IFTYPE_MESH_POINT && priv->num_mbss_vif == 1))) { set_bit(OP_TSF_RESET, &priv->op_flags); } ath_dbg(common, CONFIG, "Beacon interval changed for BSS: %pM\n", bss_conf->bssid); ath9k_htc_beacon_config(priv, vif); } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) slottime = 9; else slottime = 20; if (vif->type == NL80211_IFTYPE_AP) { /* * Defer update, so that connected stations can adjust * their settings at the same time. * See beacon.c for more details */ priv->beacon.slottime = slottime; priv->beacon.updateslot = UPDATE; } else { ah->slottime = slottime; ath9k_hw_init_global_settings(ah); } } if (changed & BSS_CHANGED_HT) ath9k_htc_update_rate(priv, vif, bss_conf); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); }
static void ath9k_htc_stop(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); int ret __attribute__ ((unused)); u8 cmd_rsp; mutex_lock(&priv->mutex); if (priv->op_flags & OP_INVALID) { ath_dbg(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&priv->mutex); return; } ath9k_htc_ps_wakeup(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); tasklet_kill(&priv->rx_tasklet); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); ath9k_wmi_event_drain(priv); mutex_unlock(&priv->mutex); /* Cancel all the running timers/work .. */ cancel_work_sync(&priv->fatal_work); cancel_work_sync(&priv->ps_work); #ifdef CONFIG_MAC80211_LEDS cancel_work_sync(&priv->led_work); #endif ath9k_htc_stop_ani(priv); mutex_lock(&priv->mutex); if (ah->btcoex_hw.enabled) { ath9k_hw_btcoex_disable(ah); if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) ath_htc_cancel_btcoex_work(priv); } /* Remove a monitor interface if it's present. */ if (priv->ah->is_monitoring) ath9k_htc_remove_monitor_interface(priv); ath9k_hw_phy_disable(ah); ath9k_hw_disable(ah); ath9k_htc_ps_restore(priv); ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); priv->op_flags |= OP_INVALID; ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n"); mutex_unlock(&priv->mutex); }
static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_conf *conf = &hw->conf; bool chip_reset = false; int ret = 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & IEEE80211_CONF_CHANGE_IDLE) { mutex_lock(&priv->htc_pm_lock); priv->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); if (!priv->ps_idle) chip_reset = true; mutex_unlock(&priv->htc_pm_lock); } /* * Monitor interface should be added before * IEEE80211_CONF_CHANGE_CHANNEL is handled. */ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if ((conf->flags & IEEE80211_CONF_MONITOR) && !priv->ah->is_monitoring) ath9k_htc_add_monitor_interface(priv); else if (priv->ah->is_monitoring) ath9k_htc_remove_monitor_interface(priv); } if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) { struct ieee80211_channel *curchan = hw->conf.chandef.chan; int pos = curchan->hw_value; ath_dbg(common, CONFIG, "Set channel: %d MHz\n", curchan->center_freq); ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); ret = -EINVAL; goto out; } } if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); priv->ps_enabled = true; } else { priv->ps_enabled = false; cancel_work_sync(&priv->ps_work); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); } } if (changed & IEEE80211_CONF_CHANGE_POWER) { priv->txpowlimit = 2 * conf->power_level; ath9k_cmn_update_txpow(priv->ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); } out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static int ath9k_htc_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); switch (vif->type) { case NL80211_IFTYPE_STATION: hvif.opmode = HTC_M_STA; break; case NL80211_IFTYPE_ADHOC: hvif.opmode = HTC_M_IBSS; break; case NL80211_IFTYPE_AP: hvif.opmode = HTC_M_HOSTAP; break; case NL80211_IFTYPE_MESH_POINT: hvif.opmode = HTC_M_WDS; /* close enough */ break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } /* Index starts from zero on the target */ avp->index = hvif.index = ffz(priv->vif_slot); hvif.rtsthreshold = cpu_to_be16(2304); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto out; /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); if (ret) { WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); goto out; } ath9k_htc_set_mac_bssid_mask(priv, vif); priv->vif_slot |= (1 << avp->index); priv->nvifs++; INC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_MESH_POINT) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_assign_bslot(priv, vif); ath9k_htc_set_opmode(priv); if ((priv->ah->opmode == NL80211_IFTYPE_AP) && !test_bit(ATH_OP_ANI_RUN, &common->op_flags)) { ath9k_hw_set_tsfadjust(priv->ah, true); ath9k_htc_start_ani(priv); } ath_dbg(common, CONFIG, "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static ssize_t read_file_tgt_tx_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath9k_htc_target_tx_stats cmd_rsp; char buf[512]; unsigned int len = 0; int ret = 0; memset(&cmd_rsp, 0, sizeof(cmd_rsp)); ath9k_htc_ps_wakeup(priv); WMI_CMD(WMI_TX_STATS_CMDID); if (ret) { ath9k_htc_ps_restore(priv); return -EINVAL; } ath9k_htc_ps_restore(priv); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Xretries", be32_to_cpu(cmd_rsp.xretries)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "FifoErr", be32_to_cpu(cmd_rsp.fifoerr)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Filtered", be32_to_cpu(cmd_rsp.filtered)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "TimerExp", be32_to_cpu(cmd_rsp.timer_exp)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "ShortRetries", be32_to_cpu(cmd_rsp.shortretries)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "LongRetries", be32_to_cpu(cmd_rsp.longretries)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "QueueNull", be32_to_cpu(cmd_rsp.qnull)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "EncapFail", be32_to_cpu(cmd_rsp.encap_fail)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "NoBuf", be32_to_cpu(cmd_rsp.nobuf)); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); }
static int ath9k_htc_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); /* Only one interface for now */ if (priv->nvifs > 0) { ret = -ENOBUFS; goto out; } ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); switch (vif->type) { case NL80211_IFTYPE_STATION: hvif.opmode = cpu_to_be32(HTC_M_STA); break; case NL80211_IFTYPE_ADHOC: hvif.opmode = cpu_to_be32(HTC_M_IBSS); break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } ath_dbg(common, ATH_DBG_CONFIG, "Attach a VIF of type: %d\n", vif->type); priv->ah->opmode = vif->type; /* Index starts from zero on the target */ avp->index = hvif.index = priv->nvifs; hvif.rtsthreshold = cpu_to_be16(2304); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto out; priv->nvifs++; /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); if (ret) goto out; ret = ath9k_htc_update_cap_target(priv); if (ret) ath_dbg(common, ATH_DBG_CONFIG, "Failed to update capability in target\n"); priv->vif = vif; out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & BSS_CHANGED_ASSOC) { ath_dbg(common, CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); bss_conf->assoc ? priv->num_sta_assoc_vif++ : priv->num_sta_assoc_vif--; if (priv->ah->opmode == NL80211_IFTYPE_STATION) { ath9k_htc_choose_set_bssid(priv); if (bss_conf->assoc && (priv->num_sta_assoc_vif == 1)) ath9k_htc_start_ani(priv); else if (priv->num_sta_assoc_vif == 0) ath9k_htc_stop_ani(priv); } } if (changed & BSS_CHANGED_IBSS) { if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { common->curaid = bss_conf->aid; memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); ath9k_htc_set_bssid(priv); } } if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) { ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n", bss_conf->bssid); ath9k_htc_set_tsfadjust(priv, vif); set_bit(OP_ENABLE_BEACON, &priv->op_flags); ath9k_htc_beacon_config(priv, vif); } if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { /* * Disable SWBA interrupt only if there are no * AP/IBSS interfaces. */ if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { ath_dbg(common, CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); clear_bit(OP_ENABLE_BEACON, &priv->op_flags); ath9k_htc_beacon_config(priv, vif); } } if (changed & BSS_CHANGED_BEACON_INT) { /* * Reset the HW TSF for the first AP interface. */ if ((priv->ah->opmode == NL80211_IFTYPE_AP) && (priv->nvifs == 1) && (priv->num_ap_vif == 1) && (vif->type == NL80211_IFTYPE_AP)) { set_bit(OP_TSF_RESET, &priv->op_flags); } ath_dbg(common, CONFIG, "Beacon interval changed for BSS: %pM\n", bss_conf->bssid); ath9k_htc_beacon_config(priv, vif); } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ah->slottime = 9; else ah->slottime = 20; ath9k_hw_init_global_settings(ah); } if (changed & BSS_CHANGED_HT) ath9k_htc_update_rate(priv, vif, bss_conf); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); }
static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); bool set_assoc; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); /* * Set the HW AID/BSSID only for the first station interface * or in IBSS mode. */ set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) || ((priv->ah->opmode == NL80211_IFTYPE_STATION) && (priv->num_sta_vif == 1))); if (changed & BSS_CHANGED_ASSOC) { if (set_assoc) { ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); common->curaid = bss_conf->assoc ? bss_conf->aid : 0; if (bss_conf->assoc) ath9k_htc_start_ani(priv); else ath9k_htc_stop_ani(priv); } } if (changed & BSS_CHANGED_BSSID) { if (set_assoc) { memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); ath9k_hw_write_associd(ah); ath_dbg(common, ATH_DBG_CONFIG, "BSSID: %pM aid: 0x%x\n", common->curbssid, common->curaid); } } if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) { ath_dbg(common, ATH_DBG_CONFIG, "Beacon enabled for BSS: %pM\n", bss_conf->bssid); priv->op_flags |= OP_ENABLE_BEACON; ath9k_htc_beacon_config(priv, vif); } if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { /* * Disable SWBA interrupt only if there are no * AP/IBSS interfaces. */ if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { ath_dbg(common, ATH_DBG_CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); priv->op_flags &= ~OP_ENABLE_BEACON; ath9k_htc_beacon_config(priv, vif); } } if (changed & BSS_CHANGED_BEACON_INT) { /* * Reset the HW TSF for the first AP interface. */ if ((priv->ah->opmode == NL80211_IFTYPE_AP) && (priv->nvifs == 1) && (priv->num_ap_vif == 1) && (vif->type == NL80211_IFTYPE_AP)) { priv->op_flags |= OP_TSF_RESET; } ath_dbg(common, ATH_DBG_CONFIG, "Beacon interval changed for BSS: %pM\n", bss_conf->bssid); ath9k_htc_beacon_config(priv, vif); } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ah->slottime = 9; else ah->slottime = 20; ath9k_hw_init_global_settings(ah); } if (changed & BSS_CHANGED_HT) ath9k_htc_update_rate(priv, vif, bss_conf); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); }
static void ath9k_htc_op_ps_restore(struct ath_common *common) { ath9k_htc_ps_restore((struct ath9k_htc_priv *) common->priv); }
static int ath9k_htc_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); if (priv->nvifs >= ATH9K_HTC_MAX_VIF) { mutex_unlock(&priv->mutex); return -ENOBUFS; } if (priv->num_ibss_vif || (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { ath_err(common, "IBSS coexistence with other modes is not allowed\n"); mutex_unlock(&priv->mutex); return -ENOBUFS; } if (((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC)) && ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) { ath_err(common, "Max. number of beaconing interfaces reached\n"); mutex_unlock(&priv->mutex); return -ENOBUFS; } ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); switch (vif->type) { case NL80211_IFTYPE_STATION: hvif.opmode = HTC_M_STA; break; case NL80211_IFTYPE_ADHOC: hvif.opmode = HTC_M_IBSS; break; case NL80211_IFTYPE_AP: hvif.opmode = HTC_M_HOSTAP; break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } /* Index starts from zero on the target */ avp->index = hvif.index = ffz(priv->vif_slot); hvif.rtsthreshold = cpu_to_be16(2304); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto out; /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); if (ret) { WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); goto out; } ath9k_htc_set_mac_bssid_mask(priv, vif); priv->vif_slot |= (1 << avp->index); priv->nvifs++; INC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_assign_bslot(priv, vif); ath9k_htc_set_opmode(priv); if ((priv->ah->opmode == NL80211_IFTYPE_AP) && !(priv->op_flags & OP_ANI_RUNNING)) { ath9k_hw_set_tsfadjust(priv->ah, 1); ath9k_htc_start_ani(priv); } ath_dbg(common, CONFIG, "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc; struct ieee80211_channel *channel = hw->conf.channel; struct ath9k_hw_cal_data *caldata; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; int ret; if (priv->op_flags & OP_INVALID) return -EIO; fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); ath9k_htc_ps_wakeup(priv); htc_stop(priv->htc); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); ath_dbg(common, ATH_DBG_CONFIG, "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", priv->ah->curchan->channel, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), fastcc); caldata = &priv->caldata[channel->hw_value]; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { ath_err(common, "Unable to reset channel (%u Mhz) reset status %d\n", channel->center_freq, ret); goto err; } ath_update_txpow(priv); WMI_CMD(WMI_START_RECV_CMDID); if (ret) goto err; ath9k_host_rx_init(priv); mode = ath9k_htc_get_curmode(priv, hchan); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); if (ret) goto err; WMI_CMD(WMI_ENABLE_INTR_CMDID); if (ret) goto err; htc_start(priv->htc); err: ath9k_htc_ps_restore(priv); return ret; }
static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & BSS_CHANGED_ASSOC) { common->curaid = bss_conf->assoc ? bss_conf->aid : 0; ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); if (bss_conf->assoc) { priv->op_flags |= OP_ASSOCIATED; ath_start_ani(priv); } else { priv->op_flags &= ~OP_ASSOCIATED; cancel_delayed_work_sync(&priv->ath9k_ani_work); } } if (changed & BSS_CHANGED_BSSID) { /* Set BSSID */ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); ath9k_hw_write_associd(ah); ath_dbg(common, ATH_DBG_CONFIG, "BSSID: %pM aid: 0x%x\n", common->curbssid, common->curaid); } if ((changed & BSS_CHANGED_BEACON_INT) || (changed & BSS_CHANGED_BEACON) || ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) { priv->op_flags |= OP_ENABLE_BEACON; ath9k_htc_beacon_config(priv, vif); } if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { priv->op_flags &= ~OP_ENABLE_BEACON; ath9k_htc_beacon_config(priv, vif); } if (changed & BSS_CHANGED_ERP_PREAMBLE) { ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", bss_conf->use_short_preamble); if (bss_conf->use_short_preamble) priv->op_flags |= OP_PREAMBLE_SHORT; else priv->op_flags &= ~OP_PREAMBLE_SHORT; } if (changed & BSS_CHANGED_ERP_CTS_PROT) { ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", bss_conf->use_cts_prot); if (bss_conf->use_cts_prot && hw->conf.channel->band != IEEE80211_BAND_5GHZ) priv->op_flags |= OP_PROTECT_ENABLE; else priv->op_flags &= ~OP_PROTECT_ENABLE; } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ah->slottime = 9; else ah->slottime = 20; ath9k_hw_init_global_settings(ah); } if (changed & BSS_CHANGED_HT) ath9k_htc_update_rate(priv, vif, bss_conf); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); }
static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc; struct ieee80211_channel *channel = hw->conf.chandef.chan; struct ath9k_hw_cal_data *caldata = NULL; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; int ret; if (test_bit(ATH_OP_INVALID, &common->op_flags)) return -EIO; fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); ath9k_htc_ps_wakeup(priv); ath9k_htc_stop_ani(priv); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); ath9k_wmi_event_drain(priv); ath_dbg(common, CONFIG, "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", priv->ah->curchan->channel, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), fastcc); if (!fastcc) caldata = &priv->caldata; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { ath_err(common, "Unable to reset channel (%u Mhz) reset status %d\n", channel->center_freq, ret); goto err; } ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); WMI_CMD(WMI_START_RECV_CMDID); if (ret) goto err; ath9k_host_rx_init(priv); mode = ath9k_htc_get_curmode(priv, hchan); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); if (ret) goto err; WMI_CMD(WMI_ENABLE_INTR_CMDID); if (ret) goto err; htc_start(priv->htc); if (!test_bit(ATH_OP_SCANNING, &common->op_flags) && !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) ath9k_htc_vif_reconfig(priv); mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); err: ath9k_htc_ps_restore(priv); return ret; }
void ath9k_htc_ani_work(struct work_struct *work) { struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, ani_work.work); struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); bool longcal = false; bool shortcal = false; bool aniflag = false; unsigned int timestamp = jiffies_to_msecs(jiffies); u32 cal_interval, short_cal_interval; short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; /* Only calibrate if awake */ if (ah->power_mode != ATH9K_PM_AWAKE) goto set_timer; /* Long calibration runs independently of short calibration. */ if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { longcal = true; ath_dbg(common, ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; } /* Short calibration applies only while caldone is false */ if (!common->ani.caldone) { if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; ath_dbg(common, ANI, "shortcal @%lu\n", jiffies); common->ani.shortcal_timer = timestamp; common->ani.resetcal_timer = timestamp; } } else { if ((timestamp - common->ani.resetcal_timer) >= ATH_RESTART_CALINTERVAL) { common->ani.caldone = ath9k_hw_reset_calvalid(ah); if (common->ani.caldone) common->ani.resetcal_timer = timestamp; } } /* Verify whether we must check ANI */ if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { aniflag = true; common->ani.checkani_timer = timestamp; } /* Skip all processing if there's nothing to do. */ if (longcal || shortcal || aniflag) { ath9k_htc_ps_wakeup(priv); /* Call ANI routine if necessary */ if (aniflag) ath9k_hw_ani_monitor(ah, ah->curchan); /* Perform calibration if necessary */ if (longcal || shortcal) common->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask, longcal); ath9k_htc_ps_restore(priv); } set_timer: /* * Set timer interval based on previous results. * The interval must be the shortest necessary to satisfy ANI, * short calibration and long calibration. */ cal_interval = ATH_LONG_CALINTERVAL; cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(cal_interval)); }
static void ath9k_htc_stop(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); if (priv->op_flags & OP_INVALID) { ath_dbg(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&priv->mutex); return; } ath9k_htc_ps_wakeup(priv); htc_stop(priv->htc); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); tasklet_kill(&priv->swba_tasklet); tasklet_kill(&priv->rx_tasklet); tasklet_kill(&priv->tx_tasklet); skb_queue_purge(&priv->tx_queue); mutex_unlock(&priv->mutex); /* Cancel all the running timers/work .. */ cancel_work_sync(&priv->fatal_work); cancel_work_sync(&priv->ps_work); cancel_delayed_work_sync(&priv->ath9k_led_blink_work); ath9k_led_stop_brightness(priv); mutex_lock(&priv->mutex); /* Remove monitor interface here */ if (ah->opmode == NL80211_IFTYPE_MONITOR) { if (ath9k_htc_remove_monitor_interface(priv)) ath_err(common, "Unable to remove monitor interface\n"); else ath_dbg(common, ATH_DBG_CONFIG, "Monitor interface removed\n"); } if (ah->btcoex_hw.enabled) { ath9k_hw_btcoex_disable(ah); if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) ath_htc_cancel_btcoex_work(priv); } ath9k_hw_phy_disable(ah); ath9k_hw_disable(ah); ath9k_htc_ps_restore(priv); ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); priv->op_flags |= OP_INVALID; ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n"); mutex_unlock(&priv->mutex); }