ath9k_htc: Add support for bluetooth coexistence.
[pandora-kernel.git] / drivers / net / wireless / ath / ath9k / htc_drv_main.c
index 6bc05fe..5e318cb 100644 (file)
@@ -27,13 +27,11 @@ static struct dentry *ath9k_debugfs_root;
 static void ath_update_txpow(struct ath9k_htc_priv *priv)
 {
        struct ath_hw *ah = priv->ah;
-       u32 txpow;
 
        if (priv->curtxpow != priv->txpowlimit) {
                ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit);
                /* read back in case value is clamped */
-               ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
-               priv->curtxpow = txpow;
+               priv->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
        }
 }
 
@@ -127,6 +125,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
        struct ieee80211_conf *conf = &common->hw->conf;
        bool fastcc = true;
        struct ieee80211_channel *channel = hw->conf.channel;
+       struct ath9k_hw_cal_data *caldata;
        enum htc_phymode mode;
        __be16 htc_mode;
        u8 cmd_rsp;
@@ -151,7 +150,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
                  priv->ah->curchan->channel,
                  channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));
 
-       ret = ath9k_hw_reset(ah, hchan, fastcc);
+       caldata = &priv->caldata[channel->hw_value];
+       ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
        if (ret) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to reset channel (%u Mhz) "
@@ -325,9 +325,9 @@ static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
        tcap.flags_ext = 0x80601000;
        tcap.ampdu_limit = 0xffff0000;
        tcap.ampdu_subframes = 20;
-       tcap.tx_chainmask_legacy = 1;
+       tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
        tcap.protmode = 1;
-       tcap.tx_chainmask = 1;
+       tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
 
        WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
 
@@ -343,8 +343,7 @@ static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
        u32 caps = 0;
        int i, j;
 
-       /* Only 2GHz is supported */
-       sband = priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+       sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
 
        for (i = 0, j = 0; i < sband->n_bitrates; i++) {
                if (sta->supp_rates[sband->band] & BIT(i)) {
@@ -365,7 +364,10 @@ static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
                trate->rates.ht_rates.rs_nrates = j;
 
                caps = WLAN_RC_HT_FLAG;
-               if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+               if (sta->ht_cap.mcs.rx_mask[1])
+                       caps |= WLAN_RC_DS_FLAG;
+               if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
+                    (conf_is_ht40(&priv->hw->conf)))
                        caps |= WLAN_RC_40_FLAG;
                if (conf_is_ht40(&priv->hw->conf) &&
                    (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
@@ -439,13 +441,14 @@ static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
                          bss_conf->bssid, be32_to_cpu(trate.capflags));
 }
 
-static int ath9k_htc_aggr_oper(struct ath9k_htc_priv *priv,
-                              struct ieee80211_vif *vif,
-                              u8 *sta_addr, u8 tid, bool oper)
+static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sta *sta,
+                                 enum ieee80211_ampdu_mlme_action action,
+                                 u16 tid)
 {
        struct ath_common *common = ath9k_hw_common(priv->ah);
        struct ath9k_htc_target_aggr aggr;
-       struct ieee80211_sta *sta = NULL;
        struct ath9k_htc_sta *ista;
        int ret = 0;
        u8 cmd_rsp;
@@ -454,72 +457,28 @@ static int ath9k_htc_aggr_oper(struct ath9k_htc_priv *priv,
                return -EINVAL;
 
        memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
-
-       rcu_read_lock();
-
-       /* Check if we are able to retrieve the station */
-       sta = ieee80211_find_sta(vif, sta_addr);
-       if (!sta) {
-               rcu_read_unlock();
-               return -EINVAL;
-       }
-
        ista = (struct ath9k_htc_sta *) sta->drv_priv;
 
-       if (oper)
-               ista->tid_state[tid] = AGGR_START;
-       else
-               ista->tid_state[tid] = AGGR_STOP;
-
        aggr.sta_index = ista->index;
-
-       rcu_read_unlock();
-
-       aggr.tidno = tid;
-       aggr.aggr_enable = oper;
+       aggr.tidno = tid & 0xf;
+       aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
 
        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",
-                         (oper) ? "start" : "stop", sta->addr, tid);
+                         (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
        else
                ath_print(common, ATH_DBG_CONFIG,
-                         "%s aggregation for (%pM, %d)\n",
-                         (oper) ? "Starting" : "Stopping", sta->addr, tid);
-
-       return ret;
-}
-
-void ath9k_htc_aggr_work(struct work_struct *work)
-{
-       int ret = 0;
-       struct ath9k_htc_priv *priv =
-               container_of(work, struct ath9k_htc_priv,
-                            ath9k_aggr_work.work);
-       struct ath9k_htc_aggr_work *wk = &priv->aggr_work;
-
-       mutex_lock(&wk->mutex);
+                         "%s TX aggregation for (%pM, %d)\n",
+                         (aggr.aggr_enable) ? "Starting" : "Stopping",
+                         sta->addr, tid);
 
-       switch (wk->action) {
-       case IEEE80211_AMPDU_TX_START:
-               ret = ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr,
-                                         wk->tid, true);
-               if (!ret)
-                       ieee80211_start_tx_ba_cb(wk->vif, wk->sta_addr,
-                                                wk->tid);
-               break;
-       case IEEE80211_AMPDU_TX_STOP:
-               ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr,
-                                   wk->tid, false);
-               ieee80211_stop_tx_ba_cb(wk->vif, wk->sta_addr, wk->tid);
-               break;
-       default:
-               ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
-                         "Unknown AMPDU action\n");
-       }
+       spin_lock_bh(&priv->tx_lock);
+       ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
+       spin_unlock_bh(&priv->tx_lock);
 
