iwlwifi: initial contextification
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / iwl-agn-lib.c
index 0f292a210ed92d1a67153234b56045647807f3f9..531a7dc3dc7287ec2035706045aaee08d094710d 100644 (file)
@@ -77,7 +77,7 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
                IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",
                                   agg->frame_count, agg->start_idx, idx);
 
-               info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]);
+               info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb);
                info->status.rates[0].count = tx_resp->failure_frame + 1;
                info->flags &= ~IEEE80211_TX_CTL_AMPDU;
                info->flags |= iwl_tx_status_to_mac80211(status);
@@ -93,6 +93,12 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
        } else {
                /* Two or more frames were attempted; expect block-ack */
                u64 bitmap = 0;
+
+               /*
+                * Start is the lowest frame sent. It may not be the first
+                * frame in the batch; we figure this out dynamically during
+                * the following loop.
+                */
                int start = agg->start_idx;
 
                /* Construct bit-map of pending frames within Tx window */
@@ -131,25 +137,58 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
                        IWL_DEBUG_TX_REPLY(priv, "AGG Frame i=%d idx %d seq=%d\n",
                                           i, idx, SEQ_TO_SN(sc));
 
+                       /*
+                        * sh -> how many frames ahead of the starting frame is
+                        * the current one?
+                        *
+                        * Note that all frames sent in the batch must be in a
+                        * 64-frame window, so this number should be in [0,63].
+                        * If outside of this window, then we've found a new
+                        * "first" frame in the batch and need to change start.
+                        */
                        sh = idx - start;
-                       if (sh > 64) {
-                               sh = (start - idx) + 0xff;
+
+                       /*
+                        * If >= 64, out of window. start must be at the front
+                        * of the circular buffer, idx must be near the end of
+                        * the buffer, and idx is the new "first" frame. Shift
+                        * the indices around.
+                        */
+                       if (sh >= 64) {
+                               /* Shift bitmap by start - idx, wrapped */
+                               sh = 0x100 - idx + start;
                                bitmap = bitmap << sh;
+                               /* Now idx is the new start so sh = 0 */
                                sh = 0;
                                start = idx;
-                       } else if (sh < -64)
-                               sh  = 0xff - (start - idx);
-                       else if (sh < 0) {
+                       /*
+                        * If <= -64 then wraps the 256-pkt circular buffer
+                        * (e.g., start = 255 and idx = 0, sh should be 1)
+                        */
+                       } else if (sh <= -64) {
+                               sh  = 0x100 - start + idx;
+                       /*
+                        * If < 0 but > -64, out of window. idx is before start
+                        * but not wrapped. Shift the indices around.
+                        */
+                       } else if (sh < 0) {
+                               /* Shift by how far start is ahead of idx */
                                sh = start - idx;
-                               start = idx;
                                bitmap = bitmap << sh;
+                               /* Now idx is the new start so sh = 0 */
+                               start = idx;
                                sh = 0;
                        }
+                       /* Sequence number start + sh was sent in this batch */
                        bitmap |= 1ULL << sh;
                        IWL_DEBUG_TX_REPLY(priv, "start=%d bitmap=0x%llx\n",
                                           start, (unsigned long long)bitmap);
                }
 
+               /*
+                * Store the bitmap and possibly the new start, if we wrapped
+                * the buffer above
+                */
                agg->bitmap = bitmap;
                agg->start_idx = start;
                IWL_DEBUG_TX_REPLY(priv, "Frames %d start_idx=%d bitmap=0x%llx\n",
@@ -166,7 +205,9 @@ void iwl_check_abort_status(struct iwl_priv *priv,
                            u8 frame_count, u32 status)
 {
        if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
-               IWL_ERR(priv, "TODO: Implement Tx flush command!!!\n");
+               IWL_ERR(priv, "Tx flush command to flush out all frames\n");
+               if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
+                       queue_work(priv->workqueue, &priv->tx_flush);
        }
 }
 
@@ -184,6 +225,7 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
        int tid;
        int sta_id;
        int freed;
+       unsigned long flags;
 
        if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) {
                IWL_ERR(priv, "Read index for DMA queue txq_id (%d) index %d "
@@ -193,18 +235,26 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
                return;
        }
 
-       info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb[0]);
+       info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb);
        memset(&info->status, 0, sizeof(info->status));
 
        tid = (tx_resp->ra_tid & IWL50_TX_RES_TID_MSK) >> IWL50_TX_RES_TID_POS;
        sta_id = (tx_resp->ra_tid & IWL50_TX_RES_RA_MSK) >> IWL50_TX_RES_RA_POS;
 
