aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorWey-Yi Guy <wey-yi.w.guy@intel.com>2010-06-24 16:22:36 -0400
committerWey-Yi Guy <wey-yi.w.guy@intel.com>2010-07-02 14:10:33 -0400
commit716c74b00717ad9caedb4a46059fb64a3da99808 (patch)
tree2343795f1314e36d51c3101502dde6aadb34d202 /drivers
parent947279eefb77f79015a79b032eb825a065ab035f (diff)
iwlwifi: add mac80211 flush callback support
Adding flush callback support in the driver. Two type of flush can be issued by mac80211: 1. drop = true: frame drop is ok, issue REPLY_TXFIFO_FLUSH host command to uCode to drop all the frames in tx fifo queues; then return the control back to mac80211 2. drop = false: wait for either all the frames in tx fifo queues been transmitted, or timeout; then return the control back to mac80211 If the flush request coming from mac80211, mac80211 will make sure there are no additional frames push down to driver before flush operation is completed. Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-lib.c63
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c39
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-commands.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h2
8 files changed, 114 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 1daf159914ad..00808ee5ce2a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -226,6 +226,7 @@ static struct iwl_lib_ops iwl1000_lib = {
226 .recover_from_tx_stall = iwl_bg_monitor_recover, 226 .recover_from_tx_stall = iwl_bg_monitor_recover,
227 .check_plcp_health = iwl_good_plcp_health, 227 .check_plcp_health = iwl_good_plcp_health,
228 .check_ack_health = iwl_good_ack_health, 228 .check_ack_health = iwl_good_ack_health,
229 .txfifo_flush = iwlagn_txfifo_flush,
229}; 230};
230 231
231static const struct iwl_ops iwl1000_ops = { 232static const struct iwl_ops iwl1000_ops = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index b8f3e20f2c80..1182498c1d8f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -402,6 +402,7 @@ static struct iwl_lib_ops iwl5000_lib = {
402 .recover_from_tx_stall = iwl_bg_monitor_recover, 402 .recover_from_tx_stall = iwl_bg_monitor_recover,
403 .check_plcp_health = iwl_good_plcp_health, 403 .check_plcp_health = iwl_good_plcp_health,
404 .check_ack_health = iwl_good_ack_health, 404 .check_ack_health = iwl_good_ack_health,
405 .txfifo_flush = iwlagn_txfifo_flush,
405}; 406};
406 407
407static struct iwl_lib_ops iwl5150_lib = { 408static struct iwl_lib_ops iwl5150_lib = {
@@ -465,6 +466,7 @@ static struct iwl_lib_ops iwl5150_lib = {
465 .recover_from_tx_stall = iwl_bg_monitor_recover, 466 .recover_from_tx_stall = iwl_bg_monitor_recover,
466 .check_plcp_health = iwl_good_plcp_health, 467 .check_plcp_health = iwl_good_plcp_health,
467 .check_ack_health = iwl_good_ack_health, 468 .check_ack_health = iwl_good_ack_health,
469 .txfifo_flush = iwlagn_txfifo_flush,
468}; 470};
469 471
470static const struct iwl_ops iwl5000_ops = { 472static const struct iwl_ops iwl5000_ops = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 8577664da77c..e1959fbafd00 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -327,6 +327,7 @@ static struct iwl_lib_ops iwl6000_lib = {
327 .recover_from_tx_stall = iwl_bg_monitor_recover, 327 .recover_from_tx_stall = iwl_bg_monitor_recover,
328 .check_plcp_health = iwl_good_plcp_health, 328 .check_plcp_health = iwl_good_plcp_health,
329 .check_ack_health = iwl_good_ack_health, 329 .check_ack_health = iwl_good_ack_health,
330 .txfifo_flush = iwlagn_txfifo_flush,
330}; 331};
331 332
332static const struct iwl_ops iwl6000_ops = { 333static const struct iwl_ops iwl6000_ops = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index 5f1e7d802cbf..95666e565c77 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -1435,3 +1435,66 @@ void iwl_free_tfds_in_queue(struct iwl_priv *priv,
1435 priv->stations[sta_id].tid[tid].tfds_in_queue = 0; 1435 priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
1436 } 1436 }
1437} 1437}
1438
1439#define IWL_FLUSH_WAIT_MS 2000
1440
1441int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv)
1442{
1443 struct iwl_tx_queue *txq;
1444 struct iwl_queue *q;
1445 int cnt;
1446 unsigned long now = jiffies;
1447 int ret = 0;
1448
1449 /* waiting for all the tx frames complete might take a while */
1450 for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
1451 if (cnt == IWL_CMD_QUEUE_NUM)
1452 continue;
1453 txq = &priv->txq[cnt];
1454 q = &txq->q;
1455 while (q->read_ptr != q->write_ptr && !time_after(jiffies,
1456 now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS)))
1457 msleep(1);
1458
1459 if (q->read_ptr != q->write_ptr) {
1460 IWL_ERR(priv, "fail to flush all tx fifo queues\n");
1461 ret = -ETIMEDOUT;
1462 break;
1463 }
1464 }
1465 return ret;
1466}
1467
1468#define IWL_TX_QUEUE_MSK 0xfffff
1469
1470/**
1471 * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode
1472 *
1473 * pre-requirements:
1474 * 1. acquire mutex before calling
1475 * 2. make sure rf is on and not in exit state
1476 */
1477int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
1478{
1479 struct iwl_txfifo_flush_cmd flush_cmd;
1480 struct iwl_host_cmd cmd = {
1481 .id = REPLY_TXFIFO_FLUSH,
1482 .len = sizeof(struct iwl_txfifo_flush_cmd),
1483 .flags = CMD_SYNC,
1484 .data = &flush_cmd,
1485 };
1486
1487 might_sleep();
1488
1489 memset(&flush_cmd, 0, sizeof(flush_cmd));
1490 flush_cmd.fifo_control = IWL_TX_FIFO_VO_MSK | IWL_TX_FIFO_VI_MSK |
1491 IWL_TX_FIFO_BE_MSK | IWL_TX_FIFO_BK_MSK;
1492 if (priv->cfg->sku & IWL_SKU_N)
1493 flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK;
1494
1495 IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n",
1496 flush_cmd.fifo_control);
1497 flush_cmd.flush_control = cpu_to_le16(flush_control);
1498
1499 return iwl_send_cmd(priv, &cmd);
1500}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 22c0149e5d4a..c735a39ec176 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -3639,6 +3639,44 @@ out_exit:
3639 IWL_DEBUG_MAC80211(priv, "leave\n"); 3639 IWL_DEBUG_MAC80211(priv, "leave\n");
3640} 3640}
3641 3641
3642static void iwl_mac_flush(struct ieee80211_hw *hw, bool drop)
3643{
3644 struct iwl_priv *priv = hw->priv;
3645
3646 mutex_lock(&priv->mutex);
3647 IWL_DEBUG_MAC80211(priv, "enter\n");
3648
3649 /* do not support "flush" */
3650 if (!priv->cfg->ops->lib->txfifo_flush)
3651 goto done;
3652
3653 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
3654 IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n");
3655 goto done;
3656 }
3657 if (iwl_is_rfkill(priv)) {
3658 IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n");
3659 goto done;
3660 }
3661
3662 /*
3663 * mac80211 will not push any more frames for transmit
3664 * until the flush is completed
3665 */
3666 if (drop) {
3667 IWL_DEBUG_MAC80211(priv, "send flush command\n");
3668 if (priv->cfg->ops->lib->txfifo_flush(priv, IWL_DROP_ALL)) {
3669 IWL_ERR(priv, "flush request fail\n");
3670 goto done;
3671 }
3672 }
3673 IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n");
3674 iwlagn_wait_tx_queue_empty(priv);
3675done:
3676 mutex_unlock(&priv->mutex);
3677 IWL_DEBUG_MAC80211(priv, "leave\n");
3678}
3679
3642/***************************************************************************** 3680/*****************************************************************************
3643 * 3681 *
3644 * driver setup and teardown 3682 * driver setup and teardown
@@ -3812,6 +3850,7 @@ static struct ieee80211_ops iwl_hw_ops = {
3812 .sta_add = iwlagn_mac_sta_add, 3850 .sta_add = iwlagn_mac_sta_add,
3813 .sta_remove = iwl_mac_sta_remove, 3851 .sta_remove = iwl_mac_sta_remove,
3814 .channel_switch = iwl_mac_channel_switch, 3852 .channel_switch = iwl_mac_channel_switch,
3853 .flush = iwl_mac_flush,
3815}; 3854};
3816 3855
3817static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 3856static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index be9d298cae2c..0298642d1d75 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -147,6 +147,8 @@ const u8 *iwlagn_eeprom_query_addr(const struct iwl_priv *priv,
147void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); 147void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
148int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq); 148int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
149int iwlagn_hw_nic_init(struct iwl_priv *priv); 149int iwlagn_hw_nic_init(struct iwl_priv *priv);
150int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv);
151int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control);
150 152
151/* rx */ 153/* rx */
152void iwlagn_rx_queue_restock(struct iwl_priv *priv); 154void iwlagn_rx_queue_restock(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index b28cb4ffedc7..084495562200 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -1216,6 +1216,10 @@ struct iwl_rem_sta_cmd {
1216#define IWL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3)) 1216#define IWL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3))
1217#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) 1217#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00)
1218 1218
1219#define IWL_DROP_SINGLE 0
1220#define IWL_DROP_SELECTED 1
1221#define IWL_DROP_ALL 2
1222
1219/* 1223/*
1220 * REPLY_TXFIFO_FLUSH = 0x1e(command and response) 1224 * REPLY_TXFIFO_FLUSH = 0x1e(command and response)
1221 * 1225 *
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index bfa34561d9da..db315b05f988 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -205,6 +205,8 @@ struct iwl_lib_ops {
205 /* check for ack health */ 205 /* check for ack health */
206 bool (*check_ack_health)(struct iwl_priv *priv, 206 bool (*check_ack_health)(struct iwl_priv *priv,
207 struct iwl_rx_packet *pkt); 207 struct iwl_rx_packet *pkt);
208 int (*txfifo_flush)(struct iwl_priv *priv, u16 flush_control);
209
208 struct iwl_debugfs_ops debugfs_ops; 210 struct iwl_debugfs_ops debugfs_ops;
209}; 211};
210 212