ath9k_htc: Add support for bluetooth coexistence.
[pandora-kernel.git] / drivers / net / wireless / ath / ath9k / htc_drv_main.c
index 8c463f5..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) "
@@ -364,12 +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 (priv->ah->caps.tx_chainmask != 1 &&
-                   ath9k_hw_getcapability(priv->ah, ATH9K_CAP_DS, 0, NULL)) {
-                       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)
+               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))
@@ -443,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;
@@ -458,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_irqsafe(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_irqsafe(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;
 }
 
 /*********/
@@ -541,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;
@@ -573,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);
 }
 
@@ -585,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;
 
@@ -619,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);
 }
 
@@ -631,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;
 
@@ -646,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);
 }
 
@@ -980,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;
 
@@ -1068,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 "
@@ -1131,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 "
@@ -1219,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 "
@@ -1247,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;
@@ -1271,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);
 
@@ -1292,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);
@@ -1590,7 +1564,7 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
        }
 
        if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
-           (qnum == priv->hwq_map[ATH9K_WME_AC_BE]))
+           (qnum == priv->hwq_map[WME_AC_BE]))
                    ath9k_htc_beaconq_config(priv);
 out:
        ath9k_htc_ps_restore(priv);
@@ -1623,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;
                }
@@ -1772,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:
@@ -1781,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)