+       spin_lock_irqsave(&priv->sta_lock, flags);
        if (txq->sched_retry) {
                const u32 scd_ssn = iwlagn_get_scd_ssn(tx_resp);
-               struct iwl_ht_agg *agg = NULL;
+               struct iwl_ht_agg *agg;
 
                agg = &priv->stations[sta_id].tid[tid].agg;
-
+               /*
+                * If the BT kill count is non-zero, we'll get this
+                * notification again.
+                */
+               if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
+                   priv->cfg->advanced_bt_coexist) {
+                       IWL_WARN(priv, "receive reply tx with bt_kill\n");
+               }
                iwlagn_tx_status_reply_tx(priv, agg, tx_resp, txq_id, index);
 
                /* check if BAR is needed */
@@ -256,6 +306,7 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
        iwlagn_txq_check_empty(priv, sta_id, tid, txq_id);
 
        iwl_check_abort_status(priv, tx_resp->frame_count, status);
+       spin_unlock_irqrestore(&priv->sta_lock, flags);
 }
 
 void iwlagn_rx_handler_setup(struct iwl_priv *priv)
@@ -319,7 +370,8 @@ int iwlagn_send_tx_power(struct iwl_priv *priv)
 void iwlagn_temperature(struct iwl_priv *priv)
 {
        /* store temperature from statistics (in Celsius) */
-       priv->temperature = le32_to_cpu(priv->statistics.general.temperature);
+       priv->temperature =
+               le32_to_cpu(priv->_agn.statistics.general.common.temperature);
        iwl_tt_handler(priv);
 }
 
@@ -444,7 +496,7 @@ int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
 
        /* Tell device where to find RBD circular buffer in DRAM */
        iwl_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG,
-                          (u32)(rxq->dma_addr >> 8));
+                          (u32)(rxq->bd_dma >> 8));
 
        /* Tell device where in DRAM to update its Rx status */
        iwl_write_direct32(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG,
@@ -709,7 +761,7 @@ void iwlagn_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
        }
 
        dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
-                         rxq->dma_addr);
+                         rxq->bd_dma);
        dma_free_coherent(&priv->pci_dev->dev, sizeof(struct iwl_rb_status),
                          rxq->rb_stts, rxq->rb_stts_dma);
        rxq->bd = NULL;
