iwlwifi : Patch adds rfkill subsystem for 3945
authorAbhijeet Kolekar <abhijeet.kolekar@intel.com>
Mon, 30 Jun 2008 09:23:18 +0000 (17:23 +0800)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 30 Jun 2008 21:37:42 +0000 (17:37 -0400)
The patch removes the sysfs interface from iwl3945 and uses
the rfkill subsystem instead.

Original patch by Adel, I fixed the patch to work it properly.

Signed-off-by: Adel Gadllah <adel.gadllah@gmail.com>
Signed-off-by: Abhijeet Kolekar <abhijeet.kolekar@intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/iwlwifi/iwl-3945.h
drivers/net/wireless/iwlwifi/iwl3945-base.c

index a9b3edad38682bdd8239148e63f2f48b3b2e59a9..a77497809d97f20e3391ffe95933c5f0d6df7ff6 100644 (file)
 #include <linux/kernel.h>
 #include <net/ieee80211_radiotap.h>
 
+/*used for rfkill*/
+#include <linux/rfkill.h>
+#include <linux/input.h>
+
 /* Hardware specific file defines the PCI IDs table for that hardware module */
 extern struct pci_device_id iwl3945_hw_card_ids[];
 
@@ -686,6 +690,23 @@ enum {
 
 #endif
 
+#ifdef CONFIG_IWLWIFI_RFKILL
+struct iwl3945_priv;
+
+struct iwl3945_rfkill_mngr {
+       struct rfkill *rfkill;
+       struct input_dev *input_dev;
+};
+
+void iwl3945_rfkill_set_hw_state(struct iwl3945_priv *priv);
+void iwl3945_rfkill_unregister(struct iwl3945_priv *priv);
+int iwl3945_rfkill_init(struct iwl3945_priv *priv);
+#else
+static inline void iwl3945_rfkill_set_hw_state(struct iwl3945_priv *priv) {}
+static inline void iwl3945_rfkill_unregister(struct iwl3945_priv *priv) {}
+static inline int iwl3945_rfkill_init(struct iwl3945_priv *priv) { return 0; }
+#endif
+
 #define IWL_MAX_NUM_QUEUES IWL39_MAX_NUM_QUEUES
 
 struct iwl3945_priv {
@@ -779,6 +800,10 @@ struct iwl3945_priv {
        struct iwl3945_init_alive_resp card_alive_init;
        struct iwl3945_alive_resp card_alive;
 
+#ifdef CONFIG_IWLWIFI_RFKILL
+       struct iwl3945_rfkill_mngr rfkill_mngr;
+#endif
+
 #ifdef CONFIG_IWL3945_LEDS
        struct iwl3945_led led[IWL_LED_TRG_MAX];
        unsigned long last_blink_time;
index fe603191c12ca46467905a3f408719ae7f0ea9d9..43cb8ff979395f853f932f9caac0767b0e898e73 100644 (file)
@@ -5921,7 +5921,9 @@ static void __iwl3945_down(struct iwl3945_priv *priv)
                               test_bit(STATUS_GEO_CONFIGURED, &priv->status) <<
                                        STATUS_GEO_CONFIGURED |
                               test_bit(STATUS_IN_SUSPEND, &priv->status) <<
-                                       STATUS_IN_SUSPEND;
+                                       STATUS_IN_SUSPEND |
+                               test_bit(STATUS_EXIT_PENDING, &priv->status) <<
+                                       STATUS_EXIT_PENDING;
                goto exit;
        }
 
@@ -5936,7 +5938,9 @@ static void __iwl3945_down(struct iwl3945_priv *priv)
                        test_bit(STATUS_IN_SUSPEND, &priv->status) <<
                                STATUS_IN_SUSPEND |
                        test_bit(STATUS_FW_ERROR, &priv->status) <<
-                               STATUS_FW_ERROR;
+                               STATUS_FW_ERROR |
+                       test_bit(STATUS_EXIT_PENDING, &priv->status) <<
+                               STATUS_EXIT_PENDING;
 
        spin_lock_irqsave(&priv->lock, flags);
        iwl3945_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -6008,11 +6012,12 @@ static int __iwl3945_up(struct iwl3945_priv *priv)
        else {
                set_bit(STATUS_RF_KILL_HW, &priv->status);
                if (!test_bit(STATUS_IN_SUSPEND, &priv->status)) {
+                       iwl3945_rfkill_set_hw_state(priv);
                        IWL_WARNING("Radio disabled by HW RF Kill switch\n");
                        return -ENODEV;
                }
        }
-
+       iwl3945_rfkill_set_hw_state(priv);
        iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF);
 
        rc = iwl3945_hw_nic_init(priv);
@@ -6068,6 +6073,7 @@ static int __iwl3945_up(struct iwl3945_priv *priv)
 
        set_bit(STATUS_EXIT_PENDING, &priv->status);
        __iwl3945_down(priv);
+       clear_bit(STATUS_EXIT_PENDING, &priv->status);
 
        /* tried to restart and config the device for as long as our
         * patience could withstand */
@@ -6135,6 +6141,8 @@ static void iwl3945_bg_rf_kill(struct work_struct *work)
                                    "Kill switch must be turned off for "
                                    "wireless networking to work.\n");
        }