-       mutex_unlock(&wk->mutex);
+       return ret;
 }
 
 /*********/
@@ -537,8 +496,7 @@ static int ath9k_debugfs_open(struct inode *inode, struct file *file)
 static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
                                   size_t count, loff_t *ppos)
 {
-       struct ath9k_htc_priv *priv =
-               (struct ath9k_htc_priv *) file->private_data;
+       struct ath9k_htc_priv *priv = file->private_data;
        struct ath9k_htc_target_stats cmd_rsp;
        char buf[512];
        unsigned int len = 0;
@@ -569,6 +527,9 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
        len += snprintf(buf + len, sizeof(buf) - len,
                        "%19s : %10u\n", "TX Rate", priv->debug.txrate);
 
+       if (len > sizeof(buf))
+               len = sizeof(buf);
+
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
@@ -581,8 +542,7 @@ static const struct file_operations fops_tgt_stats = {
 static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
-       struct ath9k_htc_priv *priv =
-               (struct ath9k_htc_priv *) file->private_data;
+       struct ath9k_htc_priv *priv = file->private_data;
        char buf[512];
        unsigned int len = 0;
 
@@ -615,6 +575,9 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
                        "%20s : %10u\n", "VO queued",
                        priv->debug.tx_stats.queue_stats[WME_AC_VO]);
 
+       if (len > sizeof(buf))
+               len = sizeof(buf);
+
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
@@ -627,8 +590,7 @@ static const struct file_operations fops_xmit = {
 static ssize_t read_file_recv(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
-       struct ath9k_htc_priv *priv =
-               (struct ath9k_htc_priv *) file->private_data;
+       struct ath9k_htc_priv *priv = file->private_data;
        char buf[512];
        unsigned int len = 0;
 
@@ -642,6 +604,9 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
                        "%20s : %10u\n", "SKBs Dropped",
                        priv->debug.rx_stats.skb_dropped);
 
+       if (len > sizeof(buf))
+               len = sizeof(buf);
+
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
@@ -976,6 +941,8 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv)
                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;
 
@@ -1064,7 +1031,7 @@ static void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
                ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
 
        /* Reset the HW */
-       ret = ath9k_hw_reset(ah, ah->curchan, false);
+       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 "
@@ -1127,7 +1094,7 @@ static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
                ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
 
        /* Reset the HW */
-       ret = ath9k_hw_reset(ah, ah->curchan, false);
+       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 "
@@ -1215,7 +1182,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
        ath9k_hw_configpcipowersave(ah, 0, 0);
 
        ath9k_hw_htc_resetinit(ah);
-       ret = ath9k_hw_reset(ah, init_channel, false);
+       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 "
@@ -1243,6 +1210,12 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
 
        ieee80211_wake_queues(hw);
 
+       if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
+               ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
+                                          AR_STOMP_LOW_WLAN_WGHT);
+               ath9k_hw_btcoex_enable(ah);
+               ath_htc_resume_btcoex_work(priv);
+       }
        mutex_unlock(&priv->mutex);
 
        return ret;