@@ -755,132 +807,6 @@ static inline int iwlagn_calc_rssi(struct iwl_priv *priv,
        return priv->cfg->ops->utils->calc_rssi(priv, rx_resp);
 }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-/**
- * iwlagn_dbg_report_frame - dump frame to syslog during debug sessions
- *
- * You may hack this function to show different aspects of received frames,
- * including selective frame dumps.
- * group100 parameter selects whether to show 1 out of 100 good data frames.
- *    All beacon and probe response frames are printed.
- */
-static void iwlagn_dbg_report_frame(struct iwl_priv *priv,
-                     struct iwl_rx_phy_res *phy_res, u16 length,
-                     struct ieee80211_hdr *header, int group100)
-{
-       u32 to_us;
-       u32 print_summary = 0;
-       u32 print_dump = 0;     /* set to 1 to dump all frames' contents */
-       u32 hundred = 0;
-       u32 dataframe = 0;
-       __le16 fc;
-       u16 seq_ctl;
-       u16 channel;
-       u16 phy_flags;
-       u32 rate_n_flags;
-       u32 tsf_low;
-       int rssi;
-
-       if (likely(!(iwl_get_debug_level(priv) & IWL_DL_RX)))
-               return;
-
-       /* MAC header */
-       fc = header->frame_control;
-       seq_ctl = le16_to_cpu(header->seq_ctrl);
-
-       /* metadata */
-       channel = le16_to_cpu(phy_res->channel);
-       phy_flags = le16_to_cpu(phy_res->phy_flags);
-       rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);
-
-       /* signal statistics */
-       rssi = iwlagn_calc_rssi(priv, phy_res);
-       tsf_low = le64_to_cpu(phy_res->timestamp) & 0x0ffffffff;
-
-       to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
-
-       /* if data frame is to us and all is good,
-        *   (optionally) print summary for only 1 out of every 100 */
-       if (to_us && (fc & ~cpu_to_le16(IEEE80211_FCTL_PROTECTED)) ==
-           cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
-               dataframe = 1;
-               if (!group100)
-                       print_summary = 1;      /* print each frame */
-               else if (priv->framecnt_to_us < 100) {
-                       priv->framecnt_to_us++;
-                       print_summary = 0;
-               } else {
-                       priv->framecnt_to_us = 0;
-                       print_summary = 1;
-                       hundred = 1;
-               }
-       } else {
-               /* print summary for all other frames */
-               print_summary = 1;
-       }
-
-       if (print_summary) {
-               char *title;
-               int rate_idx;
-               u32 bitrate;
-
-               if (hundred)
-                       title = "100Frames";
-               else if (ieee80211_has_retry(fc))
-                       title = "Retry";
-               else if (ieee80211_is_assoc_resp(fc))
-                       title = "AscRsp";
-               else if (ieee80211_is_reassoc_resp(fc))
-                       title = "RasRsp";
-               else if (ieee80211_is_probe_resp(fc)) {
-                       title = "PrbRsp";
-                       print_dump = 1; /* dump frame contents */
-               } else if (ieee80211_is_beacon(fc)) {
-                       title = "Beacon";
-                       print_dump = 1; /* dump frame contents */
-               } else if (ieee80211_is_atim(fc))
-                       title = "ATIM";
-               else if (ieee80211_is_auth(fc))
-                       title = "Auth";
-               else if (ieee80211_is_deauth(fc))
-                       title = "DeAuth";
-               else if (ieee80211_is_disassoc(fc))
-                       title = "DisAssoc";
-               else
-                       title = "Frame";
-
-               rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
-               if (unlikely((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT))) {
-                       bitrate = 0;
-                       WARN_ON_ONCE(1);
-               } else {
-                       bitrate = iwl_rates[rate_idx].ieee / 2;
-               }
-
-               /* print frame summary.
-                * MAC addresses show just the last byte (for brevity),
-                *    but you can hack it to show more, if you'd like to. */
-               if (dataframe)
-                       IWL_DEBUG_RX(priv, "%s: mhd=0x%04x, dst=0x%02x, "
-                                    "len=%u, rssi=%d, chnl=%d, rate=%u,\n",
-                                    title, le16_to_cpu(fc), header->addr1[5],
-                                    length, rssi, channel, bitrate);
-               else {
-                       /* src/dst addresses assume managed mode */
-                       IWL_DEBUG_RX(priv, "%s: 0x%04x, dst=0x%02x, src=0x%02x, "
-                                    "len=%u, rssi=%d, tim=%lu usec, "
-                                    "phy=0x%02x, chnl=%d\n",
-                                    title, le16_to_cpu(fc), header->addr1[5],
-                                    header->addr3[5], length, rssi,
-                                    tsf_low - priv->scan_start_tsf,
-                                    phy_flags, channel);
-               }
-       }
-       if (print_dump)
-               iwl_print_hex_dump(priv, IWL_DL_RX, header, length);
-}
-#endif
-
 static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
 {
        u32 decrypt_out = 0;
@@ -988,7 +914,7 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv,
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_rx_phy_res *phy_res;
        __le32 rx_pkt_status;
-       struct iwl4965_rx_mpdu_res_start *amsdu;
+       struct iwl_rx_mpdu_res_start *amsdu;
        u32 len;
        u32 ampdu_status;
        u32 rate_n_flags;
@@ -1017,7 +943,7 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv,
                        return;
                }
                phy_res = &priv->_agn.last_phy_res;
-               amsdu = (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw;
+               amsdu = (struct iwl_rx_mpdu_res_start *)pkt->u.raw;
                header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu));
                len = le16_to_cpu(amsdu->byte_count);
                rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*amsdu) + len);
@@ -1060,11 +986,6 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv,
        /* Find max signal strength (dBm) among 3 antenna/receiver chains */
        rx_status.signal = iwlagn_calc_rssi(priv, phy_res);
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       /* Set "1" to report good data frames in groups of 100 */
-       if (unlikely(iwl_get_debug_level(priv) & IWL_DL_RX))
-               iwlagn_dbg_report_frame(priv, phy_res, len, header, 1);
-#endif
        iwl_dbg_log_rx_data_frame(priv, len, header);
        IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n",
                rx_status.signal, (unsigned long long)rx_status.mactime);
@@ -1184,7 +1105,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
                if (chan->band != band)
                        continue;
 
-               channel = ieee80211_frequency_to_channel(chan->center_freq);
+               channel = chan->hw_value;
                scan_ch->channel = cpu_to_le16(channel);
 
                ch_info = iwl_get_channel_info(priv, band, channel);
@@ -1252,6 +1173,7 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        bool is_active = false;
        int  chan_mod;
        u8 active_chains;
+       u8 scan_tx_antennas = priv->hw_params.valid_tx_ant;
 
        conf = ieee80211_get_hw_conf(priv->hw);
 
