diff options
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-1000.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-6000.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 63 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 39 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-commands.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 2 |
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 | ||
231 | static const struct iwl_ops iwl1000_ops = { | 232 | static 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 | ||
407 | static struct iwl_lib_ops iwl5150_lib = { | 408 | static 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 | ||
470 | static const struct iwl_ops iwl5000_ops = { | 472 | static 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 | ||
332 | static const struct iwl_ops iwl6000_ops = { | 333 | static 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 | |||
1441 | int 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 | */ | ||
1477 | int 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 | ||
3642 | static 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); | ||
3675 | done: | ||
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 | ||
3817 | static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | 3856 | static 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, | |||
147 | void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); | 147 | void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); |
148 | int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq); | 148 | int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq); |
149 | int iwlagn_hw_nic_init(struct iwl_priv *priv); | 149 | int iwlagn_hw_nic_init(struct iwl_priv *priv); |
150 | int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv); | ||
151 | int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); | ||
150 | 152 | ||
151 | /* rx */ | 153 | /* rx */ |
152 | void iwlagn_rx_queue_restock(struct iwl_priv *priv); | 154 | void 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 | ||