Subject: [PATCH] LP#193970 iwlagn: fix hw-rfkill while the interface is down

Huaxu Wan huaxu.wan at linux.intel.com
Thu Feb 12 08:38:06 UTC 2009


>From f9e0718b28e0858c94575fb7f4358f3549ba5cea Mon Sep 17 00:00:00 2001
From: Huaxu Wan <huaxu.wan at intel.com>
Date: Wed, 11 Feb 2009 09:26:30 +0800
Subject: [PATCH] LP#193970 iwlagn: fix hw-rfkill while the interface is down

Backport from http://marc.info/?l=linux-wireless&m=123236704016931&w=2

Fix the bug #193970 in Jaunty Alpha 4 

Currently iwlagn is not able to report hw-killswitch events while the
interface is down. This has implications on user space tools (like
NetworkManager) relying on rfkill notifications to bring the interface
up once the wireless gets enabled through a hw killswitch.

Thus, enable the device already in iwl_pci_probe instead of iwl_up
and enable interrups while the interface is down in order to get
notified about killswitch state changes. The firmware loading is still
done in iwl_up.

Signed-off-by: Helmut Schaa <helmut.schaa at googlemail.com>
Signed-off-by: Huaxu Wan <huaxu.wan at intel.com>

---
 drivers/net/wireless/iwlwifi/iwl-agn.c    |  112 +++++++++++++++--------------
 drivers/net/wireless/iwlwifi/iwlagn.mod.c |    2 +-
 2 files changed, 59 insertions(+), 55 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index fff20a3..3fb64ad 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1645,13 +1645,16 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv)
 				hw_rf_kill ? "disable radio":"enable radio");
 
 		/* driver only loads ucode once setting the interface up.
-		 * the driver as well won't allow loading if RFKILL is set
-		 * therefore no need to restart the driver from this handler
+		 * the driver allows loading the ucode even if the radio
+		 * is killed. Hence update the killswitch state here. The
+		 * rfkill handler will care about restarting if needed.
 		 */
-		if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status)) {
-			clear_bit(STATUS_RF_KILL_HW, &priv->status);
-			if (priv->is_open && !iwl_is_rfkill(priv))
-				queue_work(priv->workqueue, &priv->up);
+		if (!test_bit(STATUS_ALIVE, &priv->status)) {
+			if (hw_rf_kill)
+				set_bit(STATUS_RF_KILL_HW, &priv->status);
+			else
+				clear_bit(STATUS_RF_KILL_HW, &priv->status);
+			queue_work(priv->workqueue, &priv->rf_kill);
 		}
 
 		handled |= CSR_INT_BIT_RF_KILL;
@@ -2353,7 +2356,8 @@ static void iwl4965_bg_rf_kill(struct work_struct *work)
 		IWL_DEBUG(IWL_DL_RF_KILL,
 			  "HW and/or SW RF Kill no longer active, restarting "
 			  "device\n");
-		if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
+		if (!test_bit(STATUS_EXIT_PENDING, &priv->status) &&
+		     test_bit(STATUS_ALIVE, &priv->status))
 			queue_work(priv->workqueue, &priv->restart);
 	} else {
 		/* make sure mac80211 stop sending Tx frame */
@@ -2575,31 +2579,9 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw)
 {
 	struct iwl_priv *priv = hw->priv;
 	int ret;
-	u16 pci_cmd;
 
 	IWL_DEBUG_MAC80211("enter\n");
 
-	if (pci_enable_device(priv->pci_dev)) {
-		IWL_ERROR("Fail to pci_enable_device\n");
-		return -ENODEV;
-	}
-	pci_restore_state(priv->pci_dev);
-	pci_enable_msi(priv->pci_dev);
-
-	/* enable interrupts if needed: hw bug w/a */
-	pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pci_cmd);
-	if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
-		pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
-		pci_write_config_word(priv->pci_dev, PCI_COMMAND, pci_cmd);
-	}
-
-	ret = request_irq(priv->pci_dev->irq, iwl4965_isr, IRQF_SHARED,
-			  DRV_NAME, priv);
-	if (ret) {
-		IWL_ERROR("Error allocating IRQ %d\n", priv->pci_dev->irq);
-		goto out_disable_msi;
-	}
-
 	/* we should be verifying the device is ready to be opened */
 	mutex_lock(&priv->mutex);
 
@@ -2612,7 +2594,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw)
 		if (ret) {
 			IWL_ERROR("Could not read microcode: %d\n", ret);
 			mutex_unlock(&priv->mutex);
-			goto out_release_irq;
+			return ret;
 		}
 	}
 
@@ -2623,7 +2605,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw)
 	iwl_rfkill_set_hw_state(priv);
 
 	if (ret)
-		goto out_release_irq;
+		return ret;
 
 	if (iwl_is_rfkill(priv))
 		goto out;
@@ -2642,8 +2624,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw)
 		if (!test_bit(STATUS_READY, &priv->status)) {
 			IWL_ERROR("START_ALIVE timeout after %dms.\n",
 				jiffies_to_msecs(UCODE_READY_TIMEOUT));
-			ret = -ETIMEDOUT;
-			goto out_release_irq;
+			return -ETIMEDOUT;
 		}
 	}
 