@@ -1310,7 +1232,7 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
        scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
 
-       if (iwl_is_associated(priv)) {
+       if (iwl_is_any_associated(priv)) {
                u16 interval = 0;
                u32 extra;
                u32 suspend_time = 100;
@@ -1319,7 +1241,10 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
                IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
                spin_lock_irqsave(&priv->lock, flags);
-               interval = vif ? vif->bss_conf.beacon_int : 0;
+               if (priv->is_internal_short_scan)
+                       interval = 0;
+               else
+                       interval = vif->bss_conf.beacon_int;
                spin_unlock_irqrestore(&priv->lock, flags);
 
                scan->suspend_time = 0;
@@ -1364,7 +1289,9 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        switch (priv->scan_band) {
        case IEEE80211_BAND_2GHZ:
                scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
-               chan_mod = le32_to_cpu(priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_MSK)
+               chan_mod = le32_to_cpu(
+                       priv->contexts[IWL_RXON_CTX_BSS].active.flags &
+                                               RXON_FLG_CHANNEL_MODE_MSK)
                                       >> RXON_FLG_CHANNEL_MODE_POS;
                if (chan_mod == CHANNEL_MODE_PURE_40) {
                        rate = IWL_RATE_6M_PLCP;
@@ -1372,6 +1299,12 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                        rate = IWL_RATE_1M_PLCP;
                        rate_flags = RATE_MCS_CCK_MSK;
                }
+               /*
+                * Internal scans are passive, so we can indiscriminately set
+                * the BT ignore flag on 2.4 GHz since it applies to TX only.
+                */
+               if (priv->cfg->advanced_bt_coexist)
+                       scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
                scan->good_CRC_th = IWL_GOOD_CRC_TH_DISABLED;
                break;
        case IEEE80211_BAND_5GHZ:
@@ -1403,11 +1336,20 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
        band = priv->scan_band;
 
-       if (priv->cfg->scan_antennas[band])
-               rx_ant = priv->cfg->scan_antennas[band];
+       if (priv->cfg->scan_rx_antennas[band])
+               rx_ant = priv->cfg->scan_rx_antennas[band];
+
+       if (priv->cfg->scan_tx_antennas[band])
+               scan_tx_antennas = priv->cfg->scan_tx_antennas[band];
+
+       if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
+               /* operated as 1x1 in full concurrency mode */
+               scan_tx_antennas =
+                       first_antenna(priv->cfg->scan_tx_antennas[band]);
+       }
 
-       priv->scan_tx_ant[band] =
-                       iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band]);
+       priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band],
+                                                   scan_tx_antennas);
        rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
        scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
 
@@ -1424,6 +1366,11 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
                rx_ant = first_antenna(active_chains);
        }
+       if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
+               /* operated as 1x1 in full concurrency mode */
+               rx_ant = first_antenna(rx_ant);
+       }
+
        /* MIMO is not used here, but value is required */
        rx_chain |= priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
        rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
@@ -1433,13 +1380,15 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        if (!priv->is_internal_short_scan) {
                cmd_len = iwl_fill_probe_req(priv,
                                        (struct ieee80211_mgmt *)scan->data,
+                                       vif->addr,
                                        priv->scan_request->ie,
                                        priv->scan_request->ie_len,
                                        IWL_MAX_SCAN_SIZE - sizeof(*scan));
        } else {
+               /* use bcast addr, will not be transmitted but must be valid */
                cmd_len = iwl_fill_probe_req(priv,
                                        (struct ieee80211_mgmt *)scan->data,
-                                       NULL, 0,
+                                       iwl_bcast_addr, NULL, 0,
                                        IWL_MAX_SCAN_SIZE - sizeof(*scan));
 
        }
@@ -1502,3 +1451,466 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv,
        return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id,
                                  vif->bss_conf.bssid);
 }