+
+       iwl3945_rfkill_set_hw_state(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -7412,37 +7420,6 @@ static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
 
 #endif /* CONFIG_IWL3945_DEBUG */
 
-static ssize_t show_rf_kill(struct device *d,
-                           struct device_attribute *attr, char *buf)
-{
-       /*
-        * 0 - RF kill not enabled
-        * 1 - SW based RF kill active (sysfs)
-        * 2 - HW based RF kill active
-        * 3 - Both HW and SW based RF kill active
-        */
-       struct iwl3945_priv *priv = (struct iwl3945_priv *)d->driver_data;
-       int val = (test_bit(STATUS_RF_KILL_SW, &priv->status) ? 0x1 : 0x0) |
-                 (test_bit(STATUS_RF_KILL_HW, &priv->status) ? 0x2 : 0x0);
-
-       return sprintf(buf, "%i\n", val);
-}
-
-static ssize_t store_rf_kill(struct device *d,
-                            struct device_attribute *attr,
-                            const char *buf, size_t count)
-{
-       struct iwl3945_priv *priv = (struct iwl3945_priv *)d->driver_data;
-
-       mutex_lock(&priv->mutex);
-       iwl3945_radio_kill_sw(priv, buf[0] == '1');
-       mutex_unlock(&priv->mutex);
-
-       return count;
-}
-
-static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
-
 static ssize_t show_temperature(struct device *d,
                                struct device_attribute *attr, char *buf)
 {
@@ -7928,7 +7905,6 @@ static struct attribute *iwl3945_sysfs_entries[] = {
 #endif
        &dev_attr_power_level.attr,
        &dev_attr_retry_rate.attr,
-       &dev_attr_rf_kill.attr,
        &dev_attr_rs_window.attr,
        &dev_attr_statistics.attr,
        &dev_attr_status.attr,
@@ -8169,6 +8145,11 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
        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);
+
        return 0;
 
  out_free_geos:
@@ -8231,6 +8212,7 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev)
 
        sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group);
 
+       iwl3945_rfkill_unregister(priv);
        iwl3945_dealloc_ucode_pci(priv);
 
        if (priv->rxq.bd)
@@ -8299,6 +8281,140 @@ static int iwl3945_pci_resume(struct pci_dev *pdev)
 
 #endif /* CONFIG_PM */
 
