]> err.no Git - linux-2.6/blobdiff - drivers/net/wireless/iwlwifi/iwl-core.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild-fixes
[linux-2.6] / drivers / net / wireless / iwlwifi / iwl-core.c
index 21995ff0cc0fadd129801874623aefb035341bf0..9bd61809129f800bfa7ca5ef3ff35482675e0a65 100644 (file)
@@ -85,6 +85,63 @@ const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = {
 };
 EXPORT_SYMBOL(iwl_rates);
 
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+void iwl_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
+                                 struct ieee80211_tx_info *control)
+{
+       int rate_index;
+
+       control->antenna_sel_tx =
+               ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+       if (rate_n_flags & RATE_MCS_HT_MSK)
+               control->flags |= IEEE80211_TX_CTL_OFDM_HT;
+       if (rate_n_flags & RATE_MCS_GF_MSK)
+               control->flags |= IEEE80211_TX_CTL_GREEN_FIELD;
+       if (rate_n_flags & RATE_MCS_FAT_MSK)
+               control->flags |= IEEE80211_TX_CTL_40_MHZ_WIDTH;
+       if (rate_n_flags & RATE_MCS_DUP_MSK)
+               control->flags |= IEEE80211_TX_CTL_DUP_DATA;
+       if (rate_n_flags & RATE_MCS_SGI_MSK)
+               control->flags |= IEEE80211_TX_CTL_SHORT_GI;
+       rate_index = iwl_hwrate_to_plcp_idx(rate_n_flags);
+       if (control->band == IEEE80211_BAND_5GHZ)
+               rate_index -= IWL_FIRST_OFDM_RATE;
+       control->tx_rate_idx = rate_index;
+}
+EXPORT_SYMBOL(iwl_hwrate_to_tx_control);
+
+int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
+{
+       int idx = 0;
+
+       /* HT rate format */
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               idx = (rate_n_flags & 0xff);
+
+               if (idx >= IWL_RATE_MIMO2_6M_PLCP)
+                       idx = idx - IWL_RATE_MIMO2_6M_PLCP;
+
+               idx += IWL_FIRST_OFDM_RATE;
+               /* skip 9M not supported in ht*/
+               if (idx >= IWL_RATE_9M_INDEX)
+                       idx += 1;
+               if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
+                       return idx;
+
+       /* legacy rate format, search for match in table */
+       } else {
+               for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
+                       if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF))
+                               return idx;
+       }
+
+       return -1;
+}
+EXPORT_SYMBOL(iwl_hwrate_to_plcp_idx);
+
+
 
 const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 EXPORT_SYMBOL(iwl_bcast_addr);
@@ -216,22 +273,27 @@ int iwl_hw_nic_init(struct iwl_priv *priv)
 EXPORT_SYMBOL(iwl_hw_nic_init);
 
 /**
- * iwlcore_clear_stations_table - Clear the driver's station table
+ * iwl_clear_stations_table - Clear the driver's station table
  *
  * NOTE:  This does not clear or otherwise alter the device's station table.
  */
-void iwlcore_clear_stations_table(struct iwl_priv *priv)
+void iwl_clear_stations_table(struct iwl_priv *priv)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&priv->sta_lock, flags);
 
+       if (iwl_is_alive(priv) &&
+          !test_bit(STATUS_EXIT_PENDING, &priv->status) &&
+          iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL))
+               IWL_ERROR("Couldn't clear the station table\n");
+
        priv->num_stations = 0;
        memset(priv->stations, 0, sizeof(priv->stations));
 
        spin_unlock_irqrestore(&priv->sta_lock, flags);
 }
-EXPORT_SYMBOL(iwlcore_clear_stations_table);
+EXPORT_SYMBOL(iwl_clear_stations_table);
 
 void iwl_reset_qos(struct iwl_priv *priv)
 {
@@ -321,8 +383,8 @@ void iwl_reset_qos(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_reset_qos);
 
-#define MAX_BIT_RATE_40_MHZ 0x96; /* 150 Mbps */
-#define MAX_BIT_RATE_20_MHZ 0x48; /* 72 Mbps */
+#define MAX_BIT_RATE_40_MHZ 0x96 /* 150 Mbps */
+#define MAX_BIT_RATE_20_MHZ 0x48 /* 72 Mbps */
 static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
                              struct ieee80211_ht_info *ht_info,
                              enum ieee80211_band band)
