[PATCH] report rfkill changes event if interface is down

Huaxu Wan huaxu.wan at intel.com
Fri Mar 6 08:18:41 GMT 2009


Backported from [PATCHv2] iwl3945: report killswitch changes even if the interface is down
http://markmail.org/message/efopl4gvfrqdlzyz 

Currently iwl3945 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 iwl3945_pci_probe instead of iwl3945_up
and poll the CSR_GP_CNTRL register to update the killswitch state every
two seconds. The polling is only needed on 3945 hardware as this adapter
does not use interrupts to signal rfkill changes to the driver (in case no
firmware is loaded). The firmware loading is still done in iwl3945_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-3945.h     |    3 +-
 drivers/net/wireless/iwlwifi/iwl3945-base.c |   83 ++++++++++++++++++---------
 2 files changed, 58 insertions(+), 28 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index bdd3247..304b406 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -898,7 +898,8 @@ struct iwl3945_priv {
 	struct delayed_work thermal_periodic;
 	struct delayed_work gather_stats;
 	struct delayed_work scan_check;
-
+	struct delayed_work rfkill_poll;
+	
 #define IWL_DEFAULT_TX_POWER 0x0F
 	s8 user_txpower_limit;
 	s8 max_channel_txpower_limit;
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 38e389e..bb92db2 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -6029,7 +6029,8 @@ static void iwl3945_bg_rf_kill(struct work_struct *work)
 		IWL_DEBUG(IWL_DL_INFO | 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 {
 
@@ -6046,6 +6047,26 @@ static void iwl3945_bg_rf_kill(struct work_struct *work)
 	iwl3945_rfkill_set_hw_state(priv);
 }
 
+ 
+static void iwl3945_rfkill_poll(struct work_struct *data)
+{
+	struct iwl3945_priv *priv =
+	    container_of(data, struct iwl3945_priv, rfkill_poll.work);
+	    unsigned long status = priv->status;
+
+	if (iwl3945_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);
+
+	if (test_bit(STATUS_RF_KILL_HW, &status) != test_bit(STATUS_RF_KILL_HW, &priv->status))
+	   queue_work(priv->workqueue, &priv->rf_kill);
+
+	queue_delayed_work(priv->workqueue, &priv->rfkill_poll,
+						   round_jiffies_relative(2 * HZ));
+
+}
+
 static void iwl3945_bg_set_monitor(struct work_struct *work)
 {
 	struct iwl3945_priv *priv = container_of(work,
@@ -6483,20 +6504,6 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw)
 
 	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);
-
-	ret = request_irq(priv->pci_dev->irq, iwl3945_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);
 
@@ -6540,16 +6547,15 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw)
 			goto out_release_irq;
 		}
 	}
+	/* ucode is running and will send rfkill notifications,
+	 * no need to poll the killswitch state anymore */
+	cancel_delayed_work(&priv->rfkill_poll);
 
 	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;
@@ -6580,10 +6586,10 @@ static void iwl3945_mac_stop(struct ieee80211_hw *hw)
 	iwl3945_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);
+
+	/* start polling the killswitch state again */
+	queue_delayed_work(priv->workqueue, &priv->rfkill_poll,
+						   round_jiffies_relative(2 * HZ));
 
 	IWL_DEBUG_MAC80211("leave\n");
 }
@@ -7819,7 +7825,7 @@ static void iwl3945_setup_deferred_work(struct iwl3945_priv *priv)
 	INIT_DELAYED_WORK(&priv->init_alive_start, iwl3945_bg_init_alive_start);
 	INIT_DELAYED_WORK(&priv->alive_start, iwl3945_bg_alive_start);
 	INIT_DELAYED_WORK(&priv->scan_check, iwl3945_bg_scan_check);
-
+	INIT_DELAYED_WORK(&priv->rfkill_poll, iwl3945_rfkill_poll);
 	iwl3945_hw_setup_deferred_work(priv);
 
 	tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
@@ -8036,6 +8042,15 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	iwl3945_disable_interrupts(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
+	pci_enable_msi(priv->pci_dev);
+
+	err = request_irq(priv->pci_dev->irq, iwl3945_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, &iwl3945_attribute_group);
 	if (err) {
 		IWL_ERROR("failed to create sysfs device attributes\n");
@@ -8085,14 +8100,16 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 
 	priv->hw->conf.beacon_int = 100;
 	priv->mac80211_registered = 1;
-	pci_save_state(pdev);
-	pci_disable_device(pdev);
 
 	err = iwl3945_rfkill_init(priv);
 	if (err)
 		IWL_ERROR("Unable to initialize RFKILL system. "
 				  "Ignoring error: %d\n", err);
 
+	/* Start monitoring the killswitch */
+	queue_delayed_work(priv->workqueue, &priv->rfkill_poll,
+						   2 * HZ);
+
 	return 0;
 
  out_free_geos:
@@ -8103,10 +8120,13 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group);
 
  out_release_irq:
+	free_irq(priv->pci_dev->irq, priv);
 	destroy_workqueue(priv->workqueue);
 	priv->workqueue = NULL;
 	iwl3945_unset_hw_setting(priv);
-
+ out_disable_msi:
+	pci_disable_msi(priv->pci_dev);
+	
  out_iounmap:
 	pci_iounmap(pdev, priv->hw_base);
  out_pci_release_regions:
@@ -8146,6 +8166,7 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev)
 	sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group);
 
 	iwl3945_rfkill_unregister(priv);
+	cancel_delayed_work(&priv->rfkill_poll);
 	iwl3945_dealloc_ucode_pci(priv);
 
 	if (priv->rxq.bd)
@@ -8167,6 +8188,10 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev)
 	destroy_workqueue(priv->workqueue);
 	priv->workqueue = NULL;
 
+	free_irq(pdev->irq, priv);
+	pci_disable_msi(pdev);
+
+	
 	pci_iounmap(pdev, priv->hw_base);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
@@ -8193,6 +8218,8 @@ static int iwl3945_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;
@@ -8203,6 +8230,8 @@ static int iwl3945_pci_resume(struct pci_dev *pdev)
 	struct iwl3945_priv *priv = pci_get_drvdata(pdev);
 
 	pci_set_power_state(pdev, PCI_D0);
+	pci_enable_device(pdev);
+	pci_restore_state(pdev);
 
 	if (priv->is_open)
 		iwl3945_mac_start(priv->hw);
-- 
1.6.0.4




More information about the kernel-team mailing list