| /****************************************************************************** |
| * |
| * GPL LICENSE SUMMARY |
| * |
| * Copyright(c) 2008 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, |
| * USA |
| * |
| * The full GNU General Public License is included in this distribution |
| * in the file called LICENSE.GPL. |
| * |
| * Contact Information: |
| * Tomas Winkler <tomas.winkler@intel.com> |
| * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
| *****************************************************************************/ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/version.h> |
| #include <net/mac80211.h> |
| |
| struct iwl_priv; /* FIXME: remove */ |
| #include "iwl-debug.h" |
| #include "iwl-eeprom.h" |
| #include "iwl-4965.h" /* FIXME: remove */ |
| #include "iwl-core.h" |
| #include "iwl-rfkill.h" |
| |
| |
| MODULE_DESCRIPTION("iwl core"); |
| MODULE_VERSION(IWLWIFI_VERSION); |
| MODULE_AUTHOR(DRV_COPYRIGHT); |
| MODULE_LICENSE("GPL"); |
| |
| #ifdef CONFIG_IWLWIFI_DEBUG |
| u32 iwl_debug_level; |
| EXPORT_SYMBOL(iwl_debug_level); |
| #endif |
| |
| /* This function both allocates and initializes hw and priv. */ |
| struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg, |
| struct ieee80211_ops *hw_ops) |
| { |
| struct iwl_priv *priv; |
| |
| /* mac80211 allocates memory for this device instance, including |
| * space for this driver's private structure */ |
| struct ieee80211_hw *hw = |
| ieee80211_alloc_hw(sizeof(struct iwl_priv), hw_ops); |
| if (hw == NULL) { |
| IWL_ERROR("Can not allocate network device\n"); |
| goto out; |
| } |
| |
| priv = hw->priv; |
| priv->hw = hw; |
| |
| out: |
| return hw; |
| } |
| EXPORT_SYMBOL(iwl_alloc_all); |
| |
| /** |
| * iwlcore_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) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&priv->sta_lock, flags); |
| |
| priv->num_stations = 0; |
| memset(priv->stations, 0, sizeof(priv->stations)); |
| |
| spin_unlock_irqrestore(&priv->sta_lock, flags); |
| } |
| EXPORT_SYMBOL(iwlcore_clear_stations_table); |
| |
| void iwlcore_reset_qos(struct iwl_priv *priv) |
| { |
| u16 cw_min = 15; |
| u16 cw_max = 1023; |
| u8 aifs = 2; |
| u8 is_legacy = 0; |
| unsigned long flags; |
| int i; |
| |
| spin_lock_irqsave(&priv->lock, flags); |
| priv->qos_data.qos_active = 0; |
| |
| if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) { |
| if (priv->qos_data.qos_enable) |
| priv->qos_data.qos_active = 1; |
| if (!(priv->active_rate & 0xfff0)) { |
| cw_min = 31; |
| is_legacy = 1; |
| } |
| } else if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { |
| if (priv->qos_data.qos_enable) |
| priv->qos_data.qos_active = 1; |
| } else if (!(priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK)) { |
| cw_min = 31; |
| is_legacy = 1; |
| } |
| |
| if (priv->qos_data.qos_active) |
| aifs = 3; |
| |
| priv->qos_data.def_qos_parm.ac[0].cw_min = cpu_to_le16(cw_min); |
| priv->qos_data.def_qos_parm.ac[0].cw_max = cpu_to_le16(cw_max); |
| priv->qos_data.def_qos_parm.ac[0].aifsn = aifs; |
| priv->qos_data.def_qos_parm.ac[0].edca_txop = 0; |
| priv->qos_data.def_qos_parm.ac[0].reserved1 = 0; |
| |
| if (priv->qos_data.qos_active) { |
| i = 1; |
| priv->qos_data.def_qos_parm.ac[i].cw_min = cpu_to_le16(cw_min); |
| priv->qos_data.def_qos_parm.ac[i].cw_max = cpu_to_le16(cw_max); |
| priv->qos_data.def_qos_parm.ac[i].aifsn = 7; |
| priv->qos_data.def_qos_parm.ac[i].edca_txop = 0; |
| priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| |
| i = 2; |
| priv->qos_data.def_qos_parm.ac[i].cw_min = |
| cpu_to_le16((cw_min + 1) / 2 - 1); |
| priv->qos_data.def_qos_parm.ac[i].cw_max = |
| cpu_to_le16(cw_max); |
| priv->qos_data.def_qos_parm.ac[i].aifsn = 2; |
| if (is_legacy) |
| priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| cpu_to_le16(6016); |
| else |
| priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| cpu_to_le16(3008); |
| priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| |
| i = 3; |
| priv->qos_data.def_qos_parm.ac[i].cw_min = |
| cpu_to_le16((cw_min + 1) / 4 - 1); |
| priv->qos_data.def_qos_parm.ac[i].cw_max = |
| cpu_to_le16((cw_max + 1) / 2 - 1); |
| priv->qos_data.def_qos_parm.ac[i].aifsn = 2; |
| priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| if (is_legacy) |
| priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| cpu_to_le16(3264); |
| else |
| priv->qos_data.def_qos_parm.ac[i].edca_txop = |
| cpu_to_le16(1504); |
| } else { |
| for (i = 1; i < 4; i++) { |
| priv->qos_data.def_qos_parm.ac[i].cw_min = |
| cpu_to_le16(cw_min); |
| priv->qos_data.def_qos_parm.ac[i].cw_max = |
| cpu_to_le16(cw_max); |
| priv->qos_data.def_qos_parm.ac[i].aifsn = aifs; |
| priv->qos_data.def_qos_parm.ac[i].edca_txop = 0; |
| priv->qos_data.def_qos_parm.ac[i].reserved1 = 0; |
| } |
| } |
| IWL_DEBUG_QOS("set QoS to default \n"); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| } |
| EXPORT_SYMBOL(iwlcore_reset_qos); |
| |
| /** |
| * iwlcore_set_rxon_channel - Set the phymode and channel values in staging RXON |
| * @phymode: MODE_IEEE80211A sets to 5.2GHz; all else set to 2.4GHz |
| * @channel: Any channel valid for the requested phymode |
| |
| * In addition to setting the staging RXON, priv->phymode is also set. |
| * |
| * NOTE: Does not commit to the hardware; it sets appropriate bit fields |
| * in the staging RXON flag structure based on the phymode |
| */ |
| int iwlcore_set_rxon_channel(struct iwl_priv *priv, |
| enum ieee80211_band band, |
| u16 channel) |
| { |
| if (!iwl_get_channel_info(priv, band, channel)) { |
| IWL_DEBUG_INFO("Could not set channel to %d [%d]\n", |
| channel, band); |
| return -EINVAL; |
| } |
| |
| if ((le16_to_cpu(priv->staging_rxon.channel) == channel) && |
| (priv->band == band)) |
| return 0; |
| |
| priv->staging_rxon.channel = cpu_to_le16(channel); |
| if (band == IEEE80211_BAND_5GHZ) |
| priv->staging_rxon.flags &= ~RXON_FLG_BAND_24G_MSK; |
| else |
| priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK; |
| |
| priv->band = band; |
| |
| IWL_DEBUG_INFO("Staging channel set to %d [%d]\n", channel, band); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(iwlcore_set_rxon_channel); |
| |
| static void iwlcore_init_hw(struct iwl_priv *priv) |
| { |
| struct ieee80211_hw *hw = priv->hw; |
| hw->rate_control_algorithm = "iwl-4965-rs"; |
| |
| /* Tell mac80211 and its clients (e.g. Wireless Extensions) |
| * the range of signal quality values that we'll provide. |
| * Negative values for level/noise indicate that we'll provide dBm. |
| * For WE, at least, non-0 values here *enable* display of values |
| * in app (iwconfig). */ |
| hw->max_rssi = -20; /* signal level, negative indicates dBm */ |
| hw->max_noise = -20; /* noise level, negative indicates dBm */ |
| hw->max_signal = 100; /* link quality indication (%) */ |
| |
| /* Tell mac80211 our Tx characteristics */ |
| hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE; |
| |
| /* Default value; 4 EDCA QOS priorities */ |
| hw->queues = 4; |
| #ifdef CONFIG_IWL4965_HT |
| /* Enhanced value; more queues, to support 11n aggregation */ |
| hw->queues = 16; |
| #endif /* CONFIG_IWL4965_HT */ |
| } |
| |
| int iwl_setup(struct iwl_priv *priv) |
| { |
| int ret = 0; |
| iwlcore_init_hw(priv); |
| ret = priv->cfg->ops->lib->init_drv(priv); |
| return ret; |
| } |
| EXPORT_SYMBOL(iwl_setup); |
| |
| /* 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); |
| break; |
| case IWLCORE_START_EVT: |
| 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; |
| struct iwl_host_cmd cmd = { |
| .id = REPLY_STATISTICS_CMD, |
| .meta.flags = flags, |
| .len = sizeof(stat_flags), |
| .data = (u8 *) &stat_flags, |
| }; |
| return iwl_send_cmd(priv, &cmd); |
| } |
| EXPORT_SYMBOL(iwl_send_statistics_request); |
| |