@@ -433,7 +495,9 @@ static int iwlcore_init_geos(struct iwl_priv *priv)
        sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
        sband->n_bitrates = IWL_RATE_COUNT - IWL_FIRST_OFDM_RATE;
 
-       iwlcore_init_ht_hw_capab(priv, &sband->ht_info, IEEE80211_BAND_5GHZ);
+       if (priv->cfg->sku & IWL_SKU_N)
+               iwlcore_init_ht_hw_capab(priv, &sband->ht_info,
+                                        IEEE80211_BAND_5GHZ);
 
        sband = &priv->bands[IEEE80211_BAND_2GHZ];
        sband->channels = channels;
@@ -441,7 +505,9 @@ static int iwlcore_init_geos(struct iwl_priv *priv)
        sband->bitrates = rates;
        sband->n_bitrates = IWL_RATE_COUNT;
 
-       iwlcore_init_ht_hw_capab(priv, &sband->ht_info, IEEE80211_BAND_2GHZ);
+       if (priv->cfg->sku & IWL_SKU_N)
+               iwlcore_init_ht_hw_capab(priv, &sband->ht_info,
+                                        IEEE80211_BAND_2GHZ);
 
        priv->ieee_channels = channels;
        priv->ieee_rates = rates;
@@ -480,9 +546,8 @@ static int iwlcore_init_geos(struct iwl_priv *priv)
 
                        geo_ch->flags |= ch->fat_extension_channel;
 
-                       if (ch->max_power_avg > priv->max_channel_txpower_limit)
-                               priv->max_channel_txpower_limit =
-                                   ch->max_power_avg;
+                       if (ch->max_power_avg > priv->tx_power_channel_lmt)
+                               priv->tx_power_channel_lmt = ch->max_power_avg;
                } else {
                        geo_ch->flags |= IEEE80211_CHAN_DISABLED;
                }
