diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/htc_drv_main.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_main.c | 485 |
1 files changed, 331 insertions, 154 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 6bb59958f71..db8c0c044e9 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -24,17 +24,6 @@ static struct dentry *ath9k_debugfs_root; /* Utilities */ /*************/ -void ath_update_txpow(struct ath9k_htc_priv *priv) -{ - struct ath_hw *ah = priv->ah; - - if (priv->curtxpow != priv->txpowlimit) { - ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit, false); - /* read back in case value is clamped */ - priv->curtxpow = ath9k_hw_regulatory(ah)->power_limit; - } -} - /* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */ static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, struct ath9k_channel *ichan) @@ -116,12 +105,88 @@ void ath9k_ps_work(struct work_struct *work) ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); } +static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath9k_htc_priv *priv = data; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon) + priv->reconfig_beacon = true; + + if (bss_conf->assoc) { + priv->rearm_ani = true; + priv->reconfig_beacon = true; + } +} + +static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv) +{ + priv->rearm_ani = false; + priv->reconfig_beacon = false; + + ieee80211_iterate_active_interfaces_atomic(priv->hw, + ath9k_htc_vif_iter, priv); + if (priv->rearm_ani) + ath9k_htc_start_ani(priv); + + if (priv->reconfig_beacon) { + ath9k_htc_ps_wakeup(priv); + ath9k_htc_beacon_reconfig(priv); + ath9k_htc_ps_restore(priv); + } +} + +static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath9k_vif_iter_data *iter_data = data; + int i; + + for (i = 0; i < ETH_ALEN; i++) + iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); +} + +static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_vif_iter_data iter_data; + + /* + * Use the hardware MAC address as reference, the hardware uses it + * together with the BSSID mask when matching addresses. + */ + iter_data.hw_macaddr = common->macaddr; + memset(&iter_data.mask, 0xff, ETH_ALEN); + + if (vif) + ath9k_htc_bssid_iter(&iter_data, vif->addr, vif); + + /* Get list of all active MAC addresses */ + ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter, + &iter_data); + + memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); + ath_hw_setbssidmask(common); +} + +static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv) +{ + if (priv->num_ibss_vif) + priv->ah->opmode = NL80211_IFTYPE_ADHOC; + else if (priv->num_ap_vif) + priv->ah->opmode = NL80211_IFTYPE_AP; + else + priv->ah->opmode = NL80211_IFTYPE_STATION; + + ath9k_hw_setopmode(priv->ah); +} + void ath9k_htc_reset(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *channel = priv->hw->conf.channel; - struct ath9k_hw_cal_data *caldata; + struct ath9k_hw_cal_data *caldata = NULL; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; @@ -130,16 +195,14 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv) mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); - if (priv->op_flags & OP_ASSOCIATED) - cancel_delayed_work_sync(&priv->ath9k_ani_work); - + ath9k_htc_stop_ani(priv); ieee80211_stop_queues(priv->hw); htc_stop(priv->htc); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); - caldata = &priv->caldata[channel->hw_value]; + caldata = &priv->caldata; ret = ath9k_hw_reset(ah, ah->curchan, caldata, false); if (ret) { ath_err(common, @@ -147,7 +210,8 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv) channel->center_freq, ret); } - ath_update_txpow(priv); + ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, + &priv->curtxpow); WMI_CMD(WMI_START_RECV_CMDID); ath9k_host_rx_init(priv); @@ -158,12 +222,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv) WMI_CMD(WMI_ENABLE_INTR_CMDID); htc_start(priv->htc); - - if (priv->op_flags & OP_ASSOCIATED) { - ath9k_htc_beacon_config(priv, priv->vif); - ath_start_ani(priv); - } - + ath9k_htc_vif_reconfig(priv); ieee80211_wake_queues(priv->hw); ath9k_htc_ps_restore(priv); @@ -179,7 +238,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ieee80211_conf *conf = &common->hw->conf; bool fastcc; struct ieee80211_channel *channel = hw->conf.channel; - struct ath9k_hw_cal_data *caldata; + struct ath9k_hw_cal_data *caldata = NULL; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; @@ -202,7 +261,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), fastcc); - caldata = &priv->caldata[channel->hw_value]; + if (!fastcc) + caldata = &priv->caldata; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { ath_err(common, @@ -211,7 +271,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, goto err; } - ath_update_txpow(priv); + ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, + &priv->curtxpow); WMI_CMD(WMI_START_RECV_CMDID); if (ret) @@ -230,11 +291,23 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, goto err; htc_start(priv->htc); + + if (!(priv->op_flags & OP_SCANNING) && + !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + ath9k_htc_vif_reconfig(priv); + err: ath9k_htc_ps_restore(priv); return ret; } +/* + * Monitor mode handling is a tad complicated because the firmware requires + * an interface to be created exclusively, while mac80211 doesn't associate + * an interface with the mode. + * + * So, for now, only one monitor interface can be configured. + */ static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); @@ -244,9 +317,10 @@ static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); - hvif.index = 0; /* Should do for now */ + hvif.index = priv->mon_vif_idx; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); priv->nvifs--; + priv->vif_slot &= ~(1 << priv->mon_vif_idx); } static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) @@ -254,70 +328,87 @@ static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; struct ath9k_htc_target_sta tsta; - int ret = 0; + int ret = 0, sta_idx; u8 cmd_rsp; - if (priv->nvifs > 0) - return -ENOBUFS; + if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) || + (priv->nstations >= ATH9K_HTC_MAX_STA)) { + ret = -ENOBUFS; + goto err_vif; + } - if (priv->nstations >= ATH9K_HTC_MAX_STA) - return -ENOBUFS; + sta_idx = ffz(priv->sta_slot); + if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) { + ret = -ENOBUFS; + goto err_vif; + } /* * Add an interface. */ - memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); hvif.opmode = cpu_to_be32(HTC_M_MONITOR); - priv->ah->opmode = NL80211_IFTYPE_MONITOR; - hvif.index = priv->nvifs; + hvif.index = ffz(priv->vif_slot); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) - return ret; + goto err_vif; + + /* + * Assign the monitor interface index as a special case here. + * This is needed when the interface is brought down. + */ + priv->mon_vif_idx = hvif.index; + priv->vif_slot |= (1 << hvif.index); + + /* + * Set the hardware mode to monitor only if there are no + * other interfaces. + */ + if (!priv->nvifs) + priv->ah->opmode = NL80211_IFTYPE_MONITOR; priv->nvifs++; /* * Associate a station with the interface for packet injection. */ - memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN); tsta.is_vif_sta = 1; - tsta.sta_index = priv->nstations; + tsta.sta_index = sta_idx; tsta.vif_index = hvif.index; tsta.maxampdu = 0xffff; WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { ath_err(common, "Unable to add station entry for monitor mode\n"); - goto err_vif; + goto err_sta; } + priv->sta_slot |= (1 << sta_idx); priv->nstations++; - - /* - * Set chainmask etc. on the target. - */ - ret = ath9k_htc_update_cap_target(priv); - if (ret) - ath_dbg(common, ATH_DBG_CONFIG, - "Failed to update capability in target\n"); - + priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx; priv->ah->is_monitoring = true; + ath_dbg(common, ATH_DBG_CONFIG, + "Attached a monitor interface at idx: %d, sta idx: %d\n", + priv->mon_vif_idx, sta_idx); + return 0; -err_vif: +err_sta: /* * Remove the interface from the target. */ __ath9k_htc_remove_monitor_interface(priv); +err_vif: + ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n"); + return ret; } @@ -329,7 +420,7 @@ static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) __ath9k_htc_remove_monitor_interface(priv); - sta_idx = 0; /* Only single interface, for now */ + sta_idx = priv->vif_sta_pos[priv->mon_vif_idx]; WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); if (ret) { @@ -337,9 +428,14 @@ static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) return ret; } + priv->sta_slot &= ~(1 << sta_idx); priv->nstations--; priv->ah->is_monitoring = false; + ath_dbg(common, ATH_DBG_CONFIG, + "Removed a monitor interface at idx: %d, sta idx: %d\n", + priv->mon_vif_idx, sta_idx); + return 0; } @@ -351,12 +447,16 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, struct ath9k_htc_target_sta tsta; struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; - int ret; + int ret, sta_idx; u8 cmd_rsp; if (priv->nstations >= ATH9K_HTC_MAX_STA) return -ENOBUFS; + sta_idx = ffz(priv->sta_slot); + if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) + return -ENOBUFS; + memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); if (sta) { @@ -366,13 +466,13 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, tsta.associd = common->curaid; tsta.is_vif_sta = 0; tsta.valid = true; - ista->index = priv->nstations; + ista->index = sta_idx; } else { memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); tsta.is_vif_sta = 1; } - tsta.sta_index = priv->nstations; + tsta.sta_index = sta_idx; tsta.vif_index = avp->index; tsta.maxampdu = 0xffff; if (sta && sta->ht_cap.ht_supported) @@ -387,12 +487,21 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, return ret; } - if (sta) + if (sta) { ath_dbg(common, ATH_DBG_CONFIG, "Added a station entry for: %pM (idx: %d)\n", sta->addr, tsta.sta_index); + } else { + ath_dbg(common, ATH_DBG_CONFIG, + "Added a station entry for VIF %d (idx: %d)\n", + avp->index, tsta.sta_index); + } + priv->sta_slot |= (1 << sta_idx); priv->nstations++; + if (!sta) + priv->vif_sta_pos[avp->index] = sta_idx; + return 0; } @@ -401,6 +510,7 @@ static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; int ret; u8 cmd_rsp, sta_idx; @@ -409,7 +519,7 @@ static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, ista = (struct ath9k_htc_sta *) sta->drv_priv; sta_idx = ista->index; } else { - sta_idx = 0; + sta_idx = priv->vif_sta_pos[avp->index]; } WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); @@ -421,12 +531,19 @@ static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, return ret; } - if (sta) + if (sta) { ath_dbg(common, ATH_DBG_CONFIG, "Removed a station entry for: %pM (idx: %d)\n", sta->addr, sta_idx); + } else { + ath_dbg(common, ATH_DBG_CONFIG, + "Removed a station entry for VIF %d (idx: %d)\n", + avp->index, sta_idx); + } + priv->sta_slot &= ~(1 << sta_idx); priv->nstations--; + return 0; } @@ -808,7 +925,7 @@ void ath9k_htc_debug_remove_root(void) /* ANI */ /*******/ -void ath_start_ani(struct ath9k_htc_priv *priv) +void ath9k_htc_start_ani(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); unsigned long timestamp = jiffies_to_msecs(jiffies); @@ -817,15 +934,22 @@ void ath_start_ani(struct ath9k_htc_priv *priv) common->ani.shortcal_timer = timestamp; common->ani.checkani_timer = timestamp; - ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + priv->op_flags |= OP_ANI_RUNNING; + + ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); } -void ath9k_ani_work(struct work_struct *work) +void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv) +{ + cancel_delayed_work_sync(&priv->ani_work); + priv->op_flags &= ~OP_ANI_RUNNING; +} + +void ath9k_htc_ani_work(struct work_struct *work) { struct ath9k_htc_priv *priv = - container_of(work, struct ath9k_htc_priv, - ath9k_ani_work.work); + 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; @@ -834,7 +958,8 @@ void ath9k_ani_work(struct work_struct *work) unsigned int timestamp = jiffies_to_msecs(jiffies); u32 cal_interval, short_cal_interval; - short_cal_interval = ATH_STA_SHORT_CALINTERVAL; + 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) @@ -903,7 +1028,7 @@ set_timer: if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); - ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(cal_interval)); } @@ -911,7 +1036,7 @@ set_timer: /* mac80211 Callbacks */ /**********************/ -static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct ath9k_htc_priv *priv = hw->priv; @@ -924,7 +1049,7 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) - return -1; + goto fail_tx; skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); } @@ -945,11 +1070,10 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) goto fail_tx; } - return 0; + return; fail_tx: dev_kfree_skb_any(skb); - return 0; } static int ath9k_htc_start(struct ieee80211_hw *hw) @@ -987,7 +1111,8 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) return ret; } - ath_update_txpow(priv); + ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, + &priv->curtxpow); mode = ath9k_htc_get_curmode(priv, init_channel); htc_mode = cpu_to_be16(mode); @@ -997,6 +1122,11 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ath9k_host_rx_init(priv); + ret = ath9k_htc_update_cap_target(priv); + if (ret) + ath_dbg(common, ATH_DBG_CONFIG, + "Failed to update capability in target\n"); + priv->op_flags &= ~OP_INVALID; htc_start(priv->htc); @@ -1051,25 +1181,21 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) cancel_work_sync(&priv->fatal_work); cancel_work_sync(&priv->ps_work); cancel_delayed_work_sync(&priv->ath9k_led_blink_work); + ath9k_htc_stop_ani(priv); 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); } + /* 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); @@ -1093,10 +1219,24 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); - /* Only one interface for now */ - if (priv->nvifs > 0) { - ret = -ENOBUFS; - goto out; + 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); @@ -1110,6 +1250,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_ADHOC: hvif.opmode = cpu_to_be32(HTC_M_IBSS); break; + case NL80211_IFTYPE_AP: + hvif.opmode = cpu_to_be32(HTC_M_HOSTAP); + break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); @@ -1117,34 +1260,39 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, 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; + 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; - priv->nvifs++; - /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); - if (ret) + if (ret) { + WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); goto out; + } - ret = ath9k_htc_update_cap_target(priv); - if (ret) - ath_dbg(common, ATH_DBG_CONFIG, - "Failed to update capability in target\n"); + ath9k_htc_set_bssid_mask(priv, vif); + priv->vif_slot |= (1 << avp->index); + priv->nvifs++; priv->vif = vif; + + INC_VIF(priv, vif->type); + ath9k_htc_set_opmode(priv); + + if ((priv->ah->opmode == NL80211_IFTYPE_AP) && + !(priv->op_flags & OP_ANI_RUNNING)) + ath9k_htc_start_ani(priv); + + ath_dbg(common, ATH_DBG_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); @@ -1162,8 +1310,6 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, int ret = 0; u8 cmd_rsp; - ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n"); - mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); @@ -1172,10 +1318,27 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, 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); } @@ -1211,13 +1374,11 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) * IEEE80211_CONF_CHANGE_CHANNEL is handled. */ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - if (conf->flags & IEEE80211_CONF_MONITOR) { - if (ath9k_htc_add_monitor_interface(priv)) - ath_err(common, "Failed to set monitor mode\n"); - else - ath_dbg(common, ATH_DBG_CONFIG, - "HW opmode set to Monitor mode\n"); - } + 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) { @@ -1252,7 +1413,8 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_POWER) { priv->txpowlimit = 2 * conf->power_level; - ath_update_txpow(priv); + ath9k_cmn_update_txpow(priv->ah, priv->curtxpow, + priv->txpowlimit, &priv->curtxpow); } if (changed & IEEE80211_CONF_CHANGE_IDLE) { @@ -1439,66 +1601,81 @@ static void ath9k_htc_bss_info_changed(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); + 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) { - 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 (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) { - /* Set BSSID */ - memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); - ath9k_hw_write_associd(ah); + 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); + 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)) { + 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) { - 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_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_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_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) { @@ -1557,12 +1734,14 @@ 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) + 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); + switch (action) { case IEEE80211_AMPDU_RX_START: break; @@ -1587,6 +1766,8 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } + mutex_unlock(&priv->mutex); + return ret; } @@ -1599,8 +1780,7 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) priv->op_flags |= OP_SCANNING; spin_unlock_bh(&priv->beacon_lock); cancel_work_sync(&priv->ps_work); - if (priv->op_flags & OP_ASSOCIATED) - cancel_delayed_work_sync(&priv->ath9k_ani_work); + ath9k_htc_stop_ani(priv); mutex_unlock(&priv->mutex); } @@ -1609,14 +1789,11 @@ static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) struct ath9k_htc_priv *priv = hw->priv; mutex_lock(&priv->mutex); - ath9k_htc_ps_wakeup(priv); spin_lock_bh(&priv->beacon_lock); priv->op_flags &= ~OP_SCANNING; spin_unlock_bh(&priv->beacon_lock); - if (priv->op_flags & OP_ASSOCIATED) { - ath9k_htc_beacon_config(priv, priv->vif); - ath_start_ani(priv); - } + ath9k_htc_ps_wakeup(priv); + ath9k_htc_vif_reconfig(priv); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } |