Back-port of commit pending for 2.6.30... commit 9a454abf066ab588ef28ff9d2c04cc480d13c040 Author: Helmut Schaa Date: Thu Jan 15 09:38:44 2009 +0100 iwl3945: report killswitch changes even if the interface is down 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 Acked-by: Samuel Ortiz Signed-off-by: John W. Linville diff -up linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c --- linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig 2009-04-02 14:21:18.000000000 -0400 +++ linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c 2009-04-02 14:21:22.000000000 -0400 @@ -6001,7 +6001,8 @@ static void iwl3945_bg_rf_kill(struct wo 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 { @@ -6018,6 +6019,25 @@ static void iwl3945_bg_rf_kill(struct wo 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)); + +} + #define IWL_SCAN_CHECK_WATCHDOG (7 * HZ) static void iwl3945_bg_scan_check(struct work_struct *data) @@ -6420,20 +6440,6 @@ static int iwl3945_mac_start(struct ieee 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); @@ -6478,15 +6484,15 @@ static int iwl3945_mac_start(struct ieee } } + /* 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; @@ -6517,10 +6523,10 @@ static void iwl3945_mac_stop(struct ieee 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"); } @@ -7735,6 +7741,7 @@ static void iwl3945_setup_deferred_work( 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); @@ -8007,24 +8014,27 @@ static int iwl3945_pci_probe(struct pci_ 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"); - goto out_free_geos; + goto out_release_irq; } iwl3945_set_rxon_channel(priv, IEEE80211_BAND_2GHZ, 6); iwl3945_setup_deferred_work(priv); iwl3945_setup_rx_handlers(priv); - /*********************** - * 9. Conclude - * ********************/ - pci_save_state(pdev); - pci_disable_device(pdev); - /********************************* - * 10. Setup and Register mac80211 + * 9. Setup and Register mac80211 * *******************************/ err = ieee80211_register_hw(priv->hw); @@ -8042,13 +8052,20 @@ static int iwl3945_pci_probe(struct pci_ 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_remove_sysfs: destroy_workqueue(priv->workqueue); priv->workqueue = NULL; sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group); - out_free_geos: + out_release_irq: + free_irq(priv->pci_dev->irq, priv); + out_disable_msi: + pci_disable_msi(priv->pci_dev); iwl3945_free_geos(priv); out_free_channel_map: iwl3945_free_channel_map(priv); @@ -8093,6 +8110,8 @@ static void __devexit iwl3945_pci_remove 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) @@ -8114,6 +8133,9 @@ static void __devexit iwl3945_pci_remove 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); @@ -8140,19 +8162,8 @@ static int iwl3945_pci_suspend(struct pc priv->is_open = 1; } - /* pci driver assumes state will be saved in this function. - * pci state is saved and device disabled when interface is - * stopped, so at this time pci device will always be disabled - - * whether interface was started or not. saving pci state now will - * cause saved state be that of a disabled device, which will cause - * problems during resume in that we will end up with a disabled device. - * - * indicate that the current saved state (from when interface was - * stopped) is valid. if interface was never up at time of suspend - * then the saved state will still be valid as it was saved during - * .probe. */ - pdev->state_saved = true; - + pci_save_state(pdev); + pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); return 0; @@ -8163,6 +8174,8 @@ static int iwl3945_pci_resume(struct pci 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); diff -up linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl-3945.h.orig linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl-3945.h --- linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl-3945.h.orig 2009-04-02 14:21:36.000000000 -0400 +++ linux-2.6.29.noarch/drivers/net/wireless/iwlwifi/iwl-3945.h 2009-04-02 14:21:45.000000000 -0400 @@ -895,6 +895,7 @@ 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;