@@ -1267,7 +1240,6 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
        /* Cancel all the running timers/work .. */
        cancel_work_sync(&priv->ps_work);
        cancel_delayed_work_sync(&priv->ath9k_ani_work);
-       cancel_delayed_work_sync(&priv->ath9k_aggr_work);
        cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
        ath9k_led_stop_brightness(priv);
 
@@ -1288,6 +1260,12 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
                                  "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_hw_configpcipowersave(ah, 1, 1);
@@ -1516,32 +1494,38 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
        mutex_unlock(&priv->mutex);
 }
 
-static void ath9k_htc_sta_notify(struct ieee80211_hw *hw,
-                                struct ieee80211_vif *vif,
-                                enum sta_notify_cmd cmd,
-                                struct ieee80211_sta *sta)
+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;
        int ret;
 
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
+       ret = ath9k_htc_add_station(priv, vif, sta);
+       if (!ret)
+               ath9k_htc_init_rate(priv, sta);
+       ath9k_htc_ps_restore(priv);
+       mutex_unlock(&priv->mutex);
 
-       switch (cmd) {
-       case STA_NOTIFY_ADD:
-               ret = ath9k_htc_add_station(priv, vif, sta);
-               if (!ret)
-                       ath9k_htc_init_rate(priv, sta);
-               break;
-       case STA_NOTIFY_REMOVE:
-               ath9k_htc_remove_station(priv, vif, sta);
-               break;
-       default:
-               break;
-       }
+       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;
+       int ret;
 
+       mutex_lock(&priv->mutex);
+       ath9k_htc_ps_wakeup(priv);
+       ret = ath9k_htc_remove_station(priv, vif, sta);
        ath9k_htc_ps_restore(priv);
        mutex_unlock(&priv->mutex);
+
+       return ret;
 }
 
 static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
@@ -1574,9 +1558,15 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
                  params->cw_max, params->txop);
 
        ret = ath_htc_txq_update(priv, qnum, &qi);
-       if (ret)
+       if (ret) {
                ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
+               goto out;
+       }
 
+       if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
+           (qnum == priv->hwq_map[WME_AC_BE]))
+                   ath9k_htc_beaconq_config(priv);
+out:
        ath9k_htc_ps_restore(priv);
        mutex_unlock(&priv->mutex);
 
@@ -1607,9 +1597,10 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
                        key->hw_key_idx = ret;
                        /* push IV and Michael MIC generation to stack */
                        key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
-                       if (key->alg == ALG_TKIP)
+                       if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
                                key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
-                       if (priv->ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
+                       if (priv->ah->sw_mgmt_crypto &&
+                           key->cipher == WLAN_CIPHER_SUITE_CCMP)
                                key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
                        ret = 0;
                }
@@ -1756,8 +1747,8 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
                                  u16 tid, u16 *ssn)
 {
        struct ath9k_htc_priv *priv = hw->priv;
-       struct ath9k_htc_aggr_work *work = &priv->aggr_work;
        struct ath9k_htc_sta *ista;
+       int ret = 0;
 
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
@@ -1765,26 +1756,26 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
        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:
-               if (!(priv->op_flags & OP_TXAGGR))
-                       return -ENOTSUPP;
-               memcpy(work->sta_addr, sta->addr, ETH_ALEN);
-               work->hw = hw;
-               work->vif = vif;
-               work->action = action;
-               work->tid = tid;
-               ieee80211_queue_delayed_work(hw, &priv->ath9k_aggr_work, 0);
+               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_lock);
                ista->tid_state[tid] = AGGR_OPERATIONAL;
+               spin_unlock_bh(&priv->tx_lock);
                break;
        default:
                ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
                          "Unknown AMPDU action\n");
        }
 
-       return 0;
+       return ret;
 }
 
 static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
@@ -1843,7 +1834,8 @@ struct ieee80211_ops ath9k_htc_ops = {
        .remove_interface   = ath9k_htc_remove_interface,
        .config             = ath9k_htc_config,
        .configure_filter   = ath9k_htc_configure_filter,
-       .sta_notify         = ath9k_htc_sta_notify,
+       .sta_add            = ath9k_htc_sta_add,
+       .sta_remove         = ath9k_htc_sta_remove,
        .conf_tx            = ath9k_htc_conf_tx,
        .bss_info_changed   = ath9k_htc_bss_info_changed,
        .set_key            = ath9k_htc_set_key,