提交 bb9f8692 编写于 作者: Z Zhu Yi 提交者: John W. Linville

iwmc3200wifi: Add new Intel Wireless Multicomm 802.11 driver

This driver supports Intel's full MAC wireless multicomm 802.11 hardware.
Although the hardware is a 802.11agn device, we currently only support
802.11ag, in managed and ad-hoc mode (no AP mode for now).
Signed-off-by: NZhu Yi <yi.zhu@intel.com>
Signed-off-by: NSamuel Ortiz <samuel.ortiz@intel.com>
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
上级 e31a16d6
......@@ -501,5 +501,6 @@ source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/orinoco/Kconfig"
source "drivers/net/wireless/wl12xx/Kconfig"
source "drivers/net/wireless/iwmc3200wifi/Kconfig"
endmenu
......@@ -60,3 +60,5 @@ obj-$(CONFIG_ATH_COMMON) += ath/
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_IWM) += iwmc3200wifi/
config IWM
tristate "Intel Wireless Multicomm 3200 WiFi driver"
depends on MMC && WLAN_80211 && EXPERIMENTAL
select LIB80211
select FW_LOADER
select RFKILL
config IWM_DEBUG
bool "Enable full debugging output in iwmc3200wifi"
depends on IWM && DEBUG_FS
---help---
This option will enable debug tracing and setting for iwm
You can set the debug level and module through debugfs. By
default all modules are set to the IWL_DL_ERR level.
To see the list of debug modules and levels, see iwm/debug.h
For example, if you want the full MLME debug output:
echo 0xff > /debug/iwm/phyN/debug/mlme
Or, if you want the full debug, for all modules:
echo 0xff > /debug/iwm/phyN/debug/level
echo 0xff > /debug/iwm/phyN/debug/modules
obj-$(CONFIG_IWM) := iwmc3200wifi.o
iwmc3200wifi-objs += main.o netdev.o rx.o tx.o sdio.o hal.o fw.o
iwmc3200wifi-objs += commands.o wext.o cfg80211.o eeprom.o rfkill.o
iwmc3200wifi-$(CONFIG_IWM_DEBUG) += debugfs.o
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 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-1301, USA.
*
*/
#ifndef __IWM_BUS_H__
#define __IWM_BUS_H__
#include "iwm.h"
struct iwm_if_ops {
int (*enable)(struct iwm_priv *iwm);
int (*disable)(struct iwm_priv *iwm);
int (*send_chunk)(struct iwm_priv *iwm, u8* buf, int count);
int (*debugfs_init)(struct iwm_priv *iwm, struct dentry *parent_dir);
void (*debugfs_exit)(struct iwm_priv *iwm);
const char *umac_name;
const char *calib_lmac_name;
const char *lmac_name;
};
static inline int iwm_bus_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
{
return iwm->bus_ops->send_chunk(iwm, buf, count);
}
static inline int iwm_bus_enable(struct iwm_priv *iwm)
{
return iwm->bus_ops->enable(iwm);
}
static inline int iwm_bus_disable(struct iwm_priv *iwm)
{
return iwm->bus_ops->disable(iwm);
}
#endif
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 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-1301, USA.
*
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include "iwm.h"
#include "commands.h"
#include "cfg80211.h"
#include "debug.h"
#define RATETAB_ENT(_rate, _rateid, _flags) \
{ \
.bitrate = (_rate), \
.hw_value = (_rateid), \
.flags = (_flags), \
}
#define CHAN2G(_channel, _freq, _flags) { \
.band = IEEE80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
#define CHAN5G(_channel, _flags) { \
.band = IEEE80211_BAND_5GHZ, \
.center_freq = 5000 + (5 * (_channel)), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
static struct ieee80211_rate iwm_rates[] = {
RATETAB_ENT(10, 0x1, 0),
RATETAB_ENT(20, 0x2, 0),
RATETAB_ENT(55, 0x4, 0),
RATETAB_ENT(110, 0x8, 0),
RATETAB_ENT(60, 0x10, 0),
RATETAB_ENT(90, 0x20, 0),
RATETAB_ENT(120, 0x40, 0),
RATETAB_ENT(180, 0x80, 0),
RATETAB_ENT(240, 0x100, 0),
RATETAB_ENT(360, 0x200, 0),
RATETAB_ENT(480, 0x400, 0),
RATETAB_ENT(540, 0x800, 0),
};
#define iwm_a_rates (iwm_rates + 4)
#define iwm_a_rates_size 8
#define iwm_g_rates (iwm_rates + 0)
#define iwm_g_rates_size 12
static struct ieee80211_channel iwm_2ghz_channels[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
CHAN2G(3, 2422, 0),
CHAN2G(4, 2427, 0),
CHAN2G(5, 2432, 0),
CHAN2G(6, 2437, 0),
CHAN2G(7, 2442, 0),
CHAN2G(8, 2447, 0),
CHAN2G(9, 2452, 0),
CHAN2G(10, 2457, 0),
CHAN2G(11, 2462, 0),
CHAN2G(12, 2467, 0),
CHAN2G(13, 2472, 0),
CHAN2G(14, 2484, 0),
};
static struct ieee80211_channel iwm_5ghz_a_channels[] = {
CHAN5G(34, 0), CHAN5G(36, 0),
CHAN5G(38, 0), CHAN5G(40, 0),
CHAN5G(42, 0), CHAN5G(44, 0),
CHAN5G(46, 0), CHAN5G(48, 0),
CHAN5G(52, 0), CHAN5G(56, 0),
CHAN5G(60, 0), CHAN5G(64, 0),
CHAN5G(100, 0), CHAN5G(104, 0),
CHAN5G(108, 0), CHAN5G(112, 0),
CHAN5G(116, 0), CHAN5G(120, 0),
CHAN5G(124, 0), CHAN5G(128, 0),
CHAN5G(132, 0), CHAN5G(136, 0),
CHAN5G(140, 0), CHAN5G(149, 0),
CHAN5G(153, 0), CHAN5G(157, 0),
CHAN5G(161, 0), CHAN5G(165, 0),
CHAN5G(184, 0), CHAN5G(188, 0),
CHAN5G(192, 0), CHAN5G(196, 0),
CHAN5G(200, 0), CHAN5G(204, 0),
CHAN5G(208, 0), CHAN5G(212, 0),
CHAN5G(216, 0),
};
static struct ieee80211_supported_band iwm_band_2ghz = {
.channels = iwm_2ghz_channels,
.n_channels = ARRAY_SIZE(iwm_2ghz_channels),
.bitrates = iwm_g_rates,
.n_bitrates = iwm_g_rates_size,
};
static struct ieee80211_supported_band iwm_band_5ghz = {
.channels = iwm_5ghz_a_channels,
.n_channels = ARRAY_SIZE(iwm_5ghz_a_channels),
.bitrates = iwm_a_rates,
.n_bitrates = iwm_a_rates_size,
};
int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
{
struct wiphy *wiphy = iwm_to_wiphy(iwm);
struct iwm_bss_info *bss, *next;
struct iwm_umac_notif_bss_info *umac_bss;
struct ieee80211_mgmt *mgmt;
struct ieee80211_channel *channel;
struct ieee80211_supported_band *band;
s32 signal;
int freq;
list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
umac_bss = bss->bss;
mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
if (umac_bss->band == UMAC_BAND_2GHZ)
band = wiphy->bands[IEEE80211_BAND_2GHZ];
else if (umac_bss->band == UMAC_BAND_5GHZ)
band = wiphy->bands[IEEE80211_BAND_5GHZ];
else {
IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
return -EINVAL;
}
freq = ieee80211_channel_to_frequency(umac_bss->channel);
channel = ieee80211_get_channel(wiphy, freq);
signal = umac_bss->rssi * 100;
if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
le16_to_cpu(umac_bss->frame_len),
signal, GFP_KERNEL))
return -EINVAL;
}
return 0;
}
static int iwm_cfg80211_change_iface(struct wiphy *wiphy, int ifindex,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
struct net_device *ndev;
struct wireless_dev *wdev;
struct iwm_priv *iwm;
u32 old_mode;
/* we're under RTNL */
ndev = __dev_get_by_index(&init_net, ifindex);
if (!ndev)
return -ENODEV;
wdev = ndev->ieee80211_ptr;
iwm = ndev_to_iwm(ndev);
old_mode = iwm->conf.mode;
switch (type) {
case NL80211_IFTYPE_STATION:
iwm->conf.mode = UMAC_MODE_BSS;
break;
case NL80211_IFTYPE_ADHOC:
iwm->conf.mode = UMAC_MODE_IBSS;
break;
default:
return -EOPNOTSUPP;
}
wdev->iftype = type;
if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
return 0;
iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
if (iwm->umac_profile_active) {
int ret = iwm_invalidate_mlme_profile(iwm);
if (ret < 0)
IWM_ERR(iwm, "Couldn't invalidate profile\n");
}
return 0;
}
static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_scan_request *request)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
int ret;
if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
IWM_ERR(iwm, "Scan while device is not ready\n");
return -EIO;
}
if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
IWM_ERR(iwm, "Scanning already\n");
return -EAGAIN;
}
if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
IWM_ERR(iwm, "Scanning being aborted\n");
return -EAGAIN;
}
set_bit(IWM_STATUS_SCANNING, &iwm->status);
ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
if (ret) {
clear_bit(IWM_STATUS_SCANNING, &iwm->status);
return ret;
}
iwm->scan_request = request;
return 0;
}
static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
(iwm->conf.rts_threshold != wiphy->rts_threshold)) {
int ret;
iwm->conf.rts_threshold = wiphy->rts_threshold;
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
CFG_RTS_THRESHOLD,
iwm->conf.rts_threshold);
if (ret < 0)
return ret;
}
if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
(iwm->conf.frag_threshold != wiphy->frag_threshold)) {
int ret;
iwm->conf.frag_threshold = wiphy->frag_threshold;
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
CFG_FRAG_THRESHOLD,
iwm->conf.frag_threshold);
if (ret < 0)
return ret;
}
return 0;
}
static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ibss_params *params)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
struct ieee80211_channel *chan = params->channel;
struct cfg80211_bss *bss;
if (!test_bit(IWM_STATUS_READY, &iwm->status))
return -EIO;
/* UMAC doesn't support creating IBSS network with specified bssid.
* This should be removed after we have join only mode supported. */
if (params->bssid)
return -EOPNOTSUPP;
bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
params->ssid, params->ssid_len);
if (!bss) {
iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
schedule_timeout_interruptible(2 * HZ);
bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
params->ssid, params->ssid_len);
}
/* IBSS join only mode is not supported by UMAC ATM */
if (bss) {
cfg80211_put_bss(bss);
return -EOPNOTSUPP;
}
iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
iwm->umac_profile->ibss.band = chan->band;
iwm->umac_profile->ibss.channel = iwm->channel;
iwm->umac_profile->ssid.ssid_len = params->ssid_len;
memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
if (params->bssid)
memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
return iwm_send_mlme_profile(iwm);
}
static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
if (iwm->umac_profile_active)
return iwm_invalidate_mlme_profile(iwm);
return 0;
}
static struct cfg80211_ops iwm_cfg80211_ops = {
.change_virtual_intf = iwm_cfg80211_change_iface,
.scan = iwm_cfg80211_scan,
.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
.join_ibss = iwm_cfg80211_join_ibss,
.leave_ibss = iwm_cfg80211_leave_ibss,
};
struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
{
int ret = 0;
struct wireless_dev *wdev;
/*
* We're trying to have the following memory
* layout:
*
* +-------------------------+
* | struct wiphy |
* +-------------------------+
* | struct iwm_priv |
* +-------------------------+
* | bus private data |
* | (e.g. iwm_priv_sdio) |
* +-------------------------+
*
*/
wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
if (!wdev) {
dev_err(dev, "Couldn't allocate wireless device\n");
return ERR_PTR(-ENOMEM);
}
wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
sizeof(struct iwm_priv) + sizeof_bus);
if (!wdev->wiphy) {
dev_err(dev, "Couldn't allocate wiphy device\n");
ret = -ENOMEM;
goto out_err_new;
}
set_wiphy_dev(wdev->wiphy, dev);
wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
ret = wiphy_register(wdev->wiphy);
if (ret < 0) {
dev_err(dev, "Couldn't register wiphy device\n");
goto out_err_register;
}
return wdev;
out_err_register:
wiphy_free(wdev->wiphy);
out_err_new:
kfree(wdev);
return ERR_PTR(ret);
}
void iwm_wdev_free(struct iwm_priv *iwm)
{
struct wireless_dev *wdev = iwm_to_wdev(iwm);
if (!wdev)
return;
wiphy_unregister(wdev->wiphy);
wiphy_free(wdev->wiphy);
kfree(wdev);
}
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 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-1301, USA.
*
*/
#ifndef __IWM_CFG80211_H__
#define __IWM_CFG80211_H__
int iwm_cfg80211_inform_bss(struct iwm_priv *iwm);
struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev);
void iwm_wdev_free(struct iwm_priv *iwm);
#endif
此差异已折叠。
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#ifndef __IWM_COMMANDS_H__
#define __IWM_COMMANDS_H__
#include <linux/ieee80211.h>
#define IWM_BARKER_REBOOT_NOTIFICATION 0xF
#define IWM_ACK_BARKER_NOTIFICATION 0x10
/* UMAC commands */
#define UMAC_RST_CTRL_FLG_LARC_CLK_EN 0x0001
#define UMAC_RST_CTRL_FLG_LARC_RESET 0x0002
#define UMAC_RST_CTRL_FLG_FUNC_RESET 0x0004
#define UMAC_RST_CTRL_FLG_DEV_RESET 0x0008
#define UMAC_RST_CTRL_FLG_WIFI_CORE_EN 0x0010
#define UMAC_RST_CTRL_FLG_WIFI_LINK_EN 0x0040
#define UMAC_RST_CTRL_FLG_WIFI_MLME_EN 0x0080
#define UMAC_RST_CTRL_FLG_NVM_RELOAD 0x0100
struct iwm_umac_cmd_reset {
__le32 flags;
} __attribute__ ((packed));
#define UMAC_PARAM_TBL_ORD_FIX 0x0
#define UMAC_PARAM_TBL_ORD_VAR 0x1
#define UMAC_PARAM_TBL_CFG_FIX 0x2
#define UMAC_PARAM_TBL_CFG_VAR 0x3
#define UMAC_PARAM_TBL_BSS_TRK 0x4
#define UMAC_PARAM_TBL_FA_CFG_FIX 0x5
#define UMAC_PARAM_TBL_STA 0x6
#define UMAC_PARAM_TBL_CHN 0x7
#define UMAC_PARAM_TBL_STATISTICS 0x8
/* fast access table */
enum {
CFG_FRAG_THRESHOLD = 0,
CFG_FRAME_RETRY_LIMIT,
CFG_OS_QUEUE_UTIL_TH,
CFG_RX_FILTER,
/* <-- LAST --> */
FAST_ACCESS_CFG_TBL_FIX_LAST
};
/* fixed size table */
enum {
CFG_POWER_INDEX = 0,
CFG_PM_LEGACY_RX_TIMEOUT,
CFG_PM_LEGACY_TX_TIMEOUT,
CFG_PM_CTRL_FLAGS,
CFG_PM_KEEP_ALIVE_IN_BEACONS,
CFG_BT_ON_THRESHOLD,
CFG_RTS_THRESHOLD,
CFG_CTS_TO_SELF,
CFG_COEX_MODE,
CFG_WIRELESS_MODE,
CFG_ASSOCIATION_TIMEOUT,
CFG_ROAM_TIMEOUT,
CFG_CAPABILITY_SUPPORTED_RATES,
CFG_SCAN_ALLOWED_UNASSOC_FLAGS,
CFG_SCAN_ALLOWED_MAIN_ASSOC_FLAGS,
CFG_SCAN_ALLOWED_PAN_ASSOC_FLAGS,
CFG_SCAN_INTERNAL_PERIODIC_ENABLED,
CFG_SCAN_IMM_INTERNAL_PERIODIC_SCAN_ON_INIT,
CFG_SCAN_DEFAULT_PERIODIC_FREQ_SEC,
CFG_SCAN_NUM_PASSIVE_CHAN_PER_PARTIAL_SCAN,
CFG_TLC_SUPPORTED_TX_HT_RATES,
CFG_TLC_SUPPORTED_TX_RATES,
CFG_TLC_VALID_ANTENNA,
CFG_TLC_SPATIAL_STREAM_SUPPORTED,
CFG_TLC_RETRY_PER_RATE,
CFG_TLC_RETRY_PER_HT_RATE,
CFG_TLC_FIXED_RATE,
CFG_TLC_FIXED_RATE_FLAGS,
CFG_TLC_CONTROL_FLAGS,
CFG_TLC_SR_MIN_FAIL,
CFG_TLC_SR_MIN_PASS,
CFG_TLC_HT_STAY_IN_COL_PASS_THRESH,
CFG_TLC_HT_STAY_IN_COL_FAIL_THRESH,
CFG_TLC_LEGACY_STAY_IN_COL_PASS_THRESH,
CFG_TLC_LEGACY_STAY_IN_COL_FAIL_THRESH,
CFG_TLC_HT_FLUSH_STATS_PACKETS,
CFG_TLC_LEGACY_FLUSH_STATS_PACKETS,
CFG_TLC_LEGACY_FLUSH_STATS_MS,
CFG_TLC_HT_FLUSH_STATS_MS,
CFG_TLC_STAY_IN_COL_TIME_OUT,
CFG_TLC_AGG_SHORT_LIM,
CFG_TLC_AGG_LONG_LIM,
CFG_TLC_HT_SR_NO_DECREASE,
CFG_TLC_LEGACY_SR_NO_DECREASE,
CFG_TLC_SR_FORCE_DECREASE,
CFG_TLC_SR_ALLOW_INCREASE,
CFG_TLC_AGG_SET_LONG,
CFG_TLC_AUTO_AGGREGATION,
CFG_TLC_AGG_THRESHOLD,
CFG_TLC_TID_LOAD_THRESHOLD,
CFG_TLC_BLOCK_ACK_TIMEOUT,
CFG_TLC_NO_BA_COUNTED_AS_ONE,
CFG_TLC_NUM_BA_STREAMS_ALLOWED,
CFG_TLC_NUM_BA_STREAMS_PRESENT,
CFG_TLC_RENEW_ADDBA_DELAY,
CFG_TLC_NUM_OF_MULTISEC_TO_COUN_LOAD,
CFG_TLC_IS_STABLE_IN_HT,
CFG_RLC_CHAIN_CTRL,
CFG_TRK_TABLE_OP_MODE,
CFG_TRK_TABLE_RSSI_THRESHOLD,
CFG_TX_PWR_TARGET, /* Used By xVT */
CFG_TX_PWR_LIMIT_USR,
CFG_TX_PWR_LIMIT_BSS, /* 11d limit */
CFG_TX_PWR_LIMIT_BSS_CONSTRAINT, /* 11h constraint */
CFG_TX_PWR_MODE,
CFG_MLME_DBG_NOTIF_BLOCK,
CFG_BT_OFF_BECONS_INTERVALS,
CFG_BT_FRAG_DURATION,
/* <-- LAST --> */
CFG_TBL_FIX_LAST
};
/* variable size table */
enum {
CFG_NET_ADDR = 0,
CFG_PROFILE,
/* <-- LAST --> */
CFG_TBL_VAR_LAST
};
struct iwm_umac_cmd_set_param_fix {
__le16 tbl;
__le16 key;
__le32 value;
} __attribute__ ((packed));
struct iwm_umac_cmd_set_param_var {
__le16 tbl;
__le16 key;
__le16 len;
__le16 reserved;
} __attribute__ ((packed));
struct iwm_umac_cmd_get_param {
__le16 tbl;
__le16 key;
} __attribute__ ((packed));
struct iwm_umac_cmd_get_param_resp {
__le16 tbl;
__le16 key;
__le16 len;
__le16 reserved;
} __attribute__ ((packed));
struct iwm_umac_cmd_eeprom_proxy_hdr {
__le32 type;
__le32 offset;
__le32 len;
} __attribute__ ((packed));
struct iwm_umac_cmd_eeprom_proxy {
struct iwm_umac_cmd_eeprom_proxy_hdr hdr;
u8 buf[0];
} __attribute__ ((packed));
#define IWM_UMAC_CMD_EEPROM_TYPE_READ 0x1
#define IWM_UMAC_CMD_EEPROM_TYPE_WRITE 0x2
#define UMAC_CHANNEL_FLAG_VALID BIT(0)
#define UMAC_CHANNEL_FLAG_IBSS BIT(1)
#define UMAC_CHANNEL_FLAG_ACTIVE BIT(3)
#define UMAC_CHANNEL_FLAG_RADAR BIT(4)
#define UMAC_CHANNEL_FLAG_DFS BIT(7)
struct iwm_umac_channel_info {
u8 band;
u8 type;
u8 reserved;
u8 flags;
__le32 channels_mask;
} __attribute__ ((packed));
struct iwm_umac_cmd_get_channel_list {
__le16 count;
__le16 reserved;
struct iwm_umac_channel_info ch[0];
} __attribute__ ((packed));
/* UMAC WiFi interface commands */
/* Coexistence mode */
#define COEX_MODE_SA 0x1
#define COEX_MODE_XOR 0x2
#define COEX_MODE_CM 0x3
#define COEX_MODE_MAX 0x4
/* Wireless mode */
#define WIRELESS_MODE_11A 0x1
#define WIRELESS_MODE_11G 0x2
#define UMAC_PROFILE_EX_IE_REQUIRED 0x1
#define UMAC_PROFILE_QOS_ALLOWED 0x2
/* Scanning */
#define UMAC_WIFI_IF_PROBE_OPTION_MAX 10
#define UMAC_WIFI_IF_SCAN_TYPE_USER 0x0
#define UMAC_WIFI_IF_SCAN_TYPE_UMAC_RESERVED 0x1
#define UMAC_WIFI_IF_SCAN_TYPE_HOST_PERIODIC 0x2
#define UMAC_WIFI_IF_SCAN_TYPE_MAX 0x3
struct iwm_umac_ssid {
u8 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 reserved[3];
} __attribute__ ((packed));
struct iwm_umac_cmd_scan_request {
struct iwm_umac_wifi_if hdr;
__le32 type; /* UMAC_WIFI_IF_SCAN_TYPE_* */
u8 ssid_num;
u8 seq_num;
u8 timeout; /* In seconds */
u8 reserved;
struct iwm_umac_ssid ssids[UMAC_WIFI_IF_PROBE_OPTION_MAX];
} __attribute__ ((packed));
#define UMAC_CIPHER_TYPE_NONE 0xFF
#define UMAC_CIPHER_TYPE_USE_GROUPCAST 0x00
#define UMAC_CIPHER_TYPE_WEP_40 0x01
#define UMAC_CIPHER_TYPE_WEP_104 0x02
#define UMAC_CIPHER_TYPE_TKIP 0x04
#define UMAC_CIPHER_TYPE_CCMP 0x08
/* Supported authentication types - bitmap */
#define UMAC_AUTH_TYPE_OPEN 0x00
#define UMAC_AUTH_TYPE_LEGACY_PSK 0x01
#define UMAC_AUTH_TYPE_8021X 0x02
#define UMAC_AUTH_TYPE_RSNA_PSK 0x04
/* iwm_umac_security.flag is WPA supported -- bits[0:0] */
#define UMAC_SEC_FLG_WPA_ON_POS 0
#define UMAC_SEC_FLG_WPA_ON_SEED 1
#define UMAC_SEC_FLG_WPA_ON_MSK (UMAC_SEC_FLG_WPA_ON_SEED << \
UMAC_SEC_FLG_WPA_ON_POS)
/* iwm_umac_security.flag is WPA2 supported -- bits [1:1] */
#define UMAC_SEC_FLG_RSNA_ON_POS 1
#define UMAC_SEC_FLG_RSNA_ON_SEED 1
#define UMAC_SEC_FLG_RSNA_ON_MSK (UMAC_SEC_FLG_RSNA_ON_SEED << \
UMAC_SEC_FLG_RSNA_ON_POS)
/* iwm_umac_security.flag is WSC mode on -- bits [2:2] */
#define UMAC_SEC_FLG_WSC_ON_POS 2
#define UMAC_SEC_FLG_WSC_ON_SEED 1
/* Legacy profile can use only WEP40 and WEP104 for encryption and
* OPEN or PSK for authentication */
#define UMAC_SEC_FLG_LEGACY_PROFILE 0
struct iwm_umac_security {
u8 auth_type;
u8 ucast_cipher;
u8 mcast_cipher;
u8 flags;
} __attribute__ ((packed));
struct iwm_umac_ibss {
u8 beacon_interval; /* in millisecond */
u8 atim; /* in millisecond */
s8 join_only;
u8 band;
u8 channel;
u8 reserved[3];
} __attribute__ ((packed));
#define UMAC_MODE_BSS 0
#define UMAC_MODE_IBSS 1
#define UMAC_BSSID_MAX 4
struct iwm_umac_profile {
struct iwm_umac_wifi_if hdr;
__le32 mode;
struct iwm_umac_ssid ssid;
u8 bssid[UMAC_BSSID_MAX][ETH_ALEN];
struct iwm_umac_security sec;
struct iwm_umac_ibss ibss;
__le32 channel_2ghz;
__le32 channel_5ghz;
__le16 flags;
u8 wireless_mode;
u8 bss_num;
} __attribute__ ((packed));
struct iwm_umac_invalidate_profile {
struct iwm_umac_wifi_if hdr;
u8 reason;
u8 reserved[3];
} __attribute__ ((packed));
/* Encryption key commands */
struct iwm_umac_key_wep40 {
struct iwm_umac_wifi_if hdr;
struct iwm_umac_key_hdr key_hdr;
u8 key[WLAN_KEY_LEN_WEP40];
u8 static_key;
u8 reserved[2];
} __attribute__ ((packed));
struct iwm_umac_key_wep104 {
struct iwm_umac_wifi_if hdr;
struct iwm_umac_key_hdr key_hdr;
u8 key[WLAN_KEY_LEN_WEP104];
u8 static_key;
u8 reserved[2];
} __attribute__ ((packed));
#define IWM_TKIP_KEY_SIZE 16
#define IWM_TKIP_MIC_SIZE 8
struct iwm_umac_key_tkip {
struct iwm_umac_wifi_if hdr;
struct iwm_umac_key_hdr key_hdr;
u8 iv_count[6];
u8 reserved[2];
u8 tkip_key[IWM_TKIP_KEY_SIZE];
u8 mic_rx_key[IWM_TKIP_MIC_SIZE];
u8 mic_tx_key[IWM_TKIP_MIC_SIZE];
} __attribute__ ((packed));
struct iwm_umac_key_ccmp {
struct iwm_umac_wifi_if hdr;
struct iwm_umac_key_hdr key_hdr;
u8 iv_count[6];
u8 reserved[2];
u8 key[WLAN_KEY_LEN_CCMP];
} __attribute__ ((packed));
struct iwm_umac_key_remove {
struct iwm_umac_wifi_if hdr;
struct iwm_umac_key_hdr key_hdr;
} __attribute__ ((packed));
struct iwm_umac_tx_key_id {
struct iwm_umac_wifi_if hdr;
u8 key_idx;
u8 reserved[3];
} __attribute__ ((packed));
struct iwm_umac_cmd_stats_req {
__le32 flags;
} __attribute__ ((packed));
/* LMAC commands */
int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
int iwm_send_prio_table(struct iwm_priv *iwm);
int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
int iwm_send_calib_results(struct iwm_priv *iwm);
int iwm_store_rxiq_calib_result(struct iwm_priv *iwm);
/* UMAC commands */
int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
bool resp);
int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp);
int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value);
int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
void *payload, u16 payload_size);
int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags);
int iwm_send_mlme_profile(struct iwm_priv *iwm);
int iwm_invalidate_mlme_profile(struct iwm_priv *iwm);
int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id);
int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx);
int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
struct iwm_key *key);
int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags);
int iwm_send_umac_channel_list(struct iwm_priv *iwm);
int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
int ssid_num);
int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
/* UDMA commands */
int iwm_target_reset(struct iwm_priv *iwm);
#endif
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 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-1301, USA.
*
*/
#ifndef __IWM_DEBUG_H__
#define __IWM_DEBUG_H__
#define IWM_ERR(p, f, a...) dev_err(iwm_to_dev(p), f, ## a)
#define IWM_WARN(p, f, a...) dev_warn(iwm_to_dev(p), f, ## a)
#define IWM_INFO(p, f, a...) dev_info(iwm_to_dev(p), f, ## a)
#define IWM_CRIT(p, f, a...) dev_crit(iwm_to_dev(p), f, ## a)
#ifdef CONFIG_IWM_DEBUG
#define IWM_DEBUG_MODULE(i, level, module, f, a...) \
do { \
if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
dev_printk(KERN_INFO, (iwm_to_dev(i)), \
"%s " f, __func__ , ## a); \
} while (0)
#define IWM_HEXDUMP(i, level, module, pref, buf, len) \
do { \
if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
print_hex_dump(KERN_INFO, pref, DUMP_PREFIX_OFFSET, \
16, 1, buf, len, 1); \
} while (0)
#else
#define IWM_DEBUG_MODULE(i, level, module, f, a...)
#define IWM_HEXDUMP(i, level, module, pref, buf, len)
#endif /* CONFIG_IWM_DEBUG */
/* Debug modules */
enum iwm_debug_module_id {
IWM_DM_BOOT = 0,
IWM_DM_FW,
IWM_DM_SDIO,
IWM_DM_NTF,
IWM_DM_RX,
IWM_DM_TX,
IWM_DM_MLME,
IWM_DM_CMD,
IWM_DM_WEXT,
__IWM_DM_NR,
};
#define IWM_DM_DEFAULT 0
#define IWM_DBG_BOOT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, BOOT, f, ## a)
#define IWM_DBG_FW(i, l, f, a...) IWM_DEBUG_MODULE(i, l, FW, f, ## a)
#define IWM_DBG_SDIO(i, l, f, a...) IWM_DEBUG_MODULE(i, l, SDIO, f, ## a)
#define IWM_DBG_NTF(i, l, f, a...) IWM_DEBUG_MODULE(i, l, NTF, f, ## a)
#define IWM_DBG_RX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, RX, f, ## a)
#define IWM_DBG_TX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, TX, f, ## a)
#define IWM_DBG_MLME(i, l, f, a...) IWM_DEBUG_MODULE(i, l, MLME, f, ## a)
#define IWM_DBG_CMD(i, l, f, a...) IWM_DEBUG_MODULE(i, l, CMD, f, ## a)
#define IWM_DBG_WEXT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, WEXT, f, ## a)
/* Debug levels */
enum iwm_debug_level {
IWM_DL_NONE = 0,
IWM_DL_ERR,
IWM_DL_WARN,
IWM_DL_INFO,
IWM_DL_DBG,
};
#define IWM_DL_DEFAULT IWM_DL_ERR
struct iwm_debugfs {
struct iwm_priv *iwm;
struct dentry *rootdir;
struct dentry *devdir;
struct dentry *dbgdir;
struct dentry *txdir;
struct dentry *rxdir;
struct dentry *busdir;
u32 dbg_level;
struct dentry *dbg_level_dentry;
unsigned long dbg_modules;
struct dentry *dbg_modules_dentry;
u8 dbg_module[__IWM_DM_NR];
struct dentry *dbg_module_dentries[__IWM_DM_NR];
struct dentry *txq_dentry;
struct dentry *tx_credit_dentry;
struct dentry *rx_ticket_dentry;
};
#ifdef CONFIG_IWM_DEBUG
int iwm_debugfs_init(struct iwm_priv *iwm);
void iwm_debugfs_exit(struct iwm_priv *iwm);
#else
static inline int iwm_debugfs_init(struct iwm_priv *iwm)
{
return 0;
}
static inline void iwm_debugfs_exit(struct iwm_priv *iwm) {}
#endif
#endif
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 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-1301, USA.
*
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include "iwm.h"
#include "bus.h"
#include "rx.h"
#include "debug.h"
static struct {
u8 id;
char *name;
} iwm_debug_module[__IWM_DM_NR] = {
{IWM_DM_BOOT, "boot"},
{IWM_DM_FW, "fw"},
{IWM_DM_SDIO, "sdio"},
{IWM_DM_NTF, "ntf"},
{IWM_DM_RX, "rx"},
{IWM_DM_TX, "tx"},
{IWM_DM_MLME, "mlme"},
{IWM_DM_CMD, "cmd"},
{IWM_DM_WEXT, "wext"},
};
#define add_dbg_module(dbg, name, id, initlevel) \
do { \
struct dentry *d; \
dbg.dbg_module[id] = (initlevel); \
d = debugfs_create_x8(name, 0600, dbg.dbgdir, \
&(dbg.dbg_module[id])); \
if (!IS_ERR(d)) \
dbg.dbg_module_dentries[id] = d; \
} while (0)
static int iwm_debugfs_u32_read(void *data, u64 *val)
{
struct iwm_priv *iwm = data;
*val = iwm->dbg.dbg_level;
return 0;
}
static int iwm_debugfs_dbg_level_write(void *data, u64 val)
{
struct iwm_priv *iwm = data;
int i;
iwm->dbg.dbg_level = val;
for (i = 0; i < __IWM_DM_NR; i++)
iwm->dbg.dbg_module[i] = val;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level,
iwm_debugfs_u32_read, iwm_debugfs_dbg_level_write,
"%llu\n");
static int iwm_debugfs_dbg_modules_write(void *data, u64 val)
{
struct iwm_priv *iwm = data;
int i, bit;
iwm->dbg.dbg_modules = val;
for (i = 0; i < __IWM_DM_NR; i++)
iwm->dbg.dbg_module[i] = 0;
for_each_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR)
iwm->dbg.dbg_module[bit] = iwm->dbg.dbg_level;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
"%llu\n");
static int iwm_txrx_open(struct inode *inode, struct file *filp)
{
filp->private_data = inode->i_private;
return 0;
}
static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct iwm_priv *iwm = filp->private_data;
char *buf;
int i, buf_len = 4096;
size_t len = 0;
ssize_t ret;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
for (i = 0; i < IWM_TX_QUEUES; i++) {
struct iwm_tx_queue *txq = &iwm->txq[i];
struct sk_buff *skb;
int j;
unsigned long flags;
spin_lock_irqsave(&txq->queue.lock, flags);
skb = (struct sk_buff *)&txq->queue;
len += snprintf(buf + len, buf_len - len, "TXQ #%d\n", i);
len += snprintf(buf + len, buf_len - len, "\tStopped: %d\n",
__netif_subqueue_stopped(iwm_to_ndev(iwm),
txq->id));
len += snprintf(buf + len, buf_len - len, "\tConcat count:%d\n",
txq->concat_count);
len += snprintf(buf + len, buf_len - len, "\tQueue len: %d\n",
skb_queue_len(&txq->queue));
for (j = 0; j < skb_queue_len(&txq->queue); j++) {
struct iwm_tx_info *tx_info;
skb = skb->next;
tx_info = skb_to_tx_info(skb);
len += snprintf(buf + len, buf_len - len,
"\tSKB #%d\n", j);
len += snprintf(buf + len, buf_len - len,
"\t\tsta: %d\n", tx_info->sta);
len += snprintf(buf + len, buf_len - len,
"\t\tcolor: %d\n", tx_info->color);
len += snprintf(buf + len, buf_len - len,
"\t\ttid: %d\n", tx_info->tid);
}
spin_unlock_irqrestore(&txq->queue.lock, flags);
}
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
kfree(buf);
return ret;
}
static ssize_t iwm_debugfs_tx_credit_read(struct file *filp,
char __user *buffer,
size_t count, loff_t *ppos)
{
struct iwm_priv *iwm = filp->private_data;
struct iwm_tx_credit *credit = &iwm->tx_credit;
char *buf;
int i, buf_len = 4096;
size_t len = 0;
ssize_t ret;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
len += snprintf(buf + len, buf_len - len,
"NR pools: %d\n", credit->pool_nr);
len += snprintf(buf + len, buf_len - len,
"pools map: 0x%lx\n", credit->full_pools_map);
len += snprintf(buf + len, buf_len - len, "\n### POOLS ###\n");
for (i = 0; i < IWM_MACS_OUT_GROUPS; i++) {
len += snprintf(buf + len, buf_len - len,
"pools entry #%d\n", i);
len += snprintf(buf + len, buf_len - len,
"\tid: %d\n",
credit->pools[i].id);
len += snprintf(buf + len, buf_len - len,
"\tsid: %d\n",
credit->pools[i].sid);
len += snprintf(buf + len, buf_len - len,
"\tmin_pages: %d\n",
credit->pools[i].min_pages);
len += snprintf(buf + len, buf_len - len,
"\tmax_pages: %d\n",
credit->pools[i].max_pages);
len += snprintf(buf + len, buf_len - len,
"\talloc_pages: %d\n",
credit->pools[i].alloc_pages);
len += snprintf(buf + len, buf_len - len,
"\tfreed_pages: %d\n",
credit->pools[i].total_freed_pages);
}
len += snprintf(buf + len, buf_len - len, "\n### SPOOLS ###\n");
for (i = 0; i < IWM_MACS_OUT_SGROUPS; i++) {
len += snprintf(buf + len, buf_len - len,
"spools entry #%d\n", i);
len += snprintf(buf + len, buf_len - len,
"\tid: %d\n",
credit->spools[i].id);
len += snprintf(buf + len, buf_len - len,
"\tmax_pages: %d\n",
credit->spools[i].max_pages);
len += snprintf(buf + len, buf_len - len,
"\talloc_pages: %d\n",
credit->spools[i].alloc_pages);
}
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
kfree(buf);
return ret;
}
static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
char __user *buffer,
size_t count, loff_t *ppos)
{
struct iwm_priv *iwm = filp->private_data;
struct iwm_rx_ticket_node *ticket, *next;
char *buf;
int buf_len = 4096, i;
size_t len = 0;
ssize_t ret;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
len += snprintf(buf + len, buf_len - len, "Ticket #%d\n",
ticket->ticket->id);
len += snprintf(buf + len, buf_len - len, "\taction: 0x%x\n",
ticket->ticket->action);
len += snprintf(buf + len, buf_len - len, "\tflags: 0x%x\n",
ticket->ticket->flags);
}
for (i = 0; i < IWM_RX_ID_HASH; i++) {
struct iwm_rx_packet *packet, *nxt;
struct list_head *pkt_list = &iwm->rx_packets[i];
if (!list_empty(pkt_list)) {
len += snprintf(buf + len, buf_len - len,
"Packet hash #%d\n", i);
list_for_each_entry_safe(packet, nxt, pkt_list, node) {
len += snprintf(buf + len, buf_len - len,
"\tPacket id: %d\n",
packet->id);
len += snprintf(buf + len, buf_len - len,
"\tPacket length: %lu\n",
packet->pkt_size);
}
}
}
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
kfree(buf);
return ret;
}
static const struct file_operations iwm_debugfs_txq_fops = {
.owner = THIS_MODULE,
.open = iwm_txrx_open,
.read = iwm_debugfs_txq_read,
};
static const struct file_operations iwm_debugfs_tx_credit_fops = {
.owner = THIS_MODULE,
.open = iwm_txrx_open,
.read = iwm_debugfs_tx_credit_read,
};
static const struct file_operations iwm_debugfs_rx_ticket_fops = {
.owner = THIS_MODULE,
.open = iwm_txrx_open,
.read = iwm_debugfs_rx_ticket_read,
};
int iwm_debugfs_init(struct iwm_priv *iwm)
{
int i, result;
char devdir[16];
iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
result = PTR_ERR(iwm->dbg.rootdir);
if (!result || IS_ERR(iwm->dbg.rootdir)) {
if (result == -ENODEV) {
IWM_ERR(iwm, "DebugFS (CONFIG_DEBUG_FS) not "
"enabled in kernel config\n");
result = 0; /* No debugfs support */
}
IWM_ERR(iwm, "Couldn't create rootdir: %d\n", result);
goto error;
}
snprintf(devdir, sizeof(devdir), "%s", wiphy_name(iwm_to_wiphy(iwm)));
iwm->dbg.devdir = debugfs_create_dir(devdir, iwm->dbg.rootdir);
result = PTR_ERR(iwm->dbg.devdir);
if (IS_ERR(iwm->dbg.devdir) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create devdir: %d\n", result);
goto error;
}
iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir);
result = PTR_ERR(iwm->dbg.dbgdir);
if (IS_ERR(iwm->dbg.dbgdir) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create dbgdir: %d\n", result);
goto error;
}
iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir);
result = PTR_ERR(iwm->dbg.rxdir);
if (IS_ERR(iwm->dbg.rxdir) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create rx dir: %d\n", result);
goto error;
}
iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir);
result = PTR_ERR(iwm->dbg.txdir);
if (IS_ERR(iwm->dbg.txdir) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create tx dir: %d\n", result);
goto error;
}
iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir);
result = PTR_ERR(iwm->dbg.busdir);
if (IS_ERR(iwm->dbg.busdir) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create bus dir: %d\n", result);
goto error;
}
if (iwm->bus_ops->debugfs_init) {
result = iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir);
if (result < 0) {
IWM_ERR(iwm, "Couldn't create bus entry: %d\n", result);
goto error;
}
}
iwm->dbg.dbg_level = IWM_DL_NONE;
iwm->dbg.dbg_level_dentry =
debugfs_create_file("level", 0200, iwm->dbg.dbgdir, iwm,
&fops_iwm_dbg_level);
result = PTR_ERR(iwm->dbg.dbg_level_dentry);
if (IS_ERR(iwm->dbg.dbg_level_dentry) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create dbg_level: %d\n", result);
goto error;
}
iwm->dbg.dbg_modules = IWM_DM_DEFAULT;
iwm->dbg.dbg_modules_dentry =
debugfs_create_file("modules", 0200, iwm->dbg.dbgdir, iwm,
&fops_iwm_dbg_modules);
result = PTR_ERR(iwm->dbg.dbg_modules_dentry);
if (IS_ERR(iwm->dbg.dbg_modules_dentry) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create dbg_modules: %d\n", result);
goto error;
}
for (i = 0; i < __IWM_DM_NR; i++)
add_dbg_module(iwm->dbg, iwm_debug_module[i].name,
iwm_debug_module[i].id, IWM_DL_DEFAULT);
iwm->dbg.txq_dentry = debugfs_create_file("queues", 0200,
iwm->dbg.txdir, iwm,
&iwm_debugfs_txq_fops);
result = PTR_ERR(iwm->dbg.txq_dentry);
if (IS_ERR(iwm->dbg.txq_dentry) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create tx queue: %d\n", result);
goto error;
}
iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200,
iwm->dbg.txdir, iwm,
&iwm_debugfs_tx_credit_fops);
result = PTR_ERR(iwm->dbg.tx_credit_dentry);
if (IS_ERR(iwm->dbg.tx_credit_dentry) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create tx credit: %d\n", result);
goto error;
}
iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200,
iwm->dbg.rxdir, iwm,
&iwm_debugfs_rx_ticket_fops);
result = PTR_ERR(iwm->dbg.rx_ticket_dentry);
if (IS_ERR(iwm->dbg.rx_ticket_dentry) && (result != -ENODEV)) {
IWM_ERR(iwm, "Couldn't create rx ticket: %d\n", result);
goto error;
}
return 0;
error:
return result;
}
void iwm_debugfs_exit(struct iwm_priv *iwm)
{
int i;
for (i = 0; i < __IWM_DM_NR; i++)
debugfs_remove(iwm->dbg.dbg_module_dentries[i]);
debugfs_remove(iwm->dbg.dbg_modules_dentry);
debugfs_remove(iwm->dbg.dbg_level_dentry);
debugfs_remove(iwm->dbg.txq_dentry);
debugfs_remove(iwm->dbg.tx_credit_dentry);
debugfs_remove(iwm->dbg.rx_ticket_dentry);
if (iwm->bus_ops->debugfs_exit)
iwm->bus_ops->debugfs_exit(iwm);
debugfs_remove(iwm->dbg.busdir);
debugfs_remove(iwm->dbg.dbgdir);
debugfs_remove(iwm->dbg.txdir);
debugfs_remove(iwm->dbg.rxdir);
debugfs_remove(iwm->dbg.devdir);
debugfs_remove(iwm->dbg.rootdir);
}
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#include <linux/kernel.h>
#include "iwm.h"
#include "umac.h"
#include "commands.h"
#include "eeprom.h"
static struct iwm_eeprom_entry eeprom_map[] = {
[IWM_EEPROM_SIG] =
{"Signature", IWM_EEPROM_SIG_OFF, IWM_EEPROM_SIG_LEN},
[IWM_EEPROM_VERSION] =
{"Version", IWM_EEPROM_VERSION_OFF, IWM_EEPROM_VERSION_LEN},
[IWM_EEPROM_OEM_HW_VERSION] =
{"OEM HW version", IWM_EEPROM_OEM_HW_VERSION_OFF,
IWM_EEPROM_OEM_HW_VERSION_LEN},
[IWM_EEPROM_MAC_VERSION] =
{"MAC version", IWM_EEPROM_MAC_VERSION_OFF, IWM_EEPROM_MAC_VERSION_LEN},
[IWM_EEPROM_CARD_ID] =
{"Card ID", IWM_EEPROM_CARD_ID_OFF, IWM_EEPROM_CARD_ID_LEN},
[IWM_EEPROM_RADIO_CONF] =
{"Radio config", IWM_EEPROM_RADIO_CONF_OFF, IWM_EEPROM_RADIO_CONF_LEN},
[IWM_EEPROM_SKU_CAP] =
{"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN},
[IWM_EEPROM_CALIB_RXIQ_OFFSET] =
{"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN},
[IWM_EEPROM_CALIB_RXIQ] =
{"Calib RX IQ", 0, IWM_EEPROM_CALIB_RXIQ_LEN},
};
static int iwm_eeprom_read(struct iwm_priv *iwm, u8 eeprom_id)
{
int ret;
u32 entry_size, chunk_size, data_offset = 0, addr_offset = 0;
u32 addr;
struct iwm_udma_wifi_cmd udma_cmd;
struct iwm_umac_cmd umac_cmd;
struct iwm_umac_cmd_eeprom_proxy eeprom_cmd;
if (eeprom_id > (IWM_EEPROM_LAST - 1))
return -EINVAL;
entry_size = eeprom_map[eeprom_id].length;
if (eeprom_id >= IWM_EEPROM_INDIRECT_DATA) {
/* indirect data */
u32 off_id = eeprom_id - IWM_EEPROM_INDIRECT_DATA +
IWM_EEPROM_INDIRECT_OFFSET;
eeprom_map[eeprom_id].offset =
*(u16 *)(iwm->eeprom + eeprom_map[off_id].offset) << 1;
}
addr = eeprom_map[eeprom_id].offset;
udma_cmd.eop = 1;
udma_cmd.credit_group = 0x4;
udma_cmd.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD;
udma_cmd.lmac_offset = 0;
umac_cmd.id = UMAC_CMD_OPCODE_EEPROM_PROXY;
umac_cmd.resp = 1;
while (entry_size > 0) {
chunk_size = min_t(u32, entry_size, IWM_MAX_EEPROM_DATA_LEN);
eeprom_cmd.hdr.type =
cpu_to_le32(IWM_UMAC_CMD_EEPROM_TYPE_READ);
eeprom_cmd.hdr.offset = cpu_to_le32(addr + addr_offset);
eeprom_cmd.hdr.len = cpu_to_le32(chunk_size);
ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd,
&umac_cmd, &eeprom_cmd,
sizeof(struct iwm_umac_cmd_eeprom_proxy));
if (ret < 0) {
IWM_ERR(iwm, "Couldn't read eeprom\n");
return ret;
}
ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_EEPROM_PROXY,
IWM_SRC_UMAC, 2*HZ);
if (ret < 0) {
IWM_ERR(iwm, "Did not get any eeprom answer\n");
return ret;
}
data_offset += chunk_size;
addr_offset += chunk_size;
entry_size -= chunk_size;
}
return 0;
}
u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id)
{
if (!iwm->eeprom)
return ERR_PTR(-ENODEV);
return iwm->eeprom + eeprom_map[eeprom_id].offset;
}
int iwm_eeprom_init(struct iwm_priv *iwm)
{
int i, ret = 0;
char name[32];
iwm->eeprom = kzalloc(IWM_EEPROM_LEN, GFP_KERNEL);
if (!iwm->eeprom)
return -ENOMEM;
for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
#ifdef CONFIG_IWM_B0_HW_SUPPORT
if (iwm->conf.hw_b0 && (i >= IWM_EEPROM_INDIRECT_OFFSET))
break;
#endif
ret = iwm_eeprom_read(iwm, i);
if (ret < 0) {
IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n",
i, eeprom_map[i].name);
break;
}
}
IWM_DBG_BOOT(iwm, DBG, "EEPROM dump:\n");
for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
memset(name, 0, 32);
sprintf(name, "%s: ", eeprom_map[i].name);
IWM_HEXDUMP(iwm, DBG, BOOT, name,
iwm->eeprom + eeprom_map[i].offset,
eeprom_map[i].length);
}
return ret;
}
void iwm_eeprom_exit(struct iwm_priv *iwm)
{
kfree(iwm->eeprom);
}
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#ifndef __IWM_EEPROM_H__
#define __IWM_EEPROM_H__
enum {
IWM_EEPROM_SIG = 0,
IWM_EEPROM_FIRST = IWM_EEPROM_SIG,
IWM_EEPROM_VERSION,
IWM_EEPROM_OEM_HW_VERSION,
IWM_EEPROM_MAC_VERSION,
IWM_EEPROM_CARD_ID,
IWM_EEPROM_RADIO_CONF,
IWM_EEPROM_SKU_CAP,
IWM_EEPROM_INDIRECT_OFFSET,
IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET,
IWM_EEPROM_INDIRECT_DATA,
IWM_EEPROM_CALIB_RXIQ = IWM_EEPROM_INDIRECT_DATA,
IWM_EEPROM_LAST,
};
#define IWM_EEPROM_SIG_OFF 0x00
#define IWM_EEPROM_VERSION_OFF (0x54 << 1)
#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1)
#define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1)
#define IWM_EEPROM_CARD_ID_OFF (0x5d << 1)
#define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1)
#define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1)
#define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1)
#define IWM_EEPROM_SIG_LEN 4
#define IWM_EEPROM_VERSION_LEN 2
#define IWM_EEPROM_OEM_HW_VERSION_LEN 2
#define IWM_EEPROM_MAC_VERSION_LEN 1
#define IWM_EEPROM_CARD_ID_LEN 2
#define IWM_EEPROM_RADIO_CONF_LEN 2
#define IWM_EEPROM_SKU_CAP_LEN 2
#define IWM_EEPROM_INDIRECT_LEN 2
#define IWM_MAX_EEPROM_DATA_LEN 240
#define IWM_EEPROM_LEN 0x800
#define IWM_EEPROM_MIN_ALLOWED_VERSION 0x0610
#define IWM_EEPROM_MAX_ALLOWED_VERSION 0x0700
#define IWM_EEPROM_CURRENT_VERSION 0x0612
#define IWM_EEPROM_SKU_CAP_BAND_24GHZ (1 << 4)
#define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5)
#define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6)
enum {
IWM_EEPROM_CALIB_CAL_HDR,
IWM_EEPROM_CALIB_TX_POWER,
IWM_EEPROM_CALIB_XTAL,
IWM_EEPROM_CALIB_TEMPERATURE,
IWM_EEPROM_CALIB_RX_BB_FILTER,
IWM_EEPROM_CALIB_RX_IQ,
IWM_EEPROM_CALIB_MAX,
};
#define IWM_EEPROM_CALIB_RXIQ_OFF (IWM_EEPROM_CALIB_CONFIG_OFF + \
(IWM_EEPROM_CALIB_RX_IQ << 1))
#define IWM_EEPROM_CALIB_RXIQ_LEN sizeof(struct iwm_lmac_calib_rxiq)
struct iwm_eeprom_entry {
char *name;
u32 offset;
u32 length;
};
int iwm_eeprom_init(struct iwm_priv *iwm);
void iwm_eeprom_exit(struct iwm_priv *iwm);
u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id);
#endif
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#include <linux/kernel.h>
#include <linux/firmware.h>
#include "iwm.h"
#include "bus.h"
#include "hal.h"
#include "umac.h"
#include "debug.h"
#include "fw.h"
#include "commands.h"
static const char fw_barker[] = "*WESTOPFORNOONE*";
/*
* @op_code: Op code we're looking for.
* @index: There can be several instances of the same opcode within
* the firmware. Index specifies which one we're looking for.
*/
static int iwm_fw_op_offset(struct iwm_priv *iwm, const struct firmware *fw,
u16 op_code, u32 index)
{
int offset = -EINVAL, fw_offset;
u32 op_index = 0;
const u8 *fw_ptr;
struct iwm_fw_hdr_rec *rec;
fw_offset = 0;
fw_ptr = fw->data;
/* We first need to look for the firmware barker */
if (memcmp(fw_ptr, fw_barker, IWM_HDR_BARKER_LEN)) {
IWM_ERR(iwm, "No barker string in this FW\n");
return -EINVAL;
}
if (fw->size < IWM_HDR_LEN) {
IWM_ERR(iwm, "FW is too small (%d)\n", fw->size);
return -EINVAL;
}
fw_offset += IWM_HDR_BARKER_LEN;
while (fw_offset < fw->size) {
rec = (struct iwm_fw_hdr_rec *)(fw_ptr + fw_offset);
IWM_DBG_FW(iwm, DBG, "FW: op_code: 0x%x, len: %d @ 0x%x\n",
rec->op_code, rec->len, fw_offset);
if (rec->op_code == IWM_HDR_REC_OP_INVALID) {
IWM_DBG_FW(iwm, DBG, "Reached INVALID op code\n");
break;
}
if (rec->op_code == op_code) {
if (op_index == index) {
fw_offset += sizeof(struct iwm_fw_hdr_rec);
offset = fw_offset;
goto out;
}
op_index++;
}
fw_offset += sizeof(struct iwm_fw_hdr_rec) + rec->len;
}
out:
return offset;
}
static int iwm_load_firmware_chunk(struct iwm_priv *iwm,
const struct firmware *fw,
struct iwm_fw_img_desc *img_desc)
{
struct iwm_udma_nonwifi_cmd target_cmd;
u32 chunk_size;
const u8 *chunk_ptr;
int ret = 0;
IWM_DBG_FW(iwm, INFO, "Loading FW chunk: %d bytes @ 0x%x\n",
img_desc->length, img_desc->address);
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
target_cmd.handle_by_hw = 1;
target_cmd.op2 = 0;
target_cmd.resp = 0;
target_cmd.eop = 1;
chunk_size = img_desc->length;
chunk_ptr = fw->data + img_desc->offset;
while (chunk_size > 0) {
u32 tmp_chunk_size;
tmp_chunk_size = min_t(u32, chunk_size,
IWM_MAX_NONWIFI_CMD_BUFF_SIZE);
target_cmd.addr = cpu_to_le32(img_desc->address +
(chunk_ptr - fw->data - img_desc->offset));
target_cmd.op1_sz = cpu_to_le32(tmp_chunk_size);
IWM_DBG_FW(iwm, DBG, "\t%d bytes @ 0x%x\n",
tmp_chunk_size, target_cmd.addr);
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, chunk_ptr);
if (ret < 0) {
IWM_ERR(iwm, "Couldn't load FW chunk\n");
break;
}
chunk_size -= tmp_chunk_size;
chunk_ptr += tmp_chunk_size;
}
return ret;
}
/*
* To load a fw image to the target, we basically go through the
* fw, looking for OP_MEM_DESC records. Once we found one, we
* pass it to iwm_load_firmware_chunk().
* The OP_MEM_DESC records contain the actuall memory chunk to be
* sent, but also the destination address.
*/
static int iwm_load_img(struct iwm_priv *iwm, const char *img_name)
{
const struct firmware *fw;
struct iwm_fw_img_desc *img_desc;
struct iwm_fw_img_ver *ver;
int ret = 0, fw_offset;
u32 opcode_idx = 0, build_date;
char *build_tag;
ret = request_firmware(&fw, img_name, iwm_to_dev(iwm));
if (ret) {
IWM_ERR(iwm, "Request firmware failed");
return ret;
}
IWM_DBG_FW(iwm, INFO, "Start to load FW %s\n", img_name);
while (1) {
fw_offset = iwm_fw_op_offset(iwm, fw,
IWM_HDR_REC_OP_MEM_DESC,
opcode_idx);
if (fw_offset < 0)
break;
img_desc = (struct iwm_fw_img_desc *)(fw->data + fw_offset);
ret = iwm_load_firmware_chunk(iwm, fw, img_desc);
if (ret < 0)
goto err_release_fw;
opcode_idx++;
};
/* Read firmware version */
fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_SW_VER, 0);
if (fw_offset < 0)
goto err_release_fw;
ver = (struct iwm_fw_img_ver *)(fw->data + fw_offset);
/* Read build tag */
fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_TAG, 0);
if (fw_offset < 0)
goto err_release_fw;
build_tag = (char *)(fw->data + fw_offset);
/* Read build date */
fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_DATE, 0);
if (fw_offset < 0)
goto err_release_fw;
build_date = *(u32 *)(fw->data + fw_offset);
IWM_INFO(iwm, "%s:\n", img_name);
IWM_INFO(iwm, "\tVersion: %02X.%02X\n", ver->major, ver->minor);
IWM_INFO(iwm, "\tBuild tag: %s\n", build_tag);
IWM_INFO(iwm, "\tBuild date: %x-%x-%x\n",
IWM_BUILD_YEAR(build_date), IWM_BUILD_MONTH(build_date),
IWM_BUILD_DAY(build_date));
err_release_fw:
release_firmware(fw);
return ret;
}
static int iwm_load_umac(struct iwm_priv *iwm)
{
struct iwm_udma_nonwifi_cmd target_cmd;
int ret;
ret = iwm_load_img(iwm, iwm->bus_ops->umac_name);
if (ret < 0)
return ret;
/* We've loaded the UMAC, we can tell the target to jump there */
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_JUMP;
target_cmd.addr = cpu_to_le32(UMAC_MU_FW_INST_DATA_12_ADDR);
target_cmd.op1_sz = 0;
target_cmd.op2 = 0;
target_cmd.handle_by_hw = 0;
target_cmd.resp = 1 ;
target_cmd.eop = 1;
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
if (ret < 0)
IWM_ERR(iwm, "Couldn't send JMP command\n");
return ret;
}
static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name)
{
int ret;
ret = iwm_load_img(iwm, img_name);
if (ret < 0)
return ret;
return iwm_send_umac_reset(iwm,
cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_CLK_EN), 0);
}
/*
* We currently have to load 3 FWs:
* 1) The UMAC (Upper MAC).
* 2) The calibration LMAC (Lower MAC).
* We then send the calibration init command, so that the device can
* run a first calibration round.
* 3) The operational LMAC, which replaces the calibration one when it's
* done with the first calibration round.
*
* Once those 3 FWs have been loaded, we send the periodic calibration
* command, and then the device is available for regular 802.11 operations.
*/
int iwm_load_fw(struct iwm_priv *iwm)
{
int ret;
/* We first start downloading the UMAC */
ret = iwm_load_umac(iwm);
if (ret < 0) {
IWM_ERR(iwm, "UMAC loading failed\n");
return ret;
}
/* Handle UMAC_ALIVE notification */
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_ALIVE, IWM_SRC_UMAC,
WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Handle UMAC_ALIVE failed: %d\n", ret);
return ret;
}
/* UMAC is alive, we can download the calibration LMAC */
ret = iwm_load_lmac(iwm, iwm->bus_ops->calib_lmac_name);
if (ret) {
IWM_ERR(iwm, "Calibration LMAC loading failed\n");
return ret;
}
/* Handle UMAC_INIT_COMPLETE notification */
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Handle INIT_COMPLETE failed for calibration "
"LMAC: %d\n", ret);
return ret;
}
/* Read EEPROM data */
ret = iwm_eeprom_init(iwm);
if (ret < 0) {
IWM_ERR(iwm, "Couldn't init eeprom array\n");
return ret;
}
#ifdef CONFIG_IWM_B0_HW_SUPPORT
if (iwm->conf.hw_b0) {
clear_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map);
clear_bit(PHY_CALIBRATE_RX_IQ_CMD,
&iwm->conf.periodic_calib_map);
}
#endif
/* Read RX IQ calibration result from EEPROM */
if (test_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map)) {
iwm_store_rxiq_calib_result(iwm);
set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map);
}
iwm_send_prio_table(iwm);
iwm_send_init_calib_cfg(iwm, iwm->conf.init_calib_map);
while (iwm->calib_done_map != iwm->conf.init_calib_map) {
ret = iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION,
IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Wait for calibration result timeout\n");
goto out;
}
IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: "
"0x%lx, requested calibrations: 0x%lx\n",
iwm->calib_done_map, iwm->conf.init_calib_map);
}
/* Handle LMAC CALIBRATION_COMPLETE notification */
ret = iwm_notif_handle(iwm, CALIBRATION_COMPLETE_NOTIFICATION,
IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Wait for CALIBRATION_COMPLETE timeout\n");
goto out;
}
IWM_INFO(iwm, "LMAC calibration done: 0x%lx\n", iwm->calib_done_map);
iwm_send_umac_reset(iwm, cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_RESET), 1);
ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
goto out;
}
/* Download the operational LMAC */
ret = iwm_load_lmac(iwm, iwm->bus_ops->lmac_name);
if (ret) {
IWM_ERR(iwm, "LMAC loading failed\n");
goto out;
}
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Handle INIT_COMPLETE failed for LMAC: %d\n", ret);
goto out;
}
iwm_send_prio_table(iwm);
iwm_send_calib_results(iwm);
iwm_send_periodic_calib_cfg(iwm, iwm->conf.periodic_calib_map);
return 0;
out:
iwm_eeprom_exit(iwm);
return ret;
}
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#ifndef __IWM_FW_H__
#define __IWM_FW_H__
/**
* struct iwm_fw_hdr_rec - An iwm firmware image is a
* concatenation of various records. Each of them is
* defined by an ID (aka op code), a length, and the
* actual data.
* @op_code: The record ID, see IWM_HDR_REC_OP_*
*
* @len: The record payload length
*
* @buf: The record payload
*/
struct iwm_fw_hdr_rec {
u16 op_code;
u16 len;
u8 buf[0];
};
/* Header's definitions */
#define IWM_HDR_LEN (512)
#define IWM_HDR_BARKER_LEN (16)
/* Header's opcodes */
#define IWM_HDR_REC_OP_INVALID (0x00)
#define IWM_HDR_REC_OP_BUILD_DATE (0x01)
#define IWM_HDR_REC_OP_BUILD_TAG (0x02)
#define IWM_HDR_REC_OP_SW_VER (0x03)
#define IWM_HDR_REC_OP_HW_SKU (0x04)
#define IWM_HDR_REC_OP_BUILD_OPT (0x05)
#define IWM_HDR_REC_OP_MEM_DESC (0x06)
#define IWM_HDR_REC_USERDEFS (0x07)
/* Header's records length (in bytes) */
#define IWM_HDR_REC_LEN_BUILD_DATE (4)
#define IWM_HDR_REC_LEN_BUILD_TAG (64)
#define IWM_HDR_REC_LEN_SW_VER (4)
#define IWM_HDR_REC_LEN_HW_SKU (4)
#define IWM_HDR_REC_LEN_BUILD_OPT (4)
#define IWM_HDR_REC_LEN_MEM_DESC (12)
#define IWM_HDR_REC_LEN_USERDEF (64)
#define IWM_BUILD_YEAR(date) ((date >> 16) & 0xffff)
#define IWM_BUILD_MONTH(date) ((date >> 8) & 0xff)
#define IWM_BUILD_DAY(date) (date & 0xff)
struct iwm_fw_img_desc {
u32 offset;
u32 address;
u32 length;
};
struct iwm_fw_img_ver {
u8 minor;
u8 major;
u16 reserved;
};
int iwm_load_fw(struct iwm_priv *iwm);
#endif
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
/*
* Hardware Abstraction Layer for iwm.
*
* This file mostly defines an abstraction API for
* sending various commands to the target.
*
* We have 2 types of commands: wifi and non-wifi ones.
*
* - wifi commands:
* They are used for sending LMAC and UMAC commands,
* and thus are the most commonly used ones.
* There are 2 different wifi command types, the regular
* one and the LMAC one. The former is used to send
* UMAC commands (see UMAC_CMD_OPCODE_* from umac.h)
* while the latter is used for sending commands to the
* LMAC. If you look at LMAC commands you'll se that they
* are actually regular iwlwifi target commands encapsulated
* into a special UMAC command called UMAC passthrough.
* This is due to the fact the the host talks exclusively
* to the UMAC and so there needs to be a special UMAC
* command for talking to the LMAC.
* This is how a wifi command is layed out:
* ------------------------
* | iwm_udma_out_wifi_hdr |
* ------------------------
* | SW meta_data (32 bits) |
* ------------------------
* | iwm_dev_cmd_hdr |
* ------------------------
* | payload |
* | .... |
*
* - non-wifi, or general commands:
* Those commands are handled by the device's bootrom,
* and are typically sent when the UMAC and the LMAC
* are not yet available.
* * This is how a non-wifi command is layed out:
* ---------------------------
* | iwm_udma_out_nonwifi_hdr |
* ---------------------------
* | payload |
* | .... |
*
* All the commands start with a UDMA header, which is
* basically a 32 bits field. The 4 LSB there define
* an opcode that allows the target to differentiate
* between wifi (opcode is 0xf) and non-wifi commands
* (opcode is [0..0xe]).
*
* When a command (wifi or non-wifi) is supposed to receive
* an answer, we queue the command buffer. When we do receive
* a command response from the UMAC, we go through the list
* of pending command, and pass both the command and the answer
* to the rx handler. Each command is sent with a unique
* sequence id, and the answer is sent with the same one. This
* is how we're supposed to match an answer with its command.
* See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi()
* for the implementation details.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include "iwm.h"
#include "bus.h"
#include "hal.h"
#include "umac.h"
#include "debug.h"
static void iwm_nonwifi_cmd_init(struct iwm_priv *iwm,
struct iwm_nonwifi_cmd *cmd,
struct iwm_udma_nonwifi_cmd *udma_cmd)
{
INIT_LIST_HEAD(&cmd->pending);
spin_lock(&iwm->cmd_lock);
cmd->resp_received = 0;
cmd->seq_num = iwm->nonwifi_seq_num;
udma_cmd->seq_num = cpu_to_le16(cmd->seq_num);
cmd->seq_num = iwm->nonwifi_seq_num++;
iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX;
if (udma_cmd->resp)
list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd);
spin_unlock(&iwm->cmd_lock);
cmd->buf.start = cmd->buf.payload;
cmd->buf.len = 0;
memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
}
u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm)
{
u16 seq_num = iwm->wifi_seq_num;
iwm->wifi_seq_num++;
iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX;
return seq_num;
}
static void iwm_wifi_cmd_init(struct iwm_priv *iwm,
struct iwm_wifi_cmd *cmd,
struct iwm_udma_wifi_cmd *udma_cmd,
struct iwm_umac_cmd *umac_cmd,
struct iwm_lmac_cmd *lmac_cmd,
u16 payload_size)
{
INIT_LIST_HEAD(&cmd->pending);
spin_lock(&iwm->cmd_lock);
cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm);
umac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
if (umac_cmd->resp)
list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd);
spin_unlock(&iwm->cmd_lock);
cmd->buf.start = cmd->buf.payload;
cmd->buf.len = 0;
if (lmac_cmd) {
cmd->buf.start -= sizeof(struct iwm_lmac_hdr);
lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
lmac_cmd->count = cpu_to_le16(payload_size);
memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd));
umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr));
} else
umac_cmd->count = 0;
umac_cmd->count = cpu_to_le16(payload_size +
le16_to_cpu(umac_cmd->count));
udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) +
le16_to_cpu(umac_cmd->count));
memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd));
}
void iwm_cmd_flush(struct iwm_priv *iwm)
{
struct iwm_wifi_cmd *wcmd, *wnext;
struct iwm_nonwifi_cmd *nwcmd, *nwnext;
list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) {
list_del(&wcmd->pending);
kfree(wcmd);
}
list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd,
pending) {
list_del(&nwcmd->pending);
kfree(nwcmd);
}
}
struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num)
{
struct iwm_wifi_cmd *cmd, *next;
list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending)
if (cmd->seq_num == seq_num) {
list_del(&cmd->pending);
return cmd;
}
return NULL;
}
struct iwm_nonwifi_cmd *
iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode)
{
struct iwm_nonwifi_cmd *cmd, *next;
list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
if ((cmd->seq_num == seq_num) &&
(cmd->udma_cmd.opcode == cmd_opcode) &&
(cmd->resp_received)) {
list_del(&cmd->pending);
return cmd;
}
return NULL;
}
static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm,
struct iwm_udma_out_nonwifi_hdr *hdr,
struct iwm_udma_nonwifi_cmd *cmd)
{
memset(hdr, 0, sizeof(*hdr));
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode);
SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp);
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1);
SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW,
cmd->handle_by_hw);
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM,
le16_to_cpu(cmd->seq_num));
hdr->addr = cmd->addr;
hdr->op1_sz = cmd->op1_sz;
hdr->op2 = cmd->op2;
}
static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm,
struct iwm_nonwifi_cmd *cmd)
{
struct iwm_udma_out_nonwifi_hdr *udma_hdr;
struct iwm_nonwifi_cmd_buff *buf;
struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd;
buf = &cmd->buf;
buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr);
buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr);
udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start);
iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd);
IWM_DBG_CMD(iwm, DBG,
"Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, "
"hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, "
"op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp,
udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr,
udma_cmd->op1_sz, udma_cmd->op2);
return iwm_bus_send_chunk(iwm, buf->start, buf->len);
}
void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop)
{
struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf;
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop);
}
void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
struct iwm_udma_out_wifi_hdr *hdr,
struct iwm_udma_wifi_cmd *cmd)
{
memset(hdr, 0, sizeof(*hdr));
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI);
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop);
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT,
le16_to_cpu(cmd->count));
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group);
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid);
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset);
}
void iwm_build_umac_hdr(struct iwm_priv *iwm,
struct iwm_umac_fw_cmd_hdr *hdr,
struct iwm_umac_cmd *cmd)
{
memset(hdr, 0, sizeof(*hdr));
SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT,
le16_to_cpu(cmd->count));
SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color);
SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp);
hdr->cmd.cmd = cmd->id;
hdr->cmd.seq_num = cmd->seq_num;
}
static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm,
struct iwm_wifi_cmd *cmd)
{
struct iwm_umac_wifi_out_hdr *umac_hdr;
struct iwm_wifi_cmd_buff *buf;
struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd;
struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd;
int ret;
buf = &cmd->buf;
buf->start -= sizeof(struct iwm_umac_wifi_out_hdr);
buf->len += sizeof(struct iwm_umac_wifi_out_hdr);
umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start);
iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd);
iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd);
IWM_DBG_CMD(iwm, DBG,
"Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, "
"eop = 0x%x, count = 0x%x, credit_group = 0x%x, "
"ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n",
UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id,
udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group,
udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num);
if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH)
IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n",
cmd->lmac_cmd.id);
ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len);
/* We keep sending UMAC reset regardless of the command credits.
* The UMAC is supposed to be reset anyway and the Tx credits are
* reinitialized afterwards. If we are lucky, the reset could
* still be done even though we have run out of credits for the
* command pool at this moment.*/
if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) {
IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n",
umac_cmd->id);
return ret;
}
return iwm_bus_send_chunk(iwm, buf->start, buf->len);
}
/* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */
int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
struct iwm_udma_nonwifi_cmd *udma_cmd,
const void *payload)
{
struct iwm_nonwifi_cmd *cmd;
int ret;
cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
if (!cmd) {
IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
return -ENOMEM;
}
iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd);
if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE ||
cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) {
cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz);
memcpy(&cmd->buf.payload, payload, cmd->buf.len);
}
ret = iwm_send_udma_nonwifi_cmd(iwm, cmd);
if (!udma_cmd->resp)
kfree(cmd);
if (ret < 0)
return ret;
return cmd->seq_num;
}
static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
struct iwm_lmac_cmd *cmd)
{
memset(hdr, 0, sizeof(*hdr));
hdr->id = cmd->id;
hdr->flags = 0; /* Is this ever used? */
hdr->seq_num = cmd->seq_num;
}
/*
* iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
* Sending command to the LMAC is equivalent to sending a
* regular UMAC command with the LMAC passtrough or the LMAC
* wrapper UMAC command IDs.
*/
int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
struct iwm_udma_wifi_cmd *udma_cmd,
struct iwm_umac_cmd *umac_cmd,
struct iwm_lmac_cmd *lmac_cmd,
const void *payload, u16 payload_size)
{
struct iwm_wifi_cmd *cmd;
struct iwm_lmac_hdr *hdr;
int lmac_hdr_len = 0;
int ret;
cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL);
if (!cmd) {
IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n");
return -ENOMEM;
}
iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size);
if (lmac_cmd) {
hdr = (struct iwm_lmac_hdr *)(cmd->buf.start);
iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd);
lmac_hdr_len = sizeof(struct iwm_lmac_hdr);
}
memcpy(cmd->buf.payload, payload, payload_size);
cmd->buf.len = le16_to_cpu(umac_cmd->count);
ret = iwm_send_udma_wifi_cmd(iwm, cmd);
/* We free the cmd if we're not expecting any response */
if (!umac_cmd->resp)
kfree(cmd);
return ret;
}
/*
* iwm_hal_send_umac_cmd(): This is a special case for
* iwm_hal_send_host_cmd() to send direct UMAC cmd (without
* LMAC involved).
*/
int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
struct iwm_udma_wifi_cmd *udma_cmd,
struct iwm_umac_cmd *umac_cmd,
const void *payload, u16 payload_size)
{
return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL,
payload, payload_size);
}
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#ifndef _IWM_HAL_H_
#define _IWM_HAL_H_
#include "umac.h"
#define GET_VAL8(s, name) ((s >> name##_POS) & name##_SEED)
#define GET_VAL16(s, name) ((le16_to_cpu(s) >> name##_POS) & name##_SEED)
#define GET_VAL32(s, name) ((le32_to_cpu(s) >> name##_POS) & name##_SEED)
#define SET_VAL8(s, name, val) \
do { \
s = (s & ~(name##_SEED << name##_POS)) | \
((val & name##_SEED) << name##_POS); \
} while (0)
#define SET_VAL16(s, name, val) \
do { \
s = cpu_to_le16((le16_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
((val & name##_SEED) << name##_POS)); \
} while (0)
#define SET_VAL32(s, name, val) \
do { \
s = cpu_to_le32((le32_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
((val & name##_SEED) << name##_POS)); \
} while (0)
#define UDMA_UMAC_INIT { .eop = 1, \
.credit_group = 0x4, \
.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \
.lmac_offset = 0 }
#define UDMA_LMAC_INIT { .eop = 1, \
.credit_group = 0x4, \
.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \
.lmac_offset = 4 }
/* UDMA IN OP CODE -- cmd bits [3:0] */
#define UDMA_IN_OPCODE_MASK 0xF
#define UDMA_IN_OPCODE_GENERAL_RESP 0x0
#define UDMA_IN_OPCODE_READ_RESP 0x1
#define UDMA_IN_OPCODE_WRITE_RESP 0x2
#define UDMA_IN_OPCODE_PERS_WRITE_RESP 0x5
#define UDMA_IN_OPCODE_PERS_READ_RESP 0x6
#define UDMA_IN_OPCODE_RD_MDFY_WR_RESP 0x7
#define UDMA_IN_OPCODE_EP_MNGMT_MSG 0x8
#define UDMA_IN_OPCODE_CRDT_CHNG_MSG 0x9
#define UDMA_IN_OPCODE_CNTRL_DATABASE_MSG 0xA
#define UDMA_IN_OPCODE_SW_MSG 0xB
#define UDMA_IN_OPCODE_WIFI 0xF
#define UDMA_IN_OPCODE_WIFI_LMAC 0x1F
#define UDMA_IN_OPCODE_WIFI_UMAC 0x2F
/* HW API: udma_hdi_nonwifi API (OUT and IN) */
/* iwm_udma_nonwifi_cmd request response -- bits [9:9] */
#define UDMA_HDI_OUT_NW_CMD_RESP_POS 9
#define UDMA_HDI_OUT_NW_CMD_RESP_SEED 0x1
/* iwm_udma_nonwifi_cmd handle by HW -- bits [11:11] */
#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_POS 11
#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_SEED 0x1
/* iwm_udma_nonwifi_cmd sequence-number -- bits [12:15] */
#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_POS 12
#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_SEED 0xF
/* UDMA IN Non-WIFI HW sequence number -- bits [12:15] */
#define UDMA_IN_NW_HW_SEQ_NUM_POS 12
#define UDMA_IN_NW_HW_SEQ_NUM_SEED 0xF
/* UDMA IN Non-WIFI HW signature -- bits [16:31] */
#define UDMA_IN_NW_HW_SIG_POS 16
#define UDMA_IN_NW_HW_SIG_SEED 0xFFFF
/* fixed signature */
#define UDMA_IN_NW_HW_SIG 0xCBBC
/* UDMA IN Non-WIFI HW block length -- bits [32:35] */
#define UDMA_IN_NW_HW_LENGTH_SEED 0xF
#define UDMA_IN_NW_HW_LENGTH_POS 32
/* End of HW API: udma_hdi_nonwifi API (OUT and IN) */
#define IWM_SDIO_FW_MAX_CHUNK_SIZE 2032
#define IWM_MAX_WIFI_HEADERS_SIZE 32
#define IWM_MAX_NONWIFI_HEADERS_SIZE 16
#define IWM_MAX_NONWIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \
IWM_MAX_NONWIFI_HEADERS_SIZE)
#define IWM_MAX_WIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \
IWM_MAX_WIFI_HEADERS_SIZE)
#define IWM_HAL_CONCATENATE_BUF_SIZE 8192
struct iwm_wifi_cmd_buff {
u16 len;
u8 *start;
u8 hdr[IWM_MAX_WIFI_HEADERS_SIZE];
u8 payload[IWM_MAX_WIFI_CMD_BUFF_SIZE];
};
struct iwm_nonwifi_cmd_buff {
u16 len;
u8 *start;
u8 hdr[IWM_MAX_NONWIFI_HEADERS_SIZE];
u8 payload[IWM_MAX_NONWIFI_CMD_BUFF_SIZE];
};
struct iwm_udma_nonwifi_cmd {
u8 opcode;
u8 eop;
u8 resp;
u8 handle_by_hw;
__le32 addr;
__le32 op1_sz;
__le32 op2;
__le16 seq_num;
};
struct iwm_udma_wifi_cmd {
__le16 count;
u8 eop;
u8 credit_group;
u8 ra_tid;
u8 lmac_offset;
};
struct iwm_umac_cmd {
u8 id;
__le16 count;
u8 resp;
__le16 seq_num;
u8 color;
};
struct iwm_lmac_cmd {
u8 id;
__le16 count;
u8 resp;
__le16 seq_num;
};
struct iwm_nonwifi_cmd {
u16 seq_num;
bool resp_received;
struct list_head pending;
struct iwm_udma_nonwifi_cmd udma_cmd;
struct iwm_umac_cmd umac_cmd;
struct iwm_lmac_cmd lmac_cmd;
struct iwm_nonwifi_cmd_buff buf;
u32 flags;
};
struct iwm_wifi_cmd {
u16 seq_num;
struct list_head pending;
struct iwm_udma_wifi_cmd udma_cmd;
struct iwm_umac_cmd umac_cmd;
struct iwm_lmac_cmd lmac_cmd;
struct iwm_wifi_cmd_buff buf;
u32 flags;
};
void iwm_cmd_flush(struct iwm_priv *iwm);
struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm,
u16 seq_num);
struct iwm_nonwifi_cmd *iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm,
u8 seq_num, u8 cmd_opcode);
int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
struct iwm_udma_nonwifi_cmd *ucmd,
const void *payload);
int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
struct iwm_udma_wifi_cmd *udma_cmd,
struct iwm_umac_cmd *umac_cmd,
struct iwm_lmac_cmd *lmac_cmd,
const void *payload, u16 payload_size);
int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
struct iwm_udma_wifi_cmd *udma_cmd,
struct iwm_umac_cmd *umac_cmd,
const void *payload, u16 payload_size);
u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm);
void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop);
void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
struct iwm_udma_out_wifi_hdr *hdr,
struct iwm_udma_wifi_cmd *cmd);
void iwm_build_umac_hdr(struct iwm_priv *iwm,
struct iwm_umac_fw_cmd_hdr *hdr,
struct iwm_umac_cmd *cmd);
#endif /* _IWM_HAL_H_ */
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#ifndef __IWM_H__
#define __IWM_H__
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include "debug.h"
#include "hal.h"
#include "umac.h"
#include "lmac.h"
#include "eeprom.h"
#define IWM_COPYRIGHT "Copyright(c) 2009 Intel Corporation"
#define IWM_AUTHOR "<ilw@linux.intel.com>"
#define CONFIG_IWM_B0_HW_SUPPORT 1
#define IWM_SRC_LMAC UMAC_HDI_IN_SOURCE_FHRX
#define IWM_SRC_UDMA UMAC_HDI_IN_SOURCE_UDMA
#define IWM_SRC_UMAC UMAC_HDI_IN_SOURCE_FW
#define IWM_SRC_NUM 3
#define IWM_POWER_INDEX_MIN 0
#define IWM_POWER_INDEX_MAX 5
#define IWM_POWER_INDEX_DEFAULT 3
struct iwm_conf {
u32 sdio_ior_timeout;
unsigned long init_calib_map;
unsigned long periodic_calib_map;
bool reset_on_fatal_err;
bool auto_connect;
bool wimax_not_present;
bool enable_qos;
u32 mode;
u32 power_index;
u32 frag_threshold;
u32 rts_threshold;
bool cts_to_self;
u32 assoc_timeout;
u32 roam_timeout;
u32 wireless_mode;
u32 coexist_mode;
u8 ibss_band;
u8 ibss_channel;
u8 mac_addr[ETH_ALEN];
#ifdef CONFIG_IWM_B0_HW_SUPPORT
bool hw_b0;
#endif
};
enum {
COEX_MODE_SA = 1,
COEX_MODE_XOR,
COEX_MODE_CM,
COEX_MODE_MAX,
};
struct iwm_if_ops;
struct iwm_wifi_cmd;
struct pool_entry {
int id; /* group id */
int sid; /* super group id */
int min_pages; /* min capacity in pages */
int max_pages; /* max capacity in pages */
int alloc_pages; /* allocated # of pages. incresed by driver */
int total_freed_pages; /* total freed # of pages. incresed by UMAC */
};
struct spool_entry {
int id;
int max_pages;
int alloc_pages;
};
struct iwm_tx_credit {
spinlock_t lock;
int pool_nr;
unsigned long full_pools_map; /* bitmap for # of filled tx pools */
struct pool_entry pools[IWM_MACS_OUT_GROUPS];
struct spool_entry spools[IWM_MACS_OUT_SGROUPS];
};
struct iwm_notif {
struct list_head pending;
u32 cmd_id;
void *cmd;
u8 src;
void *buf;
unsigned long buf_size;
};
struct iwm_sta_info {
u8 addr[ETH_ALEN];
bool valid;
bool qos;
u8 color;
};
struct iwm_tx_info {
u8 sta;
u8 color;
u8 tid;
};
struct iwm_rx_info {
unsigned long rx_size;
unsigned long rx_buf_size;
};
#define IWM_NUM_KEYS 4
struct iwm_umac_key_hdr {
u8 mac[ETH_ALEN];
u8 key_idx;
u8 multicast; /* BCast encrypt & BCast decrypt of frames FROM mac */
} __attribute__ ((packed));
struct iwm_key {
struct iwm_umac_key_hdr hdr;
u8 in_use;
u8 alg;
u32 flags;
u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE];
u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE];
u8 key_len;
u8 key[32];
};
#define IWM_RX_ID_HASH 0xff
#define IWM_RX_ID_GET_HASH(id) ((id) % IWM_RX_ID_HASH)
#define IWM_STA_TABLE_NUM 16
#define IWM_TX_LIST_SIZE 64
#define IWM_RX_LIST_SIZE 256
#define IWM_SCAN_ID_MAX 0xff
#define IWM_STATUS_READY 0
#define IWM_STATUS_SCANNING 1
#define IWM_STATUS_SCAN_ABORTING 2
#define IWM_STATUS_ASSOCIATING 3
#define IWM_STATUS_ASSOCIATED 4
#define IWM_RADIO_RFKILL_OFF 0
#define IWM_RADIO_RFKILL_HW 1
#define IWM_RADIO_RFKILL_SW 2
struct iwm_tx_queue {
int id;
struct sk_buff_head queue;
struct workqueue_struct *wq;
struct work_struct worker;
u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE];
int concat_count;
u8 *concat_ptr;
};
/* Queues 0 ~ 3 for AC data, 5 for iPAN */
#define IWM_TX_QUEUES 5
#define IWM_TX_DATA_QUEUES 4
#define IWM_TX_CMD_QUEUE 4
struct iwm_bss_info {
struct list_head node;
struct cfg80211_bss *cfg_bss;
struct iwm_umac_notif_bss_info *bss;
};
typedef int (*iwm_handler)(struct iwm_priv *priv, u8 *buf,
unsigned long buf_size, struct iwm_wifi_cmd *cmd);
#define IWM_WATCHDOG_PERIOD (6 * HZ)
struct iwm_priv {
struct wireless_dev *wdev;
struct iwm_if_ops *bus_ops;
struct iwm_conf conf;
unsigned long status;
unsigned long radio;
struct list_head pending_notif;
wait_queue_head_t notif_queue;
wait_queue_head_t nonwifi_queue;
unsigned long calib_done_map;
struct {
u8 *buf;
u32 size;
} calib_res[CALIBRATION_CMD_NUM];
struct iwm_umac_profile *umac_profile;
bool umac_profile_active;
u8 bssid[ETH_ALEN];
u8 channel;
u16 rate;
struct iwm_sta_info sta_table[IWM_STA_TABLE_NUM];
struct list_head bss_list;
void (*nonwifi_rx_handlers[UMAC_HDI_IN_OPCODE_NONWIFI_MAX])
(struct iwm_priv *priv, u8 *buf, unsigned long buf_size);
const iwm_handler *umac_handlers;
const iwm_handler *lmac_handlers;
DECLARE_BITMAP(lmac_handler_map, LMAC_COMMAND_ID_NUM);
DECLARE_BITMAP(umac_handler_map, LMAC_COMMAND_ID_NUM);
DECLARE_BITMAP(udma_handler_map, LMAC_COMMAND_ID_NUM);
struct list_head wifi_pending_cmd;
struct list_head nonwifi_pending_cmd;
u16 wifi_seq_num;
u8 nonwifi_seq_num;
spinlock_t cmd_lock;
u32 core_enabled;
u8 scan_id;
struct cfg80211_scan_request *scan_request;
struct sk_buff_head rx_list;
struct list_head rx_tickets;
struct list_head rx_packets[IWM_RX_ID_HASH];
struct workqueue_struct *rx_wq;
struct work_struct rx_worker;
struct iwm_tx_credit tx_credit;
struct iwm_tx_queue txq[IWM_TX_QUEUES];
struct iwm_key keys[IWM_NUM_KEYS];
struct iwm_key *default_key;
wait_queue_head_t mlme_queue;
struct iw_statistics wstats;
struct delayed_work stats_request;
struct iwm_debugfs dbg;
u8 *eeprom;
struct timer_list watchdog;
struct work_struct reset_worker;
struct rfkill *rfkill;
char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
};
static inline void *iwm_private(struct iwm_priv *iwm)
{
BUG_ON(!iwm);
return &iwm->private;
}
#define hw_to_iwm(h) (h->iwm)
#define iwm_to_dev(i) (wiphy_dev(i->wdev->wiphy))
#define iwm_to_wiphy(i) (i->wdev->wiphy)
#define wiphy_to_iwm(w) (struct iwm_priv *)(wiphy_priv(w))
#define iwm_to_wdev(i) (i->wdev)
#define wdev_to_iwm(w) (struct iwm_priv *)(wdev_priv(w))
#define iwm_to_ndev(i) (i->wdev->netdev)
#define ndev_to_iwm(n) (wdev_to_iwm(n->ieee80211_ptr))
#define skb_to_rx_info(s) ((struct iwm_rx_info *)(s->cb))
#define skb_to_tx_info(s) ((struct iwm_tx_info *)s->cb)
extern const struct iw_handler_def iwm_iw_handler_def;
void *iwm_if_alloc(int sizeof_bus, struct device *dev,
struct iwm_if_ops *if_ops);
void iwm_if_free(struct iwm_priv *iwm);
int iwm_mode_to_nl80211_iftype(int mode);
int iwm_priv_init(struct iwm_priv *iwm);
void iwm_reset(struct iwm_priv *iwm);
void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
struct iwm_umac_notif_alive *alive);
int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb);
int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size);
int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout);
void iwm_init_default_profile(struct iwm_priv *iwm,
struct iwm_umac_profile *profile);
void iwm_link_on(struct iwm_priv *iwm);
void iwm_link_off(struct iwm_priv *iwm);
int iwm_up(struct iwm_priv *iwm);
int iwm_down(struct iwm_priv *iwm);
/* TX API */
void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages);
void iwm_tx_worker(struct work_struct *work);
int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
/* RX API */
void iwm_rx_setup_handlers(struct iwm_priv *iwm);
int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size);
int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size,
struct iwm_wifi_cmd *cmd);
void iwm_rx_free(struct iwm_priv *iwm);
/* RF Kill API */
int iwm_rfkill_init(struct iwm_priv *iwm);
void iwm_rfkill_exit(struct iwm_priv *iwm);
#endif
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#ifndef __IWM_LMAC_H__
#define __IWM_LMAC_H__
struct iwm_lmac_hdr {
u8 id;
u8 flags;
__le16 seq_num;
} __attribute__ ((packed));
/* LMAC commands */
#define CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK 0x1
struct iwm_lmac_cal_cfg_elt {
__le32 enable; /* 1 means LMAC needs to do something */
__le32 start; /* 1 to start calibration, 0 to stop */
__le32 send_res; /* 1 for sending back results */
__le32 apply_res; /* 1 for applying calibration results to HW */
__le32 reserved;
} __attribute__ ((packed));
struct iwm_lmac_cal_cfg_status {
struct iwm_lmac_cal_cfg_elt init;
struct iwm_lmac_cal_cfg_elt periodic;
__le32 flags; /* CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK */
} __attribute__ ((packed));
struct iwm_lmac_cal_cfg_cmd {
struct iwm_lmac_cal_cfg_status ucode_cfg;
struct iwm_lmac_cal_cfg_status driver_cfg;
__le32 reserved;
} __attribute__ ((packed));
struct iwm_lmac_cal_cfg_resp {
__le32 status;
} __attribute__ ((packed));
#define IWM_CARD_STATE_SW_HW_ENABLED 0x00
#define IWM_CARD_STATE_HW_DISABLED 0x01
#define IWM_CARD_STATE_SW_DISABLED 0x02
#define IWM_CARD_STATE_CTKILL_DISABLED 0x04
#define IWM_CARD_STATE_IS_RXON 0x10
struct iwm_lmac_card_state {
__le32 flags;
} __attribute__ ((packed));
/**
* COEX_PRIORITY_TABLE_CMD
*
* Priority entry for each state
* Will keep two tables, for STA and WIPAN
*/
enum {
/* UN-ASSOCIATION PART */
COEX_UNASSOC_IDLE = 0,
COEX_UNASSOC_MANUAL_SCAN,
COEX_UNASSOC_AUTO_SCAN,
/* CALIBRATION */
COEX_CALIBRATION,
COEX_PERIODIC_CALIBRATION,
/* CONNECTION */
COEX_CONNECTION_ESTAB,
/* ASSOCIATION PART */
COEX_ASSOCIATED_IDLE,
COEX_ASSOC_MANUAL_SCAN,
COEX_ASSOC_AUTO_SCAN,
COEX_ASSOC_ACTIVE_LEVEL,
/* RF ON/OFF */
COEX_RF_ON,
COEX_RF_OFF,
COEX_STAND_ALONE_DEBUG,
/* IPNN */
COEX_IPAN_ASSOC_LEVEL,
/* RESERVED */
COEX_RSRVD1,
COEX_RSRVD2,
COEX_EVENTS_NUM
};
#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK 0x1
#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK 0x2
#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK 0x4
struct coex_event {
u8 req_prio;
u8 win_med_prio;
u8 reserved;
u8 flags;
} __attribute__ ((packed));
#define COEX_FLAGS_STA_TABLE_VALID_MSK 0x1
#define COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK 0x4
#define COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK 0x8
#define COEX_FLAGS_COEX_ENABLE_MSK 0x80
struct iwm_coex_prio_table_cmd {
u8 flags;
u8 reserved[3];
struct coex_event sta_prio[COEX_EVENTS_NUM];
} __attribute__ ((packed));
/* Coexistence definitions
*
* Constants to fill in the Priorities' Tables
* RP - Requested Priority
* WP - Win Medium Priority: priority assigned when the contention has been won
* FLAGS - Combination of COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK and
* COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK
*/
#define COEX_UNASSOC_IDLE_FLAGS 0
#define COEX_UNASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
#define COEX_UNASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
#define COEX_CALIBRATION_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
#define COEX_PERIODIC_CALIBRATION_FLAGS 0
/* COEX_CONNECTION_ESTAB: we need DELAY_MEDIUM_FREE_NTFY to let WiMAX
* disconnect from network. */
#define COEX_CONNECTION_ESTAB_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
#define COEX_ASSOCIATED_IDLE_FLAGS 0
#define COEX_ASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
#define COEX_ASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0
#define COEX_RF_ON_FLAGS 0
#define COEX_RF_OFF_FLAGS 0
#define COEX_STAND_ALONE_DEBUG_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
#define COEX_IPAN_ASSOC_LEVEL_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
#define COEX_RSRVD1_FLAGS 0
#define COEX_RSRVD2_FLAGS 0
/* XOR_RF_ON is the event wrapping all radio ownership. We need
* DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */
#define COEX_XOR_RF_ON_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
/* LMAC OP CODES */
#define REPLY_PAD 0x0
#define REPLY_ALIVE 0x1
#define REPLY_ERROR 0x2
#define REPLY_ECHO 0x3
#define REPLY_HALT 0x6
/* RXON state commands */
#define REPLY_RX_ON 0x10
#define REPLY_RX_ON_ASSOC 0x11
#define REPLY_RX_OFF 0x12
#define REPLY_QOS_PARAM 0x13
#define REPLY_RX_ON_TIMING 0x14
#define REPLY_INTERNAL_QOS_PARAM 0x15
#define REPLY_RX_INT_TIMEOUT_CNFG 0x16
#define REPLY_NULL 0x17
/* Multi-Station support */
#define REPLY_ADD_STA 0x18
#define REPLY_REMOVE_STA 0x19
#define REPLY_RESET_ALL_STA 0x1a
/* RX, TX */
#define REPLY_ALM_RX 0x1b
#define REPLY_TX 0x1c
#define REPLY_TXFIFO_FLUSH 0x1e
/* MISC commands */
#define REPLY_MGMT_MCAST_KEY 0x1f
#define REPLY_WEPKEY 0x20
#define REPLY_INIT_IV 0x21
#define REPLY_WRITE_MIB 0x22
#define REPLY_READ_MIB 0x23
#define REPLY_RADIO_FE 0x24
#define REPLY_TXFIFO_CFG 0x25
#define REPLY_WRITE_READ 0x26
#define REPLY_INSTALL_SEC_KEY 0x27
#define REPLY_RATE_SCALE 0x47
#define REPLY_LEDS_CMD 0x48
#define REPLY_TX_LINK_QUALITY_CMD 0x4e
#define REPLY_ANA_MIB_OVERRIDE_CMD 0x4f
#define REPLY_WRITE2REG_CMD 0x50
/* winfi-wifi coexistence */
#define COEX_PRIORITY_TABLE_CMD 0x5a
#define COEX_MEDIUM_NOTIFICATION 0x5b
#define COEX_EVENT_CMD 0x5c
/* more Protocol and Protocol-test commands */
#define REPLY_MAX_SLEEP_TIME_CMD 0x61
#define CALIBRATION_CFG_CMD 0x65
#define CALIBRATION_RES_NOTIFICATION 0x66
#define CALIBRATION_COMPLETE_NOTIFICATION 0x67
/* Measurements */
#define REPLY_QUIET_CMD 0x71
#define REPLY_CHANNEL_SWITCH 0x72
#define CHANNEL_SWITCH_NOTIFICATION 0x73
#define REPLY_SPECTRUM_MEASUREMENT_CMD 0x74
#define SPECTRUM_MEASURE_NOTIFICATION 0x75
#define REPLY_MEASUREMENT_ABORT_CMD 0x76
/* Power Management */
#define POWER_TABLE_CMD 0x77
#define SAVE_RESTORE_ADRESS_CMD 0x78
#define REPLY_WATERMARK_CMD 0x79
#define PM_DEBUG_STATISTIC_NOTIFIC 0x7B
#define PD_FLUSH_N_NOTIFICATION 0x7C
/* Scan commands and notifications */
#define REPLY_SCAN_REQUEST_CMD 0x80
#define REPLY_SCAN_ABORT_CMD 0x81
#define SCAN_START_NOTIFICATION 0x82
#define SCAN_RESULTS_NOTIFICATION 0x83
#define SCAN_COMPLETE_NOTIFICATION 0x84
/* Continuous TX commands */
#define REPLY_CONT_TX_CMD 0x85
#define END_OF_CONT_TX_NOTIFICATION 0x86
/* Timer/Eeprom commands */
#define TIMER_CMD 0x87
#define EEPROM_WRITE_CMD 0x88
/* PAPD commands */
#define FEEDBACK_REQUEST_NOTIFICATION 0x8b
#define REPLY_CW_CMD 0x8c
/* IBSS/AP commands Continue */
#define BEACON_NOTIFICATION 0x90
#define REPLY_TX_BEACON 0x91
#define REPLY_REQUEST_ATIM 0x93
#define WHO_IS_AWAKE_NOTIFICATION 0x94
#define TX_PWR_DBM_LIMIT_CMD 0x95
#define QUIET_NOTIFICATION 0x96
#define TX_PWR_TABLE_CMD 0x97
#define TX_ANT_CONFIGURATION_CMD 0x98
#define MEASURE_ABORT_NOTIFICATION 0x99
#define REPLY_CALIBRATION_TUNE 0x9a
/* bt config command */
#define REPLY_BT_CONFIG 0x9b
#define REPLY_STATISTICS_CMD 0x9c
#define STATISTICS_NOTIFICATION 0x9d
/* RF-KILL commands and notifications */
#define REPLY_CARD_STATE_CMD 0xa0
#define CARD_STATE_NOTIFICATION 0xa1
/* Missed beacons notification */
#define MISSED_BEACONS_NOTIFICATION 0xa2
#define MISSED_BEACONS_NOTIFICATION_TH_CMD 0xa3
#define REPLY_CT_KILL_CONFIG_CMD 0xa4
/* HD commands and notifications */
#define REPLY_HD_PARAMS_CMD 0xa6
#define HD_PARAMS_NOTIFICATION 0xa7
#define SENSITIVITY_CMD 0xa8
#define U_APSD_PARAMS_CMD 0xa9
#define NOISY_PLATFORM_CMD 0xaa
#define ILLEGAL_CMD 0xac
#define REPLY_PHY_CALIBRATION_CMD 0xb0
#define REPLAY_RX_GAIN_CALIB_CMD 0xb1
/* WiPAN commands */
#define REPLY_WIPAN_PARAMS_CMD 0xb2
#define REPLY_WIPAN_RX_ON_CMD 0xb3
#define REPLY_WIPAN_RX_ON_TIMING 0xb4
#define REPLY_WIPAN_TX_PWR_TABLE_CMD 0xb5
#define REPLY_WIPAN_RXON_ASSOC_CMD 0xb6
#define REPLY_WIPAN_QOS_PARAM 0xb7
#define WIPAN_REPLY_WEPKEY 0xb8
/* BeamForming commands */
#define BEAMFORMER_CFG_CMD 0xba
#define BEAMFORMEE_NOTIFICATION 0xbb
/* TGn new Commands */
#define REPLY_RX_PHY_CMD 0xc0
#define REPLY_RX_MPDU_CMD 0xc1
#define REPLY_MULTICAST_HASH 0xc2
#define REPLY_KDR_RX 0xc3
#define REPLY_RX_DSP_EXT_INFO 0xc4
#define REPLY_COMPRESSED_BA 0xc5
/* PNC commands */
#define PNC_CONFIG_CMD 0xc8
#define PNC_UPDATE_TABLE_CMD 0xc9
#define XVT_GENERAL_CTRL_CMD 0xca
#define REPLY_LEGACY_RADIO_FE 0xdd
/* WoWLAN commands */
#define WOWLAN_PATTERNS 0xe0
#define WOWLAN_WAKEUP_FILTER 0xe1
#define WOWLAN_TSC_RSC_PARAM 0xe2
#define WOWLAN_TKIP_PARAM 0xe3
#define WOWLAN_KEK_KCK_MATERIAL 0xe4
#define WOWLAN_GET_STATUSES 0xe5
#define WOWLAN_TX_POWER_PER_DB 0xe6
#define REPLY_WOWLAN_GET_STATUSES WOWLAN_GET_STATUSES
#define REPLY_DEBUG_CMD 0xf0
#define REPLY_DSP_DEBUG_CMD 0xf1
#define REPLY_DEBUG_MONITOR_CMD 0xf2
#define REPLY_DEBUG_XVT_CMD 0xf3
#define REPLY_DEBUG_DC_CALIB 0xf4
#define REPLY_DYNAMIC_BP 0xf5
/* General purpose Commands */
#define REPLY_GP1_CMD 0xfa
#define REPLY_GP2_CMD 0xfb
#define REPLY_GP3_CMD 0xfc
#define REPLY_GP4_CMD 0xfd
#define REPLY_REPLAY_WRAPPER 0xfe
#define REPLY_FRAME_DURATION_CALC_CMD 0xff
#define LMAC_COMMAND_ID_MAX 0xff
#define LMAC_COMMAND_ID_NUM (LMAC_COMMAND_ID_MAX + 1)
/* Calibration */
enum {
PHY_CALIBRATE_DC_CMD = 0,
PHY_CALIBRATE_LO_CMD = 1,
PHY_CALIBRATE_RX_BB_CMD = 2,
PHY_CALIBRATE_TX_IQ_CMD = 3,
PHY_CALIBRATE_RX_IQ_CMD = 4,
PHY_CALIBRATION_NOISE_CMD = 5,
PHY_CALIBRATE_AGC_TABLE_CMD = 6,
PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 7,
PHY_CALIBRATE_OPCODES_NUM,
SHILOH_PHY_CALIBRATE_DC_CMD = 8,
SHILOH_PHY_CALIBRATE_LO_CMD = 9,
SHILOH_PHY_CALIBRATE_RX_BB_CMD = 10,
SHILOH_PHY_CALIBRATE_TX_IQ_CMD = 11,
SHILOH_PHY_CALIBRATE_RX_IQ_CMD = 12,
SHILOH_PHY_CALIBRATION_NOISE_CMD = 13,
SHILOH_PHY_CALIBRATE_AGC_TABLE_CMD = 14,
SHILOH_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15,
SHILOH_PHY_CALIBRATE_BASE_BAND_CMD = 16,
SHILOH_PHY_CALIBRATE_TXIQ_PERIODIC_CMD = 17,
CALIBRATION_CMD_NUM,
};
struct iwm_lmac_calib_hdr {
u8 opcode;
u8 first_grp;
u8 grp_num;
u8 all_data_valid;
} __attribute__ ((packed));
#define IWM_LMAC_CALIB_FREQ_GROUPS_NR 7
#define IWM_CALIB_FREQ_GROUPS_NR 5
#define IWM_CALIB_DC_MODES_NR 12
struct iwm_calib_rxiq_entry {
u16 ptam_postdist_ars;
u16 ptam_postdist_arc;
} __attribute__ ((packed));
struct iwm_calib_rxiq_group {
struct iwm_calib_rxiq_entry mode[IWM_CALIB_DC_MODES_NR];
} __attribute__ ((packed));
struct iwm_lmac_calib_rxiq {
struct iwm_calib_rxiq_group group[IWM_LMAC_CALIB_FREQ_GROUPS_NR];
} __attribute__ ((packed));
struct iwm_calib_rxiq {
struct iwm_lmac_calib_hdr hdr;
struct iwm_calib_rxiq_group group[IWM_CALIB_FREQ_GROUPS_NR];
} __attribute__ ((packed));
#define LMAC_STA_ID_SEED 0x0f
#define LMAC_STA_ID_POS 0
#define LMAC_STA_COLOR_SEED 0x7
#define LMAC_STA_COLOR_POS 4
struct iwm_lmac_power_report {
u8 pa_status;
u8 pa_integ_res_A[3];
u8 pa_integ_res_B[3];
u8 pa_integ_res_C[3];
} __attribute__ ((packed));
struct iwm_lmac_tx_resp {
u8 frame_cnt; /* 1-no aggregation, greater then 1 - aggregation */
u8 bt_kill_cnt;
__le16 retry_cnt;
__le32 initial_tx_rate;
__le16 wireless_media_time;
struct iwm_lmac_power_report power_report;
__le32 tfd_info;
__le16 seq_ctl;
__le16 byte_cnt;
u8 tlc_rate_info;
u8 ra_tid;
__le16 frame_ctl;
__le32 status;
} __attribute__ ((packed));
#endif
此差异已折叠。
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 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-1301, USA.
*
*/
/*
* This is the netdev related hooks for iwm.
*
* Some interesting code paths:
*
* iwm_open() (Called at netdev interface bringup time)
* -> iwm_up() (main.c)
* -> iwm_bus_enable()
* -> if_sdio_enable() (In case of an SDIO bus)
* -> sdio_enable_func()
* -> iwm_notif_wait(BARKER_REBOOT) (wait for reboot barker)
* -> iwm_notif_wait(ACK_BARKER) (wait for ACK barker)
* -> iwm_load_fw() (fw.c)
* -> iwm_load_umac()
* -> iwm_load_lmac() (Calibration LMAC)
* -> iwm_load_lmac() (Operational LMAC)
* -> iwm_send_umac_config()
*
* iwm_stop() (Called at netdev interface bringdown time)
* -> iwm_down()
* -> iwm_bus_disable()
* -> if_sdio_disable() (In case of an SDIO bus)
* -> sdio_disable_func()
*/
#include <linux/netdevice.h>
#include "iwm.h"
#include "cfg80211.h"
#include "debug.h"
static int iwm_open(struct net_device *ndev)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
int ret = 0;
if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
ret = iwm_up(iwm);
return ret;
}
static int iwm_stop(struct net_device *ndev)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
int ret = 0;
if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
ret = iwm_down(iwm);
return ret;
}
/*
* iwm AC to queue mapping
*
* AC_VO -> queue 3
* AC_VI -> queue 2
* AC_BE -> queue 1
* AC_BK -> queue 0
*/
static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb)
{
skb->priority = cfg80211_classify8021d(skb);
return iwm_1d_to_queue[skb->priority];
}
static const struct net_device_ops iwm_netdev_ops = {
.ndo_open = iwm_open,
.ndo_stop = iwm_stop,
.ndo_start_xmit = iwm_xmit_frame,
.ndo_select_queue = iwm_select_queue,
};
void *iwm_if_alloc(int sizeof_bus, struct device *dev,
struct iwm_if_ops *if_ops)
{
struct net_device *ndev;
struct wireless_dev *wdev;
struct iwm_priv *iwm;
int ret = 0;
wdev = iwm_wdev_alloc(sizeof_bus, dev);
if (!wdev) {
dev_err(dev, "no memory for wireless device instance\n");
return ERR_PTR(-ENOMEM);
}
iwm = wdev_to_iwm(wdev);
iwm->bus_ops = if_ops;
iwm->wdev = wdev;
iwm_priv_init(iwm);
wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode);
ndev = alloc_netdev_mq(0, "wlan%d", ether_setup,
IWM_TX_QUEUES);
if (!ndev) {
dev_err(dev, "no memory for network device instance\n");
goto out_wdev;
}
ndev->netdev_ops = &iwm_netdev_ops;
ndev->wireless_handlers = &iwm_iw_handler_def;
ndev->ieee80211_ptr = wdev;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
ret = register_netdev(ndev);
if (ret < 0) {
dev_err(dev, "Failed to register netdev: %d\n", ret);
goto out_ndev;
}
wdev->netdev = ndev;
ret = iwm_rfkill_init(iwm);
if (ret) {
dev_err(dev, "Failed to init rfkill\n");
goto out_rfkill;
}
return iwm;
out_rfkill:
unregister_netdev(ndev);
out_ndev:
free_netdev(ndev);
out_wdev:
iwm_wdev_free(iwm);
return ERR_PTR(ret);
}
void iwm_if_free(struct iwm_priv *iwm)
{
int i;
if (!iwm_to_ndev(iwm))
return;
iwm_rfkill_exit(iwm);
unregister_netdev(iwm_to_ndev(iwm));
free_netdev(iwm_to_ndev(iwm));
iwm_wdev_free(iwm);
destroy_workqueue(iwm->rx_wq);
for (i = 0; i < IWM_TX_QUEUES; i++)
destroy_workqueue(iwm->txq[i].wq);
}
此差异已折叠。
此差异已折叠。
/*
* Intel Wireless Multicomm 3200 WiFi driver
*
* Copyright (C) 2009 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <ilw@linux.intel.com>
* Samuel Ortiz <samuel.ortiz@intel.com>
* Zhu Yi <yi.zhu@intel.com>
*
*/
#ifndef __IWM_RX_H__
#define __IWM_RX_H__
#include <linux/skbuff.h>
#include "umac.h"
struct iwm_rx_ticket_node {
struct list_head node;
struct iwm_rx_ticket *ticket;
};
struct iwm_rx_packet {
struct list_head node;
u16 id;
struct sk_buff *skb;
unsigned long pkt_size;
};
void iwm_rx_worker(struct work_struct *work);
#endif
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册