+
+void iwl_free_tfds_in_queue(struct iwl_priv *priv,
+                           int sta_id, int tid, int freed)
+{
+       lockdep_assert_held(&priv->sta_lock);
+
+       if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
+               priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+       else {
+               IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n",
+                       priv->stations[sta_id].tid[tid].tfds_in_queue,
+                       freed);
+               priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
+       }
+}
+
+#define IWL_FLUSH_WAIT_MS      2000
+
+int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv)
+{
+       struct iwl_tx_queue *txq;
+       struct iwl_queue *q;
+       int cnt;
+       unsigned long now = jiffies;
+       int ret = 0;
+
+       /* waiting for all the tx frames complete might take a while */
+       for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
+               if (cnt == IWL_CMD_QUEUE_NUM)
+                       continue;
+               txq = &priv->txq[cnt];
+               q = &txq->q;
+               while (q->read_ptr != q->write_ptr && !time_after(jiffies,
+                      now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS)))
+                               msleep(1);
+
+               if (q->read_ptr != q->write_ptr) {
+                       IWL_ERR(priv, "fail to flush all tx fifo queues\n");
+                       ret = -ETIMEDOUT;
+                       break;
+               }
+       }
+       return ret;
+}
+
+#define IWL_TX_QUEUE_MSK       0xfffff
+
+/**
+ * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode
+ *
+ * pre-requirements:
+ *  1. acquire mutex before calling
+ *  2. make sure rf is on and not in exit state
+ */
+int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
+{
+       struct iwl_txfifo_flush_cmd flush_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = REPLY_TXFIFO_FLUSH,
+               .len = sizeof(struct iwl_txfifo_flush_cmd),
+               .flags = CMD_SYNC,
+               .data = &flush_cmd,
+       };
+
+       might_sleep();
+
+       memset(&flush_cmd, 0, sizeof(flush_cmd));
+       flush_cmd.fifo_control = IWL_TX_FIFO_VO_MSK | IWL_TX_FIFO_VI_MSK |
+                                IWL_TX_FIFO_BE_MSK | IWL_TX_FIFO_BK_MSK;
+       if (priv->cfg->sku & IWL_SKU_N)
+               flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK;
+
+       IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n",
+                      flush_cmd.fifo_control);
+       flush_cmd.flush_control = cpu_to_le16(flush_control);
+
+       return iwl_send_cmd(priv, &cmd);
+}
+
+void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
+{
+       mutex_lock(&priv->mutex);
+       ieee80211_stop_queues(priv->hw);
+       if (priv->cfg->ops->lib->txfifo_flush(priv, IWL_DROP_ALL)) {
+               IWL_ERR(priv, "flush request fail\n");
+               goto done;
+       }
+       IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
+       iwlagn_wait_tx_queue_empty(priv);
+done:
+       ieee80211_wake_queues(priv->hw);
+       mutex_unlock(&priv->mutex);
+}
+
+/*
+ * BT coex
+ */
+/*
+ * Macros to access the lookup table.
+ *
+ * The lookup table has 7 inputs: bt3_prio, bt3_txrx, bt_rf_act, wifi_req,
+* wifi_prio, wifi_txrx and wifi_sh_ant_req.
+ *
+ * It has three outputs: WLAN_ACTIVE, WLAN_KILL and ANT_SWITCH
+ *
+ * The format is that "registers" 8 through 11 contain the WLAN_ACTIVE bits
+ * one after another in 32-bit registers, and "registers" 0 through 7 contain
+ * the WLAN_KILL and ANT_SWITCH bits interleaved (in that order).
+ *
+ * These macros encode that format.
+ */
+#define LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, wifi_req, wifi_prio, \
+                 wifi_txrx, wifi_sh_ant_req) \
+       (bt3_prio | (bt3_txrx << 1) | (bt_rf_act << 2) | (wifi_req << 3) | \
+       (wifi_prio << 4) | (wifi_txrx << 5) | (wifi_sh_ant_req << 6))
+
+#define LUT_PTA_WLAN_ACTIVE_OP(lut, op, val) \
+       lut[8 + ((val) >> 5)] op (cpu_to_le32(BIT((val) & 0x1f)))
+#define LUT_TEST_PTA_WLAN_ACTIVE(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                                wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       (!!(LUT_PTA_WLAN_ACTIVE_OP(lut, &, LUT_VALUE(bt3_prio, bt3_txrx, \
+                                  bt_rf_act, wifi_req, wifi_prio, wifi_txrx, \
+                                  wifi_sh_ant_req))))
+#define LUT_SET_PTA_WLAN_ACTIVE(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                               wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       LUT_PTA_WLAN_ACTIVE_OP(lut, |=, LUT_VALUE(bt3_prio, bt3_txrx, \
+                              bt_rf_act, wifi_req, wifi_prio, wifi_txrx, \
+                              wifi_sh_ant_req))
+#define LUT_CLEAR_PTA_WLAN_ACTIVE(lut, bt3_prio, bt3_txrx, bt_rf_act, \
+                                 wifi_req, wifi_prio, wifi_txrx, \
+                                 wifi_sh_ant_req) \
+       LUT_PTA_WLAN_ACTIVE_OP(lut, &= ~, LUT_VALUE(bt3_prio, bt3_txrx, \
+                              bt_rf_act, wifi_req, wifi_prio, wifi_txrx, \
+                              wifi_sh_ant_req))
+
+#define LUT_WLAN_KILL_OP(lut, op, val) \
+       lut[(val) >> 4] op (cpu_to_le32(BIT(((val) << 1) & 0x1e)))
+#define LUT_TEST_WLAN_KILL(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                          wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       (!!(LUT_WLAN_KILL_OP(lut, &, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \
+                            wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req))))
+#define LUT_SET_WLAN_KILL(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                         wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       LUT_WLAN_KILL_OP(lut, |=, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \
+                        wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req))
+#define LUT_CLEAR_WLAN_KILL(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                           wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       LUT_WLAN_KILL_OP(lut, &= ~, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \
+                        wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req))
+
+#define LUT_ANT_SWITCH_OP(lut, op, val) \
+       lut[(val) >> 4] op (cpu_to_le32(BIT((((val) << 1) & 0x1e) + 1)))
+#define LUT_TEST_ANT_SWITCH(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                           wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       (!!(LUT_ANT_SWITCH_OP(lut, &, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \
+                             wifi_req, wifi_prio, wifi_txrx, \
+                             wifi_sh_ant_req))))
+#define LUT_SET_ANT_SWITCH(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                          wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       LUT_ANT_SWITCH_OP(lut, |=, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \
+                         wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req))
+#define LUT_CLEAR_ANT_SWITCH(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \
+                            wifi_prio, wifi_txrx, wifi_sh_ant_req) \
+       LUT_ANT_SWITCH_OP(lut, &= ~, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \
+                         wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req))
+
+static const __le32 iwlagn_def_3w_lookup[12] = {
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaeaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xcc00ff28),
+       cpu_to_le32(0x0000aaaa),
+       cpu_to_le32(0xcc00aaaa),
+       cpu_to_le32(0x0000aaaa),
+       cpu_to_le32(0xc0004000),
+       cpu_to_le32(0x00004000),
+       cpu_to_le32(0xf0005000),
+       cpu_to_le32(0xf0004000),
+};
+
+static const __le32 iwlagn_concurrent_lookup[12] = {
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x00000000),
+};
+
+void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
+{
+       struct iwlagn_bt_cmd bt_cmd = {
+               .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT,
+               .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT,
+               .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT,
+               .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT,
+       };
+
+       BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
+                       sizeof(bt_cmd.bt3_lookup_table));
+
+       bt_cmd.prio_boost = priv->cfg->bt_prio_boost;
+       bt_cmd.kill_ack_mask = priv->kill_ack_mask;
+       bt_cmd.kill_cts_mask = priv->kill_cts_mask;
+       bt_cmd.valid = priv->bt_valid;
+
+       /*
+        * Configure BT coex mode to "no coexistence" when the
+        * user disabled BT coexistence, we have no interface
+        * (might be in monitor mode), or the interface is in
+        * IBSS mode (no proper uCode support for coex then).
+        */
+       if (!bt_coex_active || priv->iw_mode == NL80211_IFTYPE_ADHOC) {
+               bt_cmd.flags = 0;
+       } else {
+               bt_cmd.flags = IWLAGN_BT_FLAG_COEX_MODE_3W <<
+                                       IWLAGN_BT_FLAG_COEX_MODE_SHIFT;
+               if (priv->bt_ch_announce)
+                       bt_cmd.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION;
+               IWL_DEBUG_INFO(priv, "BT coex flag: 0X%x\n", bt_cmd.flags);
+       }
+       if (priv->bt_full_concurrent)
+               memcpy(bt_cmd.bt3_lookup_table, iwlagn_concurrent_lookup,
+                       sizeof(iwlagn_concurrent_lookup));
+       else
+               memcpy(bt_cmd.bt3_lookup_table, iwlagn_def_3w_lookup,
+                       sizeof(iwlagn_def_3w_lookup));
+
+       IWL_DEBUG_INFO(priv, "BT coex %s in %s mode\n",
+                      bt_cmd.flags ? "active" : "disabled",
+                      priv->bt_full_concurrent ?
+                      "full concurrency" : "3-wire");
+
+       if (iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, sizeof(bt_cmd), &bt_cmd))
+               IWL_ERR(priv, "failed to send BT Coex Config\n");
+
+       /*
+        * When we are doing a restart, need to also reconfigure BT
+        * SCO to the device. If not doing a restart, bt_sco_active
+        * will always be false, so there's no need to have an extra
+        * variable to check for it.
+        */
+       if (priv->bt_sco_active) {
+               struct iwlagn_bt_sco_cmd sco_cmd = { .flags = 0 };
+
+               if (priv->bt_sco_active)
+                       sco_cmd.flags |= IWLAGN_BT_SCO_ACTIVE;
+               if (iwl_send_cmd_pdu(priv, REPLY_BT_COEX_SCO,
+                                    sizeof(sco_cmd), &sco_cmd))
+                       IWL_ERR(priv, "failed to send BT SCO command\n");
+       }
+}
+
+static void iwlagn_bt_traffic_change_work(struct work_struct *work)
+{
+       struct iwl_priv *priv =
+               container_of(work, struct iwl_priv, bt_traffic_change_work);
+       int smps_request = -1;
+
+       IWL_DEBUG_INFO(priv, "BT traffic load changes: %d\n",
+                      priv->bt_traffic_load);
+
+       switch (priv->bt_traffic_load) {
+       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+               smps_request = IEEE80211_SMPS_AUTOMATIC;
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+               smps_request = IEEE80211_SMPS_DYNAMIC;
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+               smps_request = IEEE80211_SMPS_STATIC;
+               break;
+       default:
+               IWL_ERR(priv, "Invalid BT traffic load: %d\n",
+                       priv->bt_traffic_load);
+               break;
+       }
+
+       mutex_lock(&priv->mutex);
+
+       if (priv->cfg->ops->lib->update_chain_flags)
+               priv->cfg->ops->lib->update_chain_flags(priv);
+
+       if (smps_request != -1 &&
+           priv->vif && priv->vif->type == NL80211_IFTYPE_STATION)
+               ieee80211_request_smps(priv->vif, smps_request);
+
+       mutex_unlock(&priv->mutex);
+}
+
+static void iwlagn_print_uartmsg(struct iwl_priv *priv,
+                               struct iwl_bt_uart_msg *uart_msg)
+{
+       IWL_DEBUG_NOTIF(priv, "Message Type = 0x%X, SSN = 0x%X, "
+                       "Update Req = 0x%X",
+               (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >>
+                       BT_UART_MSG_FRAME1MSGTYPE_POS,
+               (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >>
+                       BT_UART_MSG_FRAME1SSN_POS,
+               (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >>
+                       BT_UART_MSG_FRAME1UPDATEREQ_POS);
+
+       IWL_DEBUG_NOTIF(priv, "Open connections = 0x%X, Traffic load = 0x%X, "
+                       "Chl_SeqN = 0x%X, In band = 0x%X",
+               (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >>
+                       BT_UART_MSG_FRAME2OPENCONNECTIONS_POS,
+               (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >>
+                       BT_UART_MSG_FRAME2TRAFFICLOAD_POS,
+               (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >>
+                       BT_UART_MSG_FRAME2CHLSEQN_POS,
+               (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >>
+                       BT_UART_MSG_FRAME2INBAND_POS);
+
+       IWL_DEBUG_NOTIF(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, "
+                       "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X",
+               (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
+                       BT_UART_MSG_FRAME3SCOESCO_POS,
+               (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >>
+                       BT_UART_MSG_FRAME3SNIFF_POS,
+               (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >>
+                       BT_UART_MSG_FRAME3A2DP_POS,
+               (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >>
+                       BT_UART_MSG_FRAME3ACL_POS,
+               (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >>
+                       BT_UART_MSG_FRAME3MASTER_POS,
+               (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >>
+                       BT_UART_MSG_FRAME3OBEX_POS);
+
+       IWL_DEBUG_NOTIF(priv, "Idle duration = 0x%X",
+               (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >>
+                       BT_UART_MSG_FRAME4IDLEDURATION_POS);
+
+       IWL_DEBUG_NOTIF(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, "
+                       "eSCO Retransmissions = 0x%X",
+               (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >>
+                       BT_UART_MSG_FRAME5TXACTIVITY_POS,
+               (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >>
+                       BT_UART_MSG_FRAME5RXACTIVITY_POS,
+               (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >>
+                       BT_UART_MSG_FRAME5ESCORETRANSMIT_POS);
+
+       IWL_DEBUG_NOTIF(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X",
+               (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >>
+                       BT_UART_MSG_FRAME6SNIFFINTERVAL_POS,
+               (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >>
+                       BT_UART_MSG_FRAME6DISCOVERABLE_POS);
+
+       IWL_DEBUG_NOTIF(priv, "Sniff Activity = 0x%X, Inquiry/Page SR Mode = "
+                       "0x%X, Connectable = 0x%X",
+               (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >>
+                       BT_UART_MSG_FRAME7SNIFFACTIVITY_POS,
+               (BT_UART_MSG_FRAME7INQUIRYPAGESRMODE_MSK & uart_msg->frame7) >>
+                       BT_UART_MSG_FRAME7INQUIRYPAGESRMODE_POS,
+               (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >>
+                       BT_UART_MSG_FRAME7CONNECTABLE_POS);
+}
+
+static void iwlagn_set_kill_ack_msk(struct iwl_priv *priv,
+                                    struct iwl_bt_uart_msg *uart_msg)
+{
+       u8 kill_ack_msk;
+       __le32 bt_kill_ack_msg[2] = {
+                       cpu_to_le32(0xFFFFFFF), cpu_to_le32(0xFFFFFC00) };
+
+       kill_ack_msk = (((BT_UART_MSG_FRAME3A2DP_MSK |
+                       BT_UART_MSG_FRAME3SNIFF_MSK |
+                       BT_UART_MSG_FRAME3SCOESCO_MSK) &
+                       uart_msg->frame3) == 0) ? 1 : 0;
+       if (priv->kill_ack_mask != bt_kill_ack_msg[kill_ack_msk]) {
+               priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK;
+               priv->kill_ack_mask = bt_kill_ack_msg[kill_ack_msk];
+               /* schedule to send runtime bt_config */
+               queue_work(priv->workqueue, &priv->bt_runtime_config);
+       }
+
+}
+
+void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
+                                            struct iwl_rx_mem_buffer *rxb)
+{
+       unsigned long flags;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_bt_coex_profile_notif *coex = &pkt->u.bt_coex_profile_notif;
+       struct iwlagn_bt_sco_cmd sco_cmd = { .flags = 0 };
+       struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg;
+       u8 last_traffic_load;
+
+       IWL_DEBUG_NOTIF(priv, "BT Coex notification:\n");
+       IWL_DEBUG_NOTIF(priv, "    status: %d\n", coex->bt_status);
+       IWL_DEBUG_NOTIF(priv, "    traffic load: %d\n", coex->bt_traffic_load);
+       IWL_DEBUG_NOTIF(priv, "    CI compliance: %d\n",
+                       coex->bt_ci_compliance);
+       iwlagn_print_uartmsg(priv, uart_msg);
+
+       last_traffic_load = priv->notif_bt_traffic_load;
+       priv->notif_bt_traffic_load = coex->bt_traffic_load;
+       if (priv->iw_mode != NL80211_IFTYPE_ADHOC) {
+               if (priv->bt_status != coex->bt_status ||
+                   last_traffic_load != coex->bt_traffic_load) {
+                       if (coex->bt_status) {
+                               /* BT on */
+                               if (!priv->bt_ch_announce)
+                                       priv->bt_traffic_load =
+                                               IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
+                               else
+                                       priv->bt_traffic_load =
+                                               coex->bt_traffic_load;
+                       } else {
+                               /* BT off */
+                               priv->bt_traffic_load =
+                                       IWL_BT_COEX_TRAFFIC_LOAD_NONE;
+                       }
+                       priv->bt_status = coex->bt_status;
+                       queue_work(priv->workqueue,
+                                  &priv->bt_traffic_change_work);
+               }
+               if (priv->bt_sco_active !=
+                   (uart_msg->frame3 & BT_UART_MSG_FRAME3SCOESCO_MSK)) {
+                       priv->bt_sco_active = uart_msg->frame3 &
+                               BT_UART_MSG_FRAME3SCOESCO_MSK;
+                       if (priv->bt_sco_active)
+                               sco_cmd.flags |= IWLAGN_BT_SCO_ACTIVE;
+                       iwl_send_cmd_pdu_async(priv, REPLY_BT_COEX_SCO,
+                                      sizeof(sco_cmd), &sco_cmd, NULL);
+               }
+       }
+
+       iwlagn_set_kill_ack_msk(priv, uart_msg);
+
+       /* FIXME: based on notification, adjust the prio_boost */
+
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->bt_ci_compliance = coex->bt_ci_compliance;
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv)
+{
+       iwlagn_rx_handler_setup(priv);
+       priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] =
+               iwlagn_bt_coex_profile_notif;
+}
+
+void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv)
+{
+       iwlagn_setup_deferred_work(priv);
+
+       INIT_WORK(&priv->bt_traffic_change_work,
+                 iwlagn_bt_traffic_change_work);
+}
+
+void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv)
+{
+       cancel_work_sync(&priv->bt_traffic_change_work);
+}
This page took 0.036891 seconds and 5 git commands to generate.