+/*************** RFKILL FUNCTIONS **********/
+#ifdef CONFIG_IWLWIFI_RFKILL
+/* software rf-kill from user */
+static int iwl3945_rfkill_soft_rf_kill(void *data, enum rfkill_state state)
+{
+       struct iwl3945_priv *priv = data;
+       int err = 0;
+
+       if (!priv->rfkill_mngr.rfkill)
+       return 0;
+
+       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+               return 0;
+
+       IWL_DEBUG_RF_KILL("we recieved soft RFKILL set to state %d\n", state);
+       mutex_lock(&priv->mutex);
+
+       switch (state) {
+       case RFKILL_STATE_ON:
+               iwl3945_radio_kill_sw(priv, 0);
+               /* if HW rf-kill is set dont allow ON state */
+               if (iwl3945_is_rfkill(priv))
+                       err = -EBUSY;
+               break;
+       case RFKILL_STATE_OFF:
+               iwl3945_radio_kill_sw(priv, 1);
+               if (!iwl3945_is_rfkill(priv))
+                       err = -EBUSY;
+               break;
+       }
+       mutex_unlock(&priv->mutex);
+
+       return err;
+}
+
+int iwl3945_rfkill_init(struct iwl3945_priv *priv)
+{
+       struct device *device = wiphy_dev(priv->hw->wiphy);
+       int ret = 0;
+
+       BUG_ON(device == NULL);
+
+       IWL_DEBUG_RF_KILL("Initializing RFKILL.\n");
+       priv->rfkill_mngr.rfkill = rfkill_allocate(device, RFKILL_TYPE_WLAN);
+       if (!priv->rfkill_mngr.rfkill) {
+               IWL_ERROR("Unable to allocate rfkill device.\n");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       priv->rfkill_mngr.rfkill->name = priv->cfg->name;
+       priv->rfkill_mngr.rfkill->data = priv;
+       priv->rfkill_mngr.rfkill->state = RFKILL_STATE_ON;
+       priv->rfkill_mngr.rfkill->toggle_radio = iwl3945_rfkill_soft_rf_kill;
+       priv->rfkill_mngr.rfkill->user_claim_unsupported = 1;
+
+       priv->rfkill_mngr.rfkill->dev.class->suspend = NULL;
+       priv->rfkill_mngr.rfkill->dev.class->resume = NULL;
+
+       priv->rfkill_mngr.input_dev = input_allocate_device();
+       if (!priv->rfkill_mngr.input_dev) {
+               IWL_ERROR("Unable to allocate rfkill input device.\n");
+               ret = -ENOMEM;
+               goto freed_rfkill;
+       }
+
+       priv->rfkill_mngr.input_dev->name = priv->cfg->name;
+       priv->rfkill_mngr.input_dev->phys = wiphy_name(priv->hw->wiphy);
+       priv->rfkill_mngr.input_dev->id.bustype = BUS_HOST;
+       priv->rfkill_mngr.input_dev->id.vendor = priv->pci_dev->vendor;
+       priv->rfkill_mngr.input_dev->dev.parent = device;
+       priv->rfkill_mngr.input_dev->evbit[0] = BIT(EV_KEY);
+       set_bit(KEY_WLAN, priv->rfkill_mngr.input_dev->keybit);
+
+       ret = rfkill_register(priv->rfkill_mngr.rfkill);
+       if (ret) {
+               IWL_ERROR("Unable to register rfkill: %d\n", ret);
+               goto free_input_dev;
+       }
+
+       ret = input_register_device(priv->rfkill_mngr.input_dev);
+       if (ret) {
+               IWL_ERROR("Unable to register rfkill input device: %d\n", ret);
+               goto unregister_rfkill;
+       }
+
+       IWL_DEBUG_RF_KILL("RFKILL initialization complete.\n");
+       return ret;
+
+unregister_rfkill:
+       rfkill_unregister(priv->rfkill_mngr.rfkill);
+       priv->rfkill_mngr.rfkill = NULL;
+
+free_input_dev:
+       input_free_device(priv->rfkill_mngr.input_dev);
+       priv->rfkill_mngr.input_dev = NULL;
+
+freed_rfkill:
+       if (priv->rfkill_mngr.rfkill != NULL)
+               rfkill_free(priv->rfkill_mngr.rfkill);
+       priv->rfkill_mngr.rfkill = NULL;
+
+error:
+       IWL_DEBUG_RF_KILL("RFKILL initialization complete.\n");
+       return ret;
+}
+
+void iwl3945_rfkill_unregister(struct iwl3945_priv *priv)
+{
+
+       if (priv->rfkill_mngr.input_dev)
+               input_unregister_device(priv->rfkill_mngr.input_dev);
+
+       if (priv->rfkill_mngr.rfkill)
+               rfkill_unregister(priv->rfkill_mngr.rfkill);
+
+       priv->rfkill_mngr.input_dev = NULL;
+       priv->rfkill_mngr.rfkill = NULL;
+}
+
+/* set rf-kill to the right state. */
+void iwl3945_rfkill_set_hw_state(struct iwl3945_priv *priv)
+{
+
+       if (!priv->rfkill_mngr.rfkill)
+               return;
+
+       if (!iwl3945_is_rfkill(priv))
+               priv->rfkill_mngr.rfkill->state = RFKILL_STATE_ON;
+       else
+               priv->rfkill_mngr.rfkill->state = RFKILL_STATE_OFF;
+}
+#endif
+
 /*****************************************************************************
  *
  * driver and module entry point