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 | 1059 |
1 files changed, 528 insertions, 531 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 9a3be8da755d..db8c0c044e9e 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 */ /*************/ -static 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); - /* 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) @@ -63,8 +52,8 @@ static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, return mode; } -static bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, - enum ath9k_power_mode mode) +bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, + enum ath9k_power_mode mode) { bool ret; @@ -116,6 +105,130 @@ 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 = NULL; + enum htc_phymode mode; + __be16 htc_mode; + u8 cmd_rsp; + int ret; + + mutex_lock(&priv->mutex); + ath9k_htc_ps_wakeup(priv); + + 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; + ret = ath9k_hw_reset(ah, ah->curchan, caldata, false); + if (ret) { + ath_err(common, + "Unable to reset device (%u Mhz) reset status %d\n", + channel->center_freq, ret); + } + + ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, + &priv->curtxpow); + + WMI_CMD(WMI_START_RECV_CMDID); + ath9k_host_rx_init(priv); + + mode = ath9k_htc_get_curmode(priv, ah->curchan); + htc_mode = cpu_to_be16(mode); + WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); + + WMI_CMD(WMI_ENABLE_INTR_CMDID); + htc_start(priv->htc); + ath9k_htc_vif_reconfig(priv); + ieee80211_wake_queues(priv->hw); + + 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) @@ -123,9 +236,9 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; - bool fastcc = true; + 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; @@ -134,8 +247,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, if (priv->op_flags & OP_INVALID) return -EIO; - if (priv->op_flags & OP_FULL_RESET) - fastcc = false; + fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); ath9k_htc_ps_wakeup(priv); htc_stop(priv->htc); @@ -143,22 +255,24 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); - ath_print(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); + 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]; + if (!fastcc) + caldata = &priv->caldata; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { - ath_print(common, ATH_DBG_FATAL, - "Unable to reset channel (%u Mhz) " - "reset status %d\n", channel->center_freq, ret); + ath_err(common, + "Unable to reset channel (%u Mhz) reset status %d\n", + channel->center_freq, ret); 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) @@ -178,51 +292,151 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, htc_start(priv->htc); - priv->op_flags &= ~OP_FULL_RESET; + 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; } -static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) +/* + * 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); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; - if (priv->nvifs > 0) - return -ENOBUFS; + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); + 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) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_vif hvif; + struct ath9k_htc_target_sta tsta; + int ret = 0, sta_idx; + u8 cmd_rsp; + + if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) || + (priv->nstations >= ATH9K_HTC_MAX_STA)) { + ret = -ENOBUFS; + goto err_vif; + } + + 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 = 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_sta; + } + + priv->sta_slot |= (1 << sta_idx); + priv->nstations++; + 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_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; } static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); - struct ath9k_htc_target_vif hvif; int ret = 0; - u8 cmd_rsp; + u8 cmd_rsp, sta_idx; - memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); - memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); - hvif.index = 0; /* Should do for now */ - WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); - priv->nvifs--; + __ath9k_htc_remove_monitor_interface(priv); - return ret; + sta_idx = priv->vif_sta_pos[priv->mon_vif_idx]; + + WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); + if (ret) { + ath_err(common, "Unable to remove station entry for monitor mode\n"); + 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; } static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, @@ -233,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) { @@ -248,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) @@ -263,17 +481,27 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { if (sta) - ath_print(common, ATH_DBG_FATAL, - "Unable to add station entry for: %pM\n", sta->addr); + ath_err(common, + "Unable to add station entry for: %pM\n", + sta->addr); return ret; } - if (sta) - ath_print(common, ATH_DBG_CONFIG, - "Added a station entry for: %pM (idx: %d)\n", - sta->addr, tsta.sta_index); + 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; } @@ -282,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; @@ -290,28 +519,35 @@ 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); if (ret) { if (sta) - ath_print(common, ATH_DBG_FATAL, - "Unable to remove station entry for: %pM\n", - sta->addr); + ath_err(common, + "Unable to remove station entry for: %pM\n", + sta->addr); return ret; } - if (sta) - ath_print(common, ATH_DBG_CONFIG, - "Removed a station entry for: %pM (idx: %d)\n", - sta->addr, sta_idx); + 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; } -static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv) +int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv) { struct ath9k_htc_cap_target tcap; int ret; @@ -390,8 +626,8 @@ static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv, WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate); if (ret) { - ath_print(common, ATH_DBG_FATAL, - "Unable to initialize Rate information on target\n"); + ath_err(common, + "Unable to initialize Rate information on target\n"); } return ret; @@ -408,9 +644,9 @@ static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv, ath9k_htc_setup_rate(priv, sta, &trate); ret = ath9k_htc_send_rate_cmd(priv, &trate); if (!ret) - ath_print(common, ATH_DBG_CONFIG, - "Updated target sta: %pM, rate caps: 0x%X\n", - sta->addr, be32_to_cpu(trate.capflags)); + ath_dbg(common, ATH_DBG_CONFIG, + "Updated target sta: %pM, rate caps: 0x%X\n", + sta->addr, be32_to_cpu(trate.capflags)); } static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv, @@ -435,9 +671,9 @@ static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv, ret = ath9k_htc_send_rate_cmd(priv, &trate); if (!ret) - ath_print(common, ATH_DBG_CONFIG, - "Updated target sta: %pM, rate caps: 0x%X\n", - bss_conf->bssid, be32_to_cpu(trate.capflags)); + ath_dbg(common, ATH_DBG_CONFIG, + "Updated target sta: %pM, rate caps: 0x%X\n", + bss_conf->bssid, be32_to_cpu(trate.capflags)); } static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv, @@ -464,14 +700,14 @@ static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv, WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr); if (ret) - ath_print(common, ATH_DBG_CONFIG, - "Unable to %s TX aggregation for (%pM, %d)\n", - (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid); + ath_dbg(common, ATH_DBG_CONFIG, + "Unable to %s TX aggregation for (%pM, %d)\n", + (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid); else - ath_print(common, ATH_DBG_CONFIG, - "%s TX aggregation for (%pM, %d)\n", - (aggr.aggr_enable) ? "Starting" : "Stopping", - sta->addr, tid); + ath_dbg(common, ATH_DBG_CONFIG, + "%s TX aggregation for (%pM, %d)\n", + (aggr.aggr_enable) ? "Starting" : "Stopping", + sta->addr, tid); spin_lock_bh(&priv->tx_lock); ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP; @@ -689,7 +925,7 @@ void ath9k_htc_debug_remove_root(void) /* ANI */ /*******/ -static 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); @@ -698,15 +934,22 @@ static 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; @@ -715,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) @@ -724,7 +968,7 @@ void ath9k_ani_work(struct work_struct *work) /* Long calibration runs independently of short calibration. */ if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { longcal = true; - ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); + ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; } @@ -733,8 +977,8 @@ void ath9k_ani_work(struct work_struct *work) if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; - ath_print(common, ATH_DBG_ANI, - "shortcal @%lu\n", jiffies); + ath_dbg(common, ATH_DBG_ANI, + "shortcal @%lu\n", jiffies); common->ani.shortcal_timer = timestamp; common->ani.resetcal_timer = timestamp; } @@ -784,326 +1028,15 @@ 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)); } -/*******/ -/* LED */ -/*******/ - -static void ath9k_led_blink_work(struct work_struct *work) -{ - struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, - ath9k_led_blink_work.work); - - if (!(priv->op_flags & OP_LED_ASSOCIATED)) - return; - - if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) || - (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) - ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); - else - ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, - (priv->op_flags & OP_LED_ON) ? 1 : 0); - - ieee80211_queue_delayed_work(priv->hw, - &priv->ath9k_led_blink_work, - (priv->op_flags & OP_LED_ON) ? - msecs_to_jiffies(priv->led_off_duration) : - msecs_to_jiffies(priv->led_on_duration)); - - priv->led_on_duration = priv->led_on_cnt ? - max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) : - ATH_LED_ON_DURATION_IDLE; - priv->led_off_duration = priv->led_off_cnt ? - max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) : - ATH_LED_OFF_DURATION_IDLE; - priv->led_on_cnt = priv->led_off_cnt = 0; - - if (priv->op_flags & OP_LED_ON) - priv->op_flags &= ~OP_LED_ON; - else - priv->op_flags |= OP_LED_ON; -} - -static void ath9k_led_brightness_work(struct work_struct *work) -{ - struct ath_led *led = container_of(work, struct ath_led, - brightness_work.work); - struct ath9k_htc_priv *priv = led->priv; - - switch (led->brightness) { - case LED_OFF: - if (led->led_type == ATH_LED_ASSOC || - led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, - (led->led_type == ATH_LED_RADIO)); - priv->op_flags &= ~OP_LED_ASSOCIATED; - if (led->led_type == ATH_LED_RADIO) - priv->op_flags &= ~OP_LED_ON; - } else { - priv->led_off_cnt++; - } - break; - case LED_FULL: - if (led->led_type == ATH_LED_ASSOC) { - priv->op_flags |= OP_LED_ASSOCIATED; - ieee80211_queue_delayed_work(priv->hw, - &priv->ath9k_led_blink_work, 0); - } else if (led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); - priv->op_flags |= OP_LED_ON; - } else { - priv->led_on_cnt++; - } - break; - default: - break; - } -} - -static void ath9k_led_brightness(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); - struct ath9k_htc_priv *priv = led->priv; - - led->brightness = brightness; - if (!(priv->op_flags & OP_LED_DEINIT)) - ieee80211_queue_delayed_work(priv->hw, - &led->brightness_work, 0); -} - -static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv) -{ - cancel_delayed_work_sync(&priv->radio_led.brightness_work); - cancel_delayed_work_sync(&priv->assoc_led.brightness_work); - cancel_delayed_work_sync(&priv->tx_led.brightness_work); - cancel_delayed_work_sync(&priv->rx_led.brightness_work); -} - -static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led, - char *trigger) -{ - int ret; - - led->priv = priv; - led->led_cdev.name = led->name; - led->led_cdev.default_trigger = trigger; - led->led_cdev.brightness_set = ath9k_led_brightness; - - ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev); - if (ret) - ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, - "Failed to register led:%s", led->name); - else - led->registered = 1; - - INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work); - - return ret; -} - -static void ath9k_unregister_led(struct ath_led *led) -{ - if (led->registered) { - led_classdev_unregister(&led->led_cdev); - led->registered = 0; - } -} - -void ath9k_deinit_leds(struct ath9k_htc_priv *priv) -{ - priv->op_flags |= OP_LED_DEINIT; - ath9k_unregister_led(&priv->assoc_led); - priv->op_flags &= ~OP_LED_ASSOCIATED; - ath9k_unregister_led(&priv->tx_led); - ath9k_unregister_led(&priv->rx_led); - ath9k_unregister_led(&priv->radio_led); -} - -void ath9k_init_leds(struct ath9k_htc_priv *priv) -{ - char *trigger; - int ret; - - if (AR_SREV_9287(priv->ah)) - priv->ah->led_pin = ATH_LED_PIN_9287; - else if (AR_SREV_9271(priv->ah)) - priv->ah->led_pin = ATH_LED_PIN_9271; - else if (AR_DEVID_7010(priv->ah)) - priv->ah->led_pin = ATH_LED_PIN_7010; - else - priv->ah->led_pin = ATH_LED_PIN_DEF; - - /* Configure gpio 1 for output */ - ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - /* LED off, active low */ - ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); - - INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work); - - trigger = ieee80211_get_radio_led_name(priv->hw); - snprintf(priv->radio_led.name, sizeof(priv->radio_led.name), - "ath9k-%s::radio", wiphy_name(priv->hw->wiphy)); - ret = ath9k_register_led(priv, &priv->radio_led, trigger); - priv->radio_led.led_type = ATH_LED_RADIO; - if (ret) - goto fail; - - trigger = ieee80211_get_assoc_led_name(priv->hw); - snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name), - "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy)); - ret = ath9k_register_led(priv, &priv->assoc_led, trigger); - priv->assoc_led.led_type = ATH_LED_ASSOC; - if (ret) - goto fail; - - trigger = ieee80211_get_tx_led_name(priv->hw); - snprintf(priv->tx_led.name, sizeof(priv->tx_led.name), - "ath9k-%s::tx", wiphy_name(priv->hw->wiphy)); - ret = ath9k_register_led(priv, &priv->tx_led, trigger); - priv->tx_led.led_type = ATH_LED_TX; - if (ret) - goto fail; - - trigger = ieee80211_get_rx_led_name(priv->hw); - snprintf(priv->rx_led.name, sizeof(priv->rx_led.name), - "ath9k-%s::rx", wiphy_name(priv->hw->wiphy)); - ret = ath9k_register_led(priv, &priv->rx_led, trigger); - priv->rx_led.led_type = ATH_LED_RX; - if (ret) - goto fail; - - priv->op_flags &= ~OP_LED_DEINIT; - - return; - -fail: - cancel_delayed_work_sync(&priv->ath9k_led_blink_work); - ath9k_deinit_leds(priv); -} - -/*******************/ -/* Rfkill */ -/*******************/ - -static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv) -{ - return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == - priv->ah->rfkill_polarity; -} - -static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw) -{ - struct ath9k_htc_priv *priv = hw->priv; - bool blocked = !!ath_is_rfkill_set(priv); - - wiphy_rfkill_set_hw_state(hw->wiphy, blocked); -} - -void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv) -{ - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) - wiphy_rfkill_start_polling(priv->hw->wiphy); -} - -static void ath9k_htc_radio_enable(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; - u8 cmd_rsp; - - if (!ah->curchan) - ah->curchan = ath9k_cmn_get_curchannel(hw, ah); - - /* Reset the HW */ - ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); - if (ret) { - ath_print(common, ATH_DBG_FATAL, - "Unable to reset hardware; reset status %d " - "(freq %u MHz)\n", ret, ah->curchan->channel); - } - - ath_update_txpow(priv); - - /* Start RX */ - WMI_CMD(WMI_START_RECV_CMDID); - ath9k_host_rx_init(priv); - - /* Start TX */ - htc_start(priv->htc); - spin_lock_bh(&priv->tx_lock); - priv->tx_queues_stop = false; - spin_unlock_bh(&priv->tx_lock); - ieee80211_wake_queues(hw); - - WMI_CMD(WMI_ENABLE_INTR_CMDID); - - /* Enable LED */ - ath9k_hw_cfg_output(ah, ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - ath9k_hw_set_gpio(ah, ah->led_pin, 0); -} - -static void ath9k_htc_radio_disable(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; - u8 cmd_rsp; - - ath9k_htc_ps_wakeup(priv); - - /* Disable LED */ - ath9k_hw_set_gpio(ah, ah->led_pin, 1); - ath9k_hw_cfg_gpio_input(ah, ah->led_pin); - - WMI_CMD(WMI_DISABLE_INTR_CMDID); - - /* Stop TX */ - ieee80211_stop_queues(hw); - htc_stop(priv->htc); - WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); - skb_queue_purge(&priv->tx_queue); - - /* Stop RX */ - WMI_CMD(WMI_STOP_RECV_CMDID); - - /* - * The MIB counters have to be disabled here, - * since the target doesn't do it. - */ - ath9k_hw_disable_mib_counters(ah); - - if (!ah->curchan) - ah->curchan = ath9k_cmn_get_curchannel(hw, ah); - - /* Reset the HW */ - ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); - if (ret) { - ath_print(common, ATH_DBG_FATAL, - "Unable to reset hardware; reset status %d " - "(freq %u MHz)\n", ret, ah->curchan->channel); - } - - /* Disable the PHY */ - ath9k_hw_phy_disable(ah); - - ath9k_htc_ps_restore(priv); - ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); -} - /**********************/ /* 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; @@ -1116,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); } @@ -1124,24 +1057,23 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ret = ath9k_htc_tx_start(priv, skb); if (ret != 0) { if (ret == -ENOMEM) { - ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, - "Stopping TX queues\n"); + ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, + "Stopping TX queues\n"); ieee80211_stop_queues(hw); spin_lock_bh(&priv->tx_lock); priv->tx_queues_stop = true; spin_unlock_bh(&priv->tx_lock); } else { - ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, - "Tx failed"); + ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, + "Tx failed\n"); } goto fail_tx; } - return 0; + return; fail_tx: dev_kfree_skb_any(skb); - return 0; } static int ath9k_htc_start(struct ieee80211_hw *hw) @@ -1158,9 +1090,9 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); - ath_print(common, ATH_DBG_CONFIG, - "Starting driver with initial channel: %d MHz\n", - curchan->center_freq); + ath_dbg(common, ATH_DBG_CONFIG, + "Starting driver with initial channel: %d MHz\n", + curchan->center_freq); /* Ensure that HW is awake before flushing RX */ ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); @@ -1169,20 +1101,18 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) /* setup initial channel */ init_channel = ath9k_cmn_get_curchannel(hw, ah); - /* Reset SERDES registers */ - ath9k_hw_configpcipowersave(ah, 0, 0); - ath9k_hw_htc_resetinit(ah); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { - ath_print(common, ATH_DBG_FATAL, - "Unable to reset hardware; reset status %d " - "(freq %u MHz)\n", ret, curchan->center_freq); + ath_err(common, + "Unable to reset hardware; reset status %d (freq %u MHz)\n", + ret, curchan->center_freq); mutex_unlock(&priv->mutex); 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); @@ -1192,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); @@ -1223,32 +1158,33 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); if (priv->op_flags & OP_INVALID) { - ath_print(common, ATH_DBG_ANY, "Device not present\n"); + ath_dbg(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&priv->mutex); return; } - /* Cancel all the running timers/work .. */ - cancel_work_sync(&priv->ps_work); - cancel_delayed_work_sync(&priv->ath9k_led_blink_work); - ath9k_led_stop_brightness(priv); - 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); - /* Remove monitor interface here */ - if (ah->opmode == NL80211_IFTYPE_MONITOR) { - if (ath9k_htc_remove_monitor_interface(priv)) - ath_print(common, ATH_DBG_FATAL, - "Unable to remove monitor interface\n"); - else - ath_print(common, ATH_DBG_CONFIG, - "Monitor interface removed\n"); - } + 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_htc_stop_ani(priv); + ath9k_led_stop_brightness(priv); + + mutex_lock(&priv->mutex); if (ah->btcoex_hw.enabled) { ath9k_hw_btcoex_disable(ah); @@ -1256,15 +1192,18 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) 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_hw_configpcipowersave(ah, 1, 1); ath9k_htc_ps_restore(priv); ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); priv->op_flags |= OP_INVALID; - ath_print(common, ATH_DBG_CONFIG, "Driver halt\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n"); mutex_unlock(&priv->mutex); } @@ -1280,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); @@ -1297,41 +1250,49 @@ 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_print(common, ATH_DBG_FATAL, + ath_err(common, "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } - ath_print(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_print(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); @@ -1349,8 +1310,6 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, int ret = 0; u8 cmd_rsp; - ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n"); - mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); @@ -1359,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); } @@ -1386,30 +1362,44 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) mutex_unlock(&priv->htc_pm_lock); if (enable_radio) { - ath_print(common, ATH_DBG_CONFIG, - "not-idle: enabling radio\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "not-idle: enabling radio\n"); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); ath9k_htc_radio_enable(hw); } } + /* + * 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) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; - ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", - curchan->center_freq); + ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", + curchan->center_freq); - ath9k_cmn_update_ichannel(hw, &priv->ah->channels[pos]); + ath9k_cmn_update_ichannel(&priv->ah->channels[pos], + hw->conf.channel, + hw->conf.channel_type); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { - ath_print(common, ATH_DBG_FATAL, - "Unable to set channel\n"); + ath_err(common, "Unable to set channel\n"); mutex_unlock(&priv->mutex); return -EINVAL; } } + if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); @@ -1421,15 +1411,10 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) } } - if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - if (conf->flags & IEEE80211_CONF_MONITOR) { - if (ath9k_htc_add_monitor_interface(priv)) - ath_print(common, ATH_DBG_FATAL, - "Failed to set monitor mode\n"); - else - ath_print(common, ATH_DBG_CONFIG, - "HW opmode set to Monitor mode\n"); - } + if (changed & IEEE80211_CONF_CHANGE_POWER) { + priv->txpowlimit = 2 * conf->power_level; + ath9k_cmn_update_txpow(priv->ah, priv->curtxpow, + priv->txpowlimit, &priv->curtxpow); } if (changed & IEEE80211_CONF_CHANGE_IDLE) { @@ -1440,8 +1425,8 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) } mutex_unlock(&priv->htc_pm_lock); - ath_print(common, ATH_DBG_CONFIG, - "idle: disabling radio\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "idle: disabling radio\n"); ath9k_htc_radio_disable(hw); } @@ -1478,8 +1463,8 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, rfilt = ath9k_htc_calcrxfilter(priv); ath9k_hw_setrxfilter(priv->ah, rfilt); - ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG, - "Set HW RX filter: 0x%x\n", 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); @@ -1542,15 +1527,14 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, qnum = get_hw_qnum(queue, priv->hwq_map); - ath_print(common, ATH_DBG_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); + ath_dbg(common, ATH_DBG_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_print(common, ATH_DBG_FATAL, "TXQ Update failed\n"); + ath_err(common, "TXQ Update failed\n"); goto out; } @@ -1578,7 +1562,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, return -ENOSPC; mutex_lock(&priv->mutex); - ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n"); ath9k_htc_ps_wakeup(priv); switch (cmd) { @@ -1617,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_print(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_print(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_print(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_print(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) { @@ -1735,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; @@ -1762,10 +1763,11 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, spin_unlock_bh(&priv->tx_lock); break; default: - ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, - "Unknown AMPDU action\n"); + ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } + mutex_unlock(&priv->mutex); + return ret; } @@ -1778,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); } @@ -1788,15 +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); - priv->op_flags |= OP_FULL_RESET; - 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); } |