]> err.no Git - linux-2.6/blobdiff - drivers/net/wireless/iwlwifi/iwl3945-base.c
iwlwifi : Patch adds rfkill subsystem for 3945
[linux-2.6] / drivers / net / wireless / iwlwifi / iwl3945-base.c
index 47cf4b997f5096c1c49217e2d608d524f955deb8..43cb8ff979395f853f932f9caac0767b0e898e73 100644 (file)
@@ -970,7 +970,7 @@ static int iwl3945_full_rxon_required(struct iwl3945_priv *priv)
 {
 
        /* These items are only settable from the full RXON command */
-       if (!(priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ||
+       if (!(iwl3945_is_associated(priv)) ||
            compare_ether_addr(priv->staging_rxon.bssid_addr,
                               priv->active_rxon.bssid_addr) ||
            compare_ether_addr(priv->staging_rxon.node_addr,
@@ -2217,7 +2217,10 @@ static int iwl3945_scan_initiate(struct iwl3945_priv *priv)
        }
 
        IWL_DEBUG_INFO("Starting scan...\n");
-       priv->scan_bands = 2;
+       if (priv->cfg->sku & IWL_SKU_G)
+               priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
+       if (priv->cfg->sku & IWL_SKU_A)
+               priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
        set_bit(STATUS_SCANNING, &priv->status);
        priv->scan_start = jiffies;
        priv->scan_pass_start = priv->scan_start;
@@ -3342,13 +3345,18 @@ static void iwl3945_rx_scan_complete_notif(struct iwl3945_priv *priv,
        cancel_delayed_work(&priv->scan_check);
 
        IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n",
-                      (priv->scan_bands == 2) ? "2.4" : "5.2",
+                      (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ?
+                                                       "2.4" : "5.2",
                       jiffies_to_msecs(elapsed_jiffies
                                        (priv->scan_pass_start, jiffies)));
 
-       /* Remove this scanned band from the list
-        * of pending bands to scan */
-       priv->scan_bands--;
+       /* Remove this scanned band from the list of pending
+        * bands to scan, band G precedes A in order of scanning
+        * as seen in iwl3945_bg_request_scan */
+       if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ))
+               priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ);
+       else if (priv->scan_bands &  BIT(IEEE80211_BAND_5GHZ))
+               priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ);
 
        /* If a request to abort was given, or the scan did not succeed
         * then we reset the scan state machine and terminate,
@@ -4961,7 +4969,7 @@ static int iwl3945_get_channels_for_scan(struct iwl3945_priv *priv,
 
                ch_info = iwl3945_get_channel_info(priv, band, scan_ch->channel);
                if (!is_channel_valid(ch_info)) {
-                       IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n",
+                       IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n",
                                       scan_ch->channel);
                        continue;
                }
@@ -5913,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;
        }
 
@@ -5928,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);
@@ -6000,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);
@@ -6060,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 */
@@ -6127,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);
 }
 
@@ -6316,21 +6332,16 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
 
        /* flags + rate selection */
 
-       switch (priv->scan_bands) {
-       case 2:
+       if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
                scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
                scan->tx_cmd.rate = IWL_RATE_1M_PLCP;
                scan->good_CRC_th = 0;
                band = IEEE80211_BAND_2GHZ;
-               break;
-
-       case 1:
+       } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) {
                scan->tx_cmd.rate = IWL_RATE_6M_PLCP;
                scan->good_CRC_th = IWL_GOOD_CRC_TH;
                band = IEEE80211_BAND_5GHZ;
-               break;
-
-       default:
+       } else {
                IWL_WARNING("Invalid scan band count\n");
                goto done;
        }
@@ -6770,7 +6781,7 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
        ch_info = iwl3945_get_channel_info(priv, conf->channel->band,
                                           conf->channel->hw_value);
        if (!is_channel_valid(ch_info)) {
-               IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this SKU.\n",
+               IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this band.\n",
                               conf->channel->hw_value, conf->channel->band);
                IWL_DEBUG_MAC80211("leave - invalid channel\n");
                spin_unlock_irqrestore(&priv->lock, flags);
@@ -6833,7 +6844,7 @@ static void iwl3945_config_ap(struct iwl3945_priv *priv)
                return;
 
        /* The following should be done only at AP bring up */
-       if ((priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) == 0) {
+       if (!(iwl3945_is_associated(priv))) {
 
                /* RXON - unassoc (to set timing command) */
                priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
@@ -7409,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)
 {
@@ -7925,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,
@@ -8166,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:
@@ -8228,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)
@@ -8296,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