@@ -750,18 +815,19 @@ int iwl_setup_mac(struct iwl_priv *priv)
 {
        int ret;
        struct ieee80211_hw *hw = priv->hw;
-       hw->rate_control_algorithm = "iwl-4965-rs";
+       hw->rate_control_algorithm = "iwl-agn-rs";
 
        /* Tell mac80211 our characteristics */
-       hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE |
-                   IEEE80211_HW_SIGNAL_DBM |
+       hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM;
        /* Default value; 4 EDCA QOS priorities */
        hw->queues = 4;
-       /* Enhanced value; more queues, to support 11n aggregation */
-       hw->ampdu_queues = 12;
+       /* queues to support 11n aggregation */
+       if (priv->cfg->sku & IWL_SKU_N)
+               hw->ampdu_queues = priv->cfg->mod_params->num_of_ampdu_queues;
 
        hw->conf.beacon_int = 100;
+       hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
 
        if (priv->bands[IEEE80211_BAND_2GHZ].n_channels)
                priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
@@ -781,11 +847,28 @@ int iwl_setup_mac(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_setup_mac);
 
+int iwl_set_hw_params(struct iwl_priv *priv)
+{
+       priv->hw_params.sw_crypto = priv->cfg->mod_params->sw_crypto;
+       priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
+       priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
+       if (priv->cfg->mod_params->amsdu_size_8K)
+               priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_8K;
+       else
+               priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_4K;
+       priv->hw_params.max_pkt_size = priv->hw_params.rx_buf_size - 256;
+
+       if (priv->cfg->mod_params->disable_11n)
+               priv->cfg->sku &= ~IWL_SKU_N;
+
+       /* Device-specific setup */
+       return priv->cfg->ops->lib->set_hw_params(priv);
+}
+EXPORT_SYMBOL(iwl_set_hw_params);
 
 int iwl_init_drv(struct iwl_priv *priv)
 {
        int ret;
-       int i;
 
        priv->retry_rate = 1;
        priv->ibss_beacon = NULL;
@@ -796,15 +879,12 @@ int iwl_init_drv(struct iwl_priv *priv)
        spin_lock_init(&priv->hcmd_lock);
        spin_lock_init(&priv->lq_mngr.lock);
 
-       for (i = 0; i < IWL_IBSS_MAC_HASH_SIZE; i++)
-               INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
-
        INIT_LIST_HEAD(&priv->free_frames);
 
        mutex_init(&priv->mutex);
 
        /* Clear the driver's (not device's) station table */
-       iwlcore_clear_stations_table(priv);
+       iwl_clear_stations_table(priv);
 
        priv->data_retry_limit = -1;
        priv->ieee_channels = NULL;
@@ -818,6 +898,7 @@ int iwl_init_drv(struct iwl_priv *priv)
 
        /* Choose which receivers/antennas to use */
        iwl_set_rxon_chain(priv);
+       iwl_init_scan_params(priv);
 
        if (priv->cfg->mod_params->enable_qos)
                priv->qos_data.qos_enable = 1;
@@ -832,7 +913,7 @@ int iwl_init_drv(struct iwl_priv *priv)
        priv->rates_mask = IWL_RATES_MASK;
        /* If power management is turned on, default to AC mode */
        priv->power_mode = IWL_POWER_AC;
-       priv->user_txpower_limit = IWL_DEFAULT_TX_POWER;
+       priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MAX;
 
        ret = iwl_init_channel_map(priv);
        if (ret) {
@@ -871,6 +952,34 @@ void iwl_free_calib_results(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_free_calib_results);
 
+int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
+{
+       int ret = 0;
+       if (tx_power < IWL_TX_POWER_TARGET_POWER_MIN) {
+               IWL_WARNING("Requested user TXPOWER %d below limit.\n",
+                           priv->tx_power_user_lmt);
+               return -EINVAL;
+       }
+
+       if (tx_power > IWL_TX_POWER_TARGET_POWER_MAX) {
+               IWL_WARNING("Requested user TXPOWER %d above limit.\n",
+                           priv->tx_power_user_lmt);
+               return -EINVAL;
+       }
+
+       if (priv->tx_power_user_lmt != tx_power)
+               force = true;
+
+       priv->tx_power_user_lmt = tx_power;
+
+       if (force && priv->cfg->ops->lib->send_tx_power)
+               ret = priv->cfg->ops->lib->send_tx_power(priv);
+
+       return ret;
+}
+EXPORT_SYMBOL(iwl_set_tx_power);
+
+
 void iwl_uninit_drv(struct iwl_priv *priv)
 {
        iwl_free_calib_results(priv);
@@ -880,35 +989,6 @@ void iwl_uninit_drv(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_uninit_drv);
 
-/* Low level driver call this function to update iwlcore with
- * driver status.
- */
-int iwlcore_low_level_notify(struct iwl_priv *priv,
-                             enum iwlcore_card_notify notify)
-{
-       int ret;
-       switch (notify) {
-       case IWLCORE_INIT_EVT:
-               ret = iwl_rfkill_init(priv);
-               if (ret)
-                       IWL_ERROR("Unable to initialize RFKILL system. "
-                                 "Ignoring error: %d\n", ret);
-               iwl_power_initialize(priv);
-               break;
-       case IWLCORE_START_EVT:
-               iwl_power_update_mode(priv, 1);
-               break;
-       case IWLCORE_STOP_EVT:
-               break;
-       case IWLCORE_REMOVE_EVT:
-               iwl_rfkill_unregister(priv);
-               break;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(iwlcore_low_level_notify);
-
 int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags)
 {
        u32 stat_flags = 0;
@@ -1239,4 +1319,123 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_dump_nic_event_log);
 
+void iwl_rf_kill_ct_config(struct iwl_priv *priv)
+{
+       struct iwl_ct_kill_config cmd;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
+                   CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       cmd.critical_temperature_R =
+               cpu_to_le32(priv->hw_params.ct_kill_threshold);
+
+       ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD,
+                              sizeof(cmd), &cmd);
+       if (ret)
+               IWL_ERROR("REPLY_CT_KILL_CONFIG_CMD failed\n");
+       else
+               IWL_DEBUG_INFO("REPLY_CT_KILL_CONFIG_CMD succeeded, "
+                       "critical temperature is %d\n",
+                       cmd.critical_temperature_R);
+}
+EXPORT_SYMBOL(iwl_rf_kill_ct_config);
+
+/*
+ * CARD_STATE_CMD
+ *
+ * Use: Sets the device's internal card state to enable, disable, or halt
+ *
+ * When in the 'enable' state the card operates as normal.
+ * When in the 'disable' state, the card enters into a low power mode.
+ * When in the 'halt' state, the card is shut down and must be fully
+ * restarted to come back on.
+ */
+static int iwl_send_card_state(struct iwl_priv *priv, u32 flags, u8 meta_flag)
+{
+       struct iwl_host_cmd cmd = {
+               .id = REPLY_CARD_STATE_CMD,
+               .len = sizeof(u32),
+               .data = &flags,
+               .meta.flags = meta_flag,
+       };
+
+       return iwl_send_cmd(priv, &cmd);
+}
+
+void iwl_radio_kill_sw_disable_radio(struct iwl_priv *priv)
+{
+       unsigned long flags;
+
+       if (test_bit(STATUS_RF_KILL_SW, &priv->status))
+               return;
+
+       IWL_DEBUG_RF_KILL("Manual SW RF KILL set to: RADIO OFF\n");
+
+       iwl_scan_cancel(priv);
+       /* FIXME: This is a workaround for AP */
+       if (priv->iw_mode != IEEE80211_IF_TYPE_AP) {
+               spin_lock_irqsave(&priv->lock, flags);
+               iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
+                           CSR_UCODE_SW_BIT_RFKILL);
+               spin_unlock_irqrestore(&priv->lock, flags);
+               /* call the host command only if no hw rf-kill set */
+               if (!test_bit(STATUS_RF_KILL_HW, &priv->status) &&
+                   iwl_is_ready(priv))
+                       iwl_send_card_state(priv,
+                               CARD_STATE_CMD_DISABLE, 0);
+               set_bit(STATUS_RF_KILL_SW, &priv->status);
+                       /* make sure mac80211 stop sending Tx frame */
+               if (priv->mac80211_registered)
+                       ieee80211_stop_queues(priv->hw);
+       }
+}
+EXPORT_SYMBOL(iwl_radio_kill_sw_disable_radio);
+
+int iwl_radio_kill_sw_enable_radio(struct iwl_priv *priv)
+{
+       unsigned long flags;
+
+       if (!test_bit(STATUS_RF_KILL_SW, &priv->status))
+               return 0;
+
+       IWL_DEBUG_RF_KILL("Manual SW RF KILL set to: RADIO ON\n");
 
+       spin_lock_irqsave(&priv->lock, flags);
+       iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+
+       /* If the driver is up it will receive CARD_STATE_NOTIFICATION
+        * notification where it will clear SW rfkill status.
+        * Setting it here would break the handler. Only if the
+        * interface is down we can set here since we don't
+        * receive any further notification.
+        */
+       if (!priv->is_open)
+               clear_bit(STATUS_RF_KILL_SW, &priv->status);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* wake up ucode */
+       msleep(10);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       iwl_read32(priv, CSR_UCODE_DRV_GP1);
+       if (!iwl_grab_nic_access(priv))
+               iwl_release_nic_access(priv);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
+               IWL_DEBUG_RF_KILL("Can not turn radio back on - "
+                                 "disabled by HW switch\n");
+               return 0;
+       }
+
+       /* If the driver is already loaded, it will receive
+        * CARD_STATE_NOTIFICATION notifications and the handler will
+        * call restart to reload the driver.
+        */
+       return 1;
+}
+EXPORT_SYMBOL(iwl_radio_kill_sw_enable_radio);