@@ -2651,15 +2632,6 @@ out:
 	priv->is_open = 1;
 	IWL_DEBUG_MAC80211("leave\n");
 	return 0;
-
-out_release_irq:
-	free_irq(priv->pci_dev->irq, priv);
-out_disable_msi:
-	pci_disable_msi(priv->pci_dev);
-	pci_disable_device(priv->pci_dev);
-	priv->is_open = 0;
-	IWL_DEBUG_MAC80211("leave - failed\n");
-	return ret;
 }
 
 static void iwl4965_mac_stop(struct ieee80211_hw *hw)
@@ -2687,10 +2659,10 @@ static void iwl4965_mac_stop(struct ieee80211_hw *hw)
 	iwl4965_down(priv);
 
 	flush_workqueue(priv->workqueue);
-	free_irq(priv->pci_dev->irq, priv);
-	pci_disable_msi(priv->pci_dev);
-	pci_save_state(priv->pci_dev);
-	pci_disable_device(priv->pci_dev);
+
+	/* enable interrupts again in order to receive rfkill changes */
+	iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
+	iwl4965_enable_interrupts(priv);
 
 	IWL_DEBUG_MAC80211("leave\n");
 }
@@ -4169,6 +4141,8 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	struct ieee80211_hw *hw;
 	struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
 	unsigned long flags;
+	u16 pci_cmd;
+	
 	DECLARE_MAC_BUF(mac);
 
 	/************************
@@ -4314,7 +4288,14 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	spin_lock_irqsave(&priv->lock, flags);
 	iwl4965_disable_interrupts(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
-
+ 	pci_enable_msi(priv->pci_dev);
+ 
+ 	err = request_irq(priv->pci_dev->irq, iwl4965_isr, IRQF_SHARED,
+ 			  DRV_NAME, priv);
+ 	if (err) {
+ 		IWL_ERROR("Error allocating IRQ %d\n", priv->pci_dev->irq);
+ 		goto out_disable_msi;
+ 	}
 	err = sysfs_create_group(&pdev->dev.kobj, &iwl4965_attribute_group);
 	if (err) {
 		IWL_ERROR("failed to create sysfs device attributes\n");
@@ -4325,15 +4306,18 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	iwl_setup_deferred_work(priv);
 	iwl_setup_rx_handlers(priv);
 
-	/********************
-	 * 9. Conclude
-	 ********************/
-	pci_save_state(pdev);
-	pci_disable_device(pdev);
-
 	/**********************************
-	 * 10. Setup and register mac80211
+ 	 * 9. Setup and register mac80211
 	 **********************************/
+  
+ 	/* enable interrupts if needed: hw bug w/a */
+ 	pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pci_cmd);
+ 	if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
+ 		pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ 		pci_write_config_word(priv->pci_dev, PCI_COMMAND, pci_cmd);
+ 	}
+ 
+ 	iwl4965_enable_interrupts(priv);
 
 	err = iwl_setup_mac(priv);
 	if (err)
@@ -4343,15 +4327,28 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	if (err)
 		IWL_ERROR("failed to create debugfs files\n");
 
+  
+ 	/* If platform's RF_KILL switch is NOT set to KILL */
+ 	if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
+ 		clear_bit(STATUS_RF_KILL_HW, &priv->status);
+ 	else
+ 		set_bit(STATUS_RF_KILL_HW, &priv->status);
+	
 	err = iwl_rfkill_init(priv);
 	if (err)
 		IWL_ERROR("Unable to initialize RFKILL system. "
 				  "Ignoring error: %d\n", err);
+ 	else
+ 		iwl_rfkill_set_hw_state(priv);
+
 	iwl_power_initialize(priv);
 	return 0;
 
  out_remove_sysfs:
 	sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group);
+ out_disable_msi:
+ 	pci_disable_msi(priv->pci_dev);
+ 	pci_disable_device(priv->pci_dev);
  out_uninit_drv:
 	iwl_uninit_drv(priv);
  out_free_eeprom:
@@ -4423,6 +4420,8 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev)
 	destroy_workqueue(priv->workqueue);
 	priv->workqueue = NULL;
 
+	free_irq(priv->pci_dev->irq, priv);
+	pci_disable_msi(priv->pci_dev);
 	pci_iounmap(pdev, priv->hw_base);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
@@ -4448,6 +4447,8 @@ static int iwl4965_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 		priv->is_open = 1;
 	}
 
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
 	pci_set_power_state(pdev, PCI_D3hot);
 
 	return 0;
@@ -4458,6 +4459,9 @@ static int iwl4965_pci_resume(struct pci_dev *pdev)
 	struct iwl_priv *priv = pci_get_drvdata(pdev);
 
 	pci_set_power_state(pdev, PCI_D0);
+	pci_enable_device(pdev);
+	pci_restore_state(pdev);
+	iwl4965_enable_interrupts(priv);
 
 	if (priv->is_open)
 		iwl4965_mac_start(priv->hw);

-- 
1.6.0.4





More information about the kernel-team mailing list