iwmc3200wifi: remove driver for unavailable hardware

This hardware never became available to normal humans.  Leaving this
driver imposes unwelcome maintenance costs for no clear benefit.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Acked-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
John W. Linville 2012-06-26 15:10:12 -04:00
parent f7ace5f044
commit bdb903e499
30 changed files with 0 additions and 10563 deletions

View File

@ -3654,14 +3654,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
S: Supported
F: drivers/net/wireless/iwlwifi/
INTEL WIRELESS MULTICOMM 3200 WIFI (iwmc3200wifi)
M: Samuel Ortiz <samuel.ortiz@intel.com>
M: Intel Linux Wireless <ilw@linux.intel.com>
L: linux-wireless@vger.kernel.org
S: Supported
W: http://wireless.kernel.org/en/users/Drivers/iwmc3200wifi
F: drivers/net/wireless/iwmc3200wifi/
INTEL MANAGEMENT ENGINE (mei)
M: Tomas Winkler <tomas.winkler@intel.com>
L: linux-kernel@vger.kernel.org

View File

@ -276,7 +276,6 @@ source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/ipw2x00/Kconfig"
source "drivers/net/wireless/iwlwifi/Kconfig"
source "drivers/net/wireless/iwlegacy/Kconfig"
source "drivers/net/wireless/iwmc3200wifi/Kconfig"
source "drivers/net/wireless/libertas/Kconfig"
source "drivers/net/wireless/orinoco/Kconfig"
source "drivers/net/wireless/p54/Kconfig"

View File

@ -53,8 +53,6 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
obj-$(CONFIG_WL_TI) += ti/
obj-$(CONFIG_IWM) += iwmc3200wifi/
obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BRCMFMAC) += brcm80211/

View File

@ -1,39 +0,0 @@
config IWM
tristate "Intel Wireless Multicomm 3200 WiFi driver (EXPERIMENTAL)"
depends on MMC && EXPERIMENTAL
depends on CFG80211
select FW_LOADER
select IWMC3200TOP
help
The Intel Wireless Multicomm 3200 hardware is a combo
card with GPS, Bluetooth, WiMax and 802.11 radios. It
runs over SDIO and is typically found on Moorestown
based platform. This driver takes care of the 802.11
part, which is a fullmac one.
If you choose to build it as a module, it'll be called
iwmc3200wifi.ko.
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 > /sys/kernel/debug/iwm/phyN/debug/mlme
Or, if you want the full debug, for all modules:
echo 0xff > /sys/kernel/debug/iwm/phyN/debug/level
echo 0xff > /sys/kernel/debug/iwm/phyN/debug/modules
config IWM_TRACING
bool "Enable event tracing for iwmc3200wifi"
depends on IWM && EVENT_TRACING
help
Say Y here to trace all the commands and responses between
the driver and firmware (including TX/RX frames) with ftrace.

View File

@ -1,10 +0,0 @@
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 cfg80211.o eeprom.o
iwmc3200wifi-$(CONFIG_IWM_DEBUG) += debugfs.o
iwmc3200wifi-$(CONFIG_IWM_TRACING) += trace.o
CFLAGS_trace.o := -I$(src)
ccflags-y += -D__CHECK_ENDIAN__

View File

@ -1,57 +0,0 @@
/*
* 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);
void (*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

View File

@ -1,882 +0,0 @@
/*
* 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/sched.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/ieee80211.h>
#include <linux/slab.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,
};
static int iwm_key_init(struct iwm_key *key, u8 key_index,
const u8 *mac_addr, struct key_params *params)
{
key->hdr.key_idx = key_index;
if (!mac_addr || is_broadcast_ether_addr(mac_addr)) {
key->hdr.multicast = 1;
memset(key->hdr.mac, 0xff, ETH_ALEN);
} else {
key->hdr.multicast = 0;
memcpy(key->hdr.mac, mac_addr, ETH_ALEN);
}
if (params) {
if (params->key_len > WLAN_MAX_KEY_LEN ||
params->seq_len > IW_ENCODE_SEQ_MAX_SIZE)
return -EINVAL;
key->cipher = params->cipher;
key->key_len = params->key_len;
key->seq_len = params->seq_len;
memcpy(key->key, params->key, key->key_len);
memcpy(key->seq, params->seq, key->seq_len);
}
return 0;
}
static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
struct iwm_key *key;
int ret;
IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);
if (key_index >= IWM_NUM_KEYS)
return -ENOENT;
key = &iwm->keys[key_index];
memset(key, 0, sizeof(struct iwm_key));
ret = iwm_key_init(key, key_index, mac_addr, params);
if (ret < 0) {
IWM_ERR(iwm, "Invalid key_params\n");
return ret;
}
return iwm_set_key(iwm, 0, key);
}
static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, bool pairwise, const u8 *mac_addr,
void *cookie,
void (*callback)(void *cookie,
struct key_params*))
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
struct iwm_key *key;
struct key_params params;
IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
if (key_index >= IWM_NUM_KEYS)
return -ENOENT;
memset(&params, 0, sizeof(params));
key = &iwm->keys[key_index];
params.cipher = key->cipher;
params.key_len = key->key_len;
params.seq_len = key->seq_len;
params.seq = key->seq;
params.key = key->key;
callback(cookie, &params);
return key->key_len ? 0 : -ENOENT;
}
static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, bool pairwise, const u8 *mac_addr)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
struct iwm_key *key;
if (key_index >= IWM_NUM_KEYS)
return -ENOENT;
key = &iwm->keys[key_index];
if (!iwm->keys[key_index].key_len) {
IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
return 0;
}
if (key_index == iwm->default_key)
iwm->default_key = -1;
return iwm_set_key(iwm, 1, key);
}
static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool unicast,
bool multicast)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
if (key_index >= IWM_NUM_KEYS)
return -ENOENT;
if (!iwm->keys[key_index].key_len) {
IWM_ERR(iwm, "Key %d not used\n", key_index);
return -EINVAL;
}
iwm->default_key = key_index;
return iwm_set_tx_key(iwm, key_index);
}
static int iwm_cfg80211_get_station(struct wiphy *wiphy,
struct net_device *ndev,
u8 *mac, struct station_info *sinfo)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
if (memcmp(mac, iwm->bssid, ETH_ALEN))
return -ENOENT;
sinfo->filled |= STATION_INFO_TX_BITRATE;
sinfo->txrate.legacy = iwm->rate * 10;
if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
sinfo->filled |= STATION_INFO_SIGNAL;
sinfo->signal = iwm->wstats.qual.level;
}
return 0;
}
int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
{
struct wiphy *wiphy = iwm_to_wiphy(iwm);
struct iwm_bss_info *bss;
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(bss, &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,
band->band);
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,
struct net_device *ndev,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
struct wireless_dev *wdev;
struct iwm_priv *iwm;
u32 old_mode;
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)
iwm_invalidate_mlme_profile(iwm);
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_FA_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;
if (!test_bit(IWM_STATUS_READY, &iwm->status))
return -EIO;
/* UMAC doesn't support creating or joining an IBSS network
* with specified bssid. */
if (params->bssid)
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);
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 int iwm_set_auth_type(struct iwm_priv *iwm,
enum nl80211_auth_type sme_auth_type)
{
u8 *auth_type = &iwm->umac_profile->sec.auth_type;
switch (sme_auth_type) {
case NL80211_AUTHTYPE_AUTOMATIC:
case NL80211_AUTHTYPE_OPEN_SYSTEM:
IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n");
*auth_type = UMAC_AUTH_TYPE_OPEN;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
if (iwm->umac_profile->sec.flags &
(UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n");
*auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
} else {
IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n");
*auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
}
break;
default:
IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type);
return -ENOTSUPP;
}
return 0;
}
static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
{
IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version);
if (!wpa_version) {
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
return 0;
}
if (wpa_version & NL80211_WPA_VERSION_1)
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
if (wpa_version & NL80211_WPA_VERSION_2)
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
return 0;
}
static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast)
{
u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
&iwm->umac_profile->sec.mcast_cipher;
if (!cipher) {
*profile_cipher = UMAC_CIPHER_TYPE_NONE;
return 0;
}
IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm',
cipher);
switch (cipher) {
case IW_AUTH_CIPHER_NONE:
*profile_cipher = UMAC_CIPHER_TYPE_NONE;
break;
case WLAN_CIPHER_SUITE_WEP40:
*profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
break;
case WLAN_CIPHER_SUITE_WEP104:
*profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
break;
case WLAN_CIPHER_SUITE_TKIP:
*profile_cipher = UMAC_CIPHER_TYPE_TKIP;
break;
case WLAN_CIPHER_SUITE_CCMP:
*profile_cipher = UMAC_CIPHER_TYPE_CCMP;
break;
default:
IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
return -ENOTSUPP;
}
return 0;
}
static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt)
{
u8 *auth_type = &iwm->umac_profile->sec.auth_type;
IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
if (key_mgt == WLAN_AKM_SUITE_8021X)
*auth_type = UMAC_AUTH_TYPE_8021X;
else if (key_mgt == WLAN_AKM_SUITE_PSK) {
if (iwm->umac_profile->sec.flags &
(UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
*auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
else
*auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
} else {
IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
return -EINVAL;
}
return 0;
}
static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
struct ieee80211_channel *chan = sme->channel;
struct key_params key_param;
int ret;
if (!test_bit(IWM_STATUS_READY, &iwm->status))
return -EIO;
if (!sme->ssid)
return -EINVAL;
if (iwm->umac_profile_active) {
ret = iwm_invalidate_mlme_profile(iwm);
if (ret) {
IWM_ERR(iwm, "Couldn't invalidate profile\n");
return ret;
}
}
if (chan)
iwm->channel =
ieee80211_frequency_to_channel(chan->center_freq);
iwm->umac_profile->ssid.ssid_len = sme->ssid_len;
memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len);
if (sme->bssid) {
IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid);
memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN);
iwm->umac_profile->bss_num = 1;
} else {
memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
iwm->umac_profile->bss_num = 0;
}
ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions);
if (ret < 0)
return ret;
ret = iwm_set_auth_type(iwm, sme->auth_type);
if (ret < 0)
return ret;
if (sme->crypto.n_ciphers_pairwise) {
ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0],
true);
if (ret < 0)
return ret;
}
ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false);
if (ret < 0)
return ret;
if (sme->crypto.n_akm_suites) {
ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]);
if (ret < 0)
return ret;
}
/*
* We save the WEP key in case we want to do shared authentication.
* We have to do it so because UMAC will assert whenever it gets a
* key before a profile.
*/
if (sme->key) {
key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
if (key_param.key == NULL)
return -ENOMEM;
key_param.key_len = sme->key_len;
key_param.seq_len = 0;
key_param.cipher = sme->crypto.ciphers_pairwise[0];
ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
NULL, &key_param);
kfree(key_param.key);
if (ret < 0) {
IWM_ERR(iwm, "Invalid key_params\n");
return ret;
}
iwm->default_key = sme->key_idx;
}
/* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */
if ((iwm->umac_profile->sec.flags &
(UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) &&
iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) {
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK;
}
ret = iwm_send_mlme_profile(iwm);
if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
sme->key == NULL)
return ret;
/*
* We want to do shared auth.
* We need to actually set the key we previously cached,
* and then tell the UMAC it's the default one.
* That will trigger the auth+assoc UMAC machinery, and again,
* this must be done after setting the profile.
*/
ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
if (ret < 0)
return ret;
return iwm_set_tx_key(iwm, iwm->default_key);
}
static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
u16 reason_code)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);
if (iwm->umac_profile_active)
iwm_invalidate_mlme_profile(iwm);
return 0;
}
static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
enum nl80211_tx_power_setting type, int mbm)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
int ret;
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
return 0;
case NL80211_TX_POWER_FIXED:
if (mbm < 0 || (mbm % 100))
return -EOPNOTSUPP;
if (!test_bit(IWM_STATUS_READY, &iwm->status))
return 0;
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
CFG_TX_PWR_LIMIT_USR,
MBM_TO_DBM(mbm) * 2);
if (ret < 0)
return ret;
return iwm_tx_power_trigger(iwm);
default:
IWM_ERR(iwm, "Unsupported power type: %d\n", type);
return -EOPNOTSUPP;
}
return 0;
}
static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
*dbm = iwm->txpower >> 1;
return 0;
}
static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
struct net_device *dev,
bool enabled, int timeout)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
u32 power_index;
if (enabled)
power_index = IWM_POWER_INDEX_DEFAULT;
else
power_index = IWM_POWER_INDEX_MIN;
if (power_index == iwm->conf.power_index)
return 0;
iwm->conf.power_index = power_index;
return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
CFG_POWER_INDEX, iwm->conf.power_index);
}
static int iwm_cfg80211_set_pmksa(struct wiphy *wiphy,
struct net_device *netdev,
struct cfg80211_pmksa *pmksa)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
}
static int iwm_cfg80211_del_pmksa(struct wiphy *wiphy,
struct net_device *netdev,
struct cfg80211_pmksa *pmksa)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
}
static int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy,
struct net_device *netdev)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
struct cfg80211_pmksa pmksa;
memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
}
static struct cfg80211_ops iwm_cfg80211_ops = {
.change_virtual_intf = iwm_cfg80211_change_iface,
.add_key = iwm_cfg80211_add_key,
.get_key = iwm_cfg80211_get_key,
.del_key = iwm_cfg80211_del_key,
.set_default_key = iwm_cfg80211_set_default_key,
.get_station = iwm_cfg80211_get_station,
.scan = iwm_cfg80211_scan,
.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
.connect = iwm_cfg80211_connect,
.disconnect = iwm_cfg80211_disconnect,
.join_ibss = iwm_cfg80211_join_ibss,
.leave_ibss = iwm_cfg80211_leave_ibss,
.set_tx_power = iwm_cfg80211_set_txpower,
.get_tx_power = iwm_cfg80211_get_txpower,
.set_power_mgmt = iwm_cfg80211_set_power_mgmt,
.set_pmksa = iwm_cfg80211_set_pmksa,
.del_pmksa = iwm_cfg80211_del_pmksa,
.flush_pmksa = iwm_cfg80211_flush_pmksa,
};
static const u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
};
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->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
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;
wdev->wiphy->cipher_suites = cipher_suites;
wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
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);
}

View File

@ -1,31 +0,0 @@
/*
* 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

File diff suppressed because it is too large Load Diff

View File

@ -1,509 +0,0 @@
/*
* 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;
} __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_SPATIAL_STREAM_SUPPORTED,
CFG_TLC_RETRY_PER_RATE,
CFG_TLC_RETRY_PER_HT_RATE,
CFG_TLC_FIXED_MCS,
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_TLC_SR_SIC_1ST_FAIL,
CFG_TLC_SR_SIC_1ST_PASS,
CFG_TLC_SR_SIC_TOTAL_FAIL,
CFG_TLC_SR_SIC_TOTAL_PASS,
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,
CFG_ACTIVE_CHAINS,
CFG_CALIB_CTRL,
CFG_CAPABILITY_SUPPORTED_HT_RATES,
CFG_HT_MAC_PARAM_INFO,
CFG_MIMO_PS_MODE,
CFG_HT_DEFAULT_CAPABILIES_INFO,
CFG_LED_SC_RESOLUTION_FACTOR,
CFG_PTAM_ENERGY_CCK_DET_DEFAULT,
CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_DEFAULT,
CFG_PTAM_CORR40_4_TH_ADD_MIN_DEFAULT,
CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_DEFAULT,
CFG_PTAM_CORR32_4_TH_ADD_MIN_DEFAULT,
CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_DEFAULT,
CFG_PTAM_CORR32_1_TH_ADD_MIN_DEFAULT,
CFG_PTAM_ENERGY_CCK_DET_MIN_VAL,
CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MIN_VAL,
CFG_PTAM_CORR40_4_TH_ADD_MIN_MIN_VAL,
CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MIN_VAL,
CFG_PTAM_CORR32_4_TH_ADD_MIN_MIN_VAL,
CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MIN_VAL,
CFG_PTAM_CORR32_1_TH_ADD_MIN_MIN_VAL,
CFG_PTAM_ENERGY_CCK_DET_MAX_VAL,
CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MAX_VAL,
CFG_PTAM_CORR40_4_TH_ADD_MIN_MAX_VAL,
CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MAX_VAL,
CFG_PTAM_CORR32_4_TH_ADD_MIN_MAX_VAL,
CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MAX_VAL,
CFG_PTAM_CORR32_1_TH_ADD_MIN_MAX_VAL,
CFG_PTAM_ENERGY_CCK_DET_STEP_VAL,
CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_STEP_VAL,
CFG_PTAM_CORR40_4_TH_ADD_MIN_STEP_VAL,
CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_STEP_VAL,
CFG_PTAM_CORR32_4_TH_ADD_MIN_STEP_VAL,
CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_STEP_VAL,
CFG_PTAM_CORR32_1_TH_ADD_MIN_STEP_VAL,
CFG_PTAM_LINK_SENS_FA_OFDM_MAX,
CFG_PTAM_LINK_SENS_FA_OFDM_MIN,
CFG_PTAM_LINK_SENS_FA_CCK_MAX,
CFG_PTAM_LINK_SENS_FA_CCK_MIN,
CFG_PTAM_LINK_SENS_NRG_DIFF,
CFG_PTAM_LINK_SENS_NRG_MARGIN,
CFG_PTAM_LINK_SENS_MAX_NUMBER_OF_TIMES_IN_CCK_NO_FA,
CFG_PTAM_LINK_SENS_AUTO_CORR_MAX_TH_CCK,
CFG_AGG_MGG_TID_LOAD_ADDBA_THRESHOLD,
CFG_AGG_MGG_TID_LOAD_DELBA_THRESHOLD,
CFG_AGG_MGG_ADDBA_BUF_SIZE,
CFG_AGG_MGG_ADDBA_INACTIVE_TIMEOUT,
CFG_AGG_MGG_ADDBA_DEBUG_FLAGS,
CFG_SCAN_PERIODIC_RSSI_HIGH_THRESHOLD,
CFG_SCAN_PERIODIC_COEF_RSSI_HIGH,
CFG_11D_ENABLED,
CFG_11H_FEATURE_FLAGS,
/* <-- LAST --> */
CFG_TBL_FIX_LAST
};
/* variable size table */
enum {
CFG_NET_ADDR = 0,
CFG_LED_PATTERN_TABLE,
/* <-- LAST --> */
CFG_TBL_VAR_LAST
};
struct iwm_umac_cmd_set_param_fix {
__le16 tbl;
__le16 key;
__le32 value;
} __packed;
struct iwm_umac_cmd_set_param_var {
__le16 tbl;
__le16 key;
__le16 len;
__le16 reserved;
} __packed;
struct iwm_umac_cmd_get_param {
__le16 tbl;
__le16 key;
} __packed;
struct iwm_umac_cmd_get_param_resp {
__le16 tbl;
__le16 key;
__le16 len;
__le16 reserved;
} __packed;
struct iwm_umac_cmd_eeprom_proxy_hdr {
__le32 type;
__le32 offset;
__le32 len;
} __packed;
struct iwm_umac_cmd_eeprom_proxy {
struct iwm_umac_cmd_eeprom_proxy_hdr hdr;
u8 buf[0];
} __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;
} __packed;
struct iwm_umac_cmd_get_channel_list {
__le16 count;
__le16 reserved;
struct iwm_umac_channel_info ch[0];
} __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 WIRELESS_MODE_11N 0x4
#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];
} __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];
} __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
#define UMAC_SEC_FLG_WSC_ON_MSK (UMAC_SEC_FLG_WSC_ON_SEED << \
UMAC_SEC_FLG_WSC_ON_POS)
/* 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;
} __packed;
struct iwm_umac_ibss {
u8 beacon_interval; /* in millisecond */
u8 atim; /* in millisecond */
s8 join_only;
u8 band;
u8 channel;
u8 reserved[3];
} __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;
} __packed;
struct iwm_umac_invalidate_profile {
struct iwm_umac_wifi_if hdr;
u8 reason;
u8 reserved[3];
} __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];
} __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];
} __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];
} __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];
} __packed;
struct iwm_umac_key_remove {
struct iwm_umac_wifi_if hdr;
struct iwm_umac_key_hdr key_hdr;
} __packed;
struct iwm_umac_tx_key_id {
struct iwm_umac_wifi_if hdr;
u8 key_idx;
u8 reserved[3];
} __packed;
struct iwm_umac_pwr_trigger {
struct iwm_umac_wifi_if hdr;
__le32 reseved;
} __packed;
struct iwm_umac_cmd_stats_req {
__le32 flags;
} __packed;
struct iwm_umac_cmd_stop_resume_tx {
u8 flags;
u8 sta_id;
__le16 stop_resume_tid_msk;
__le16 last_seq_num[IWM_UMAC_TID_NR];
u16 reserved;
} __packed;
#define IWM_CMD_PMKID_ADD 1
#define IWM_CMD_PMKID_DEL 2
#define IWM_CMD_PMKID_FLUSH 3
struct iwm_umac_pmkid_update {
struct iwm_umac_wifi_if hdr;
__le32 command;
u8 bssid[ETH_ALEN];
__le16 reserved;
u8 pmkid[WLAN_PMKID_LEN];
} __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);
int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit);
/* 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_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, struct iwm_key *key);
int iwm_tx_power_trigger(struct iwm_priv *iwm);
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);
int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
struct iwm_umac_notif_stop_resume_tx *ntf);
int iwm_send_pmkid_update(struct iwm_priv *iwm,
struct cfg80211_pmksa *pmksa, u32 command);
/* UDMA commands */
int iwm_target_reset(struct iwm_priv *iwm);
#endif

View File

@ -1,123 +0,0 @@
/*
* 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;
struct dentry *fw_err_dentry;
};
#ifdef CONFIG_IWM_DEBUG
void iwm_debugfs_init(struct iwm_priv *iwm);
void iwm_debugfs_exit(struct iwm_priv *iwm);
#else
static inline void iwm_debugfs_init(struct iwm_priv *iwm) {}
static inline void iwm_debugfs_exit(struct iwm_priv *iwm) {}
#endif
#endif

View File

@ -1,488 +0,0 @@
/*
* 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/slab.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/export.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 { \
dbg.dbg_module[id] = (initlevel); \
dbg.dbg_module_dentries[id] = \
debugfs_create_x8(name, 0600, \
dbg.dbgdir, \
&(dbg.dbg_module[id])); \
} 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_set_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 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);
spin_lock_irqsave(&txq->stopped_queue.lock, flags);
len += snprintf(buf + len, buf_len - len,
"\tStopped Queue len: %d\n",
skb_queue_len(&txq->stopped_queue));
for (j = 0; j < skb_queue_len(&txq->stopped_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->stopped_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;
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;
spin_lock(&iwm->ticket_lock);
list_for_each_entry(ticket, &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);
}
spin_unlock(&iwm->ticket_lock);
for (i = 0; i < IWM_RX_ID_HASH; i++) {
struct iwm_rx_packet *packet;
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);
spin_lock(&iwm->packet_lock[i]);
list_for_each_entry(packet, 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);
}
spin_unlock(&iwm->packet_lock[i]);
}
}
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
kfree(buf);
return ret;
}
static ssize_t iwm_debugfs_fw_err_read(struct file *filp,
char __user *buffer,
size_t count, loff_t *ppos)
{
struct iwm_priv *iwm = filp->private_data;
char buf[512];
int buf_len = 512;
size_t len = 0;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
if (!iwm->last_fw_err)
return -ENOMEM;
if (iwm->last_fw_err->line_num == 0)
goto out;
len += snprintf(buf + len, buf_len - len, "%cMAC FW ERROR:\n",
(le32_to_cpu(iwm->last_fw_err->category) == UMAC_SYS_ERR_CAT_LMAC)
? 'L' : 'U');
len += snprintf(buf + len, buf_len - len,
"\tCategory: %d\n",
le32_to_cpu(iwm->last_fw_err->category));
len += snprintf(buf + len, buf_len - len,
"\tStatus: 0x%x\n",
le32_to_cpu(iwm->last_fw_err->status));
len += snprintf(buf + len, buf_len - len,
"\tPC: 0x%x\n",
le32_to_cpu(iwm->last_fw_err->pc));
len += snprintf(buf + len, buf_len - len,
"\tblink1: %d\n",
le32_to_cpu(iwm->last_fw_err->blink1));
len += snprintf(buf + len, buf_len - len,
"\tblink2: %d\n",
le32_to_cpu(iwm->last_fw_err->blink2));
len += snprintf(buf + len, buf_len - len,
"\tilink1: %d\n",
le32_to_cpu(iwm->last_fw_err->ilink1));
len += snprintf(buf + len, buf_len - len,
"\tilink2: %d\n",
le32_to_cpu(iwm->last_fw_err->ilink2));
len += snprintf(buf + len, buf_len - len,
"\tData1: 0x%x\n",
le32_to_cpu(iwm->last_fw_err->data1));
len += snprintf(buf + len, buf_len - len,
"\tData2: 0x%x\n",
le32_to_cpu(iwm->last_fw_err->data2));
len += snprintf(buf + len, buf_len - len,
"\tLine number: %d\n",
le32_to_cpu(iwm->last_fw_err->line_num));
len += snprintf(buf + len, buf_len - len,
"\tUMAC status: 0x%x\n",
le32_to_cpu(iwm->last_fw_err->umac_status));
len += snprintf(buf + len, buf_len - len,
"\tLMAC status: 0x%x\n",
le32_to_cpu(iwm->last_fw_err->lmac_status));
len += snprintf(buf + len, buf_len - len,
"\tSDIO status: 0x%x\n",
le32_to_cpu(iwm->last_fw_err->sdio_status));
out:
return simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
}
static const struct file_operations iwm_debugfs_txq_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = iwm_debugfs_txq_read,
.llseek = default_llseek,
};
static const struct file_operations iwm_debugfs_tx_credit_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = iwm_debugfs_tx_credit_read,
.llseek = default_llseek,
};
static const struct file_operations iwm_debugfs_rx_ticket_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = iwm_debugfs_rx_ticket_read,
.llseek = default_llseek,
};
static const struct file_operations iwm_debugfs_fw_err_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = iwm_debugfs_fw_err_read,
.llseek = default_llseek,
};
void iwm_debugfs_init(struct iwm_priv *iwm)
{
int i;
iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
iwm->dbg.devdir = debugfs_create_dir(wiphy_name(iwm_to_wiphy(iwm)),
iwm->dbg.rootdir);
iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir);
iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir);
iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir);
iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir);
if (iwm->bus_ops->debugfs_init)
iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir);
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);
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);
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);
iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200,
iwm->dbg.txdir, iwm,
&iwm_debugfs_tx_credit_fops);
iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200,
iwm->dbg.rxdir, iwm,
&iwm_debugfs_rx_ticket_fops);
iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200,
iwm->dbg.dbgdir, iwm,
&iwm_debugfs_fw_err_fops);
}
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);
debugfs_remove(iwm->dbg.fw_err_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);
}

View File

@ -1,234 +0,0 @@
/*
* 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/slab.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_FAT_CHANNELS_CAP] =
{"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF,
IWM_EEPROM_FAT_CHANNELS_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_fat_channels(struct iwm_priv *iwm)
{
struct wiphy *wiphy = iwm_to_wiphy(iwm);
struct ieee80211_supported_band *band;
u16 *channels, i;
channels = (u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_FAT_CHANNELS_CAP);
if (IS_ERR(channels))
return PTR_ERR(channels);
band = wiphy->bands[IEEE80211_BAND_2GHZ];
band->ht_cap.ht_supported = true;
for (i = 0; i < IWM_EEPROM_FAT_CHANNELS_24; i++)
if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED))
band->ht_cap.ht_supported = false;
band = wiphy->bands[IEEE80211_BAND_5GHZ];
band->ht_cap.ht_supported = true;
for (i = IWM_EEPROM_FAT_CHANNELS_24; i < IWM_EEPROM_FAT_CHANNELS; i++)
if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED))
band->ht_cap.ht_supported = false;
return 0;
}
u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm)
{
u16 sku_cap;
u32 wireless_mode = 0;
sku_cap = *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP));
if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_24GHZ)
wireless_mode |= WIRELESS_MODE_11G;
if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_52GHZ)
wireless_mode |= WIRELESS_MODE_11A;
if (sku_cap & IWM_EEPROM_SKU_CAP_11N_ENABLE)
wireless_mode |= WIRELESS_MODE_11N;
return wireless_mode;
}
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++) {
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);
}

View File

@ -1,127 +0,0 @@
/*
* 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_FAT_CHANNELS_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_FAT_CHANNELS_CAP_OFF (0xde << 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_FAT_CHANNELS_CAP_LEN 40
#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)
#define IWM_EEPROM_FAT_CHANNELS 20
/* 2.4 gHz FAT primary channels: 1, 2, 3, 4, 5, 6, 7, 8, 9 */
#define IWM_EEPROM_FAT_CHANNELS_24 9
/* 5.2 gHz FAT primary channels: 36,44,52,60,100,108,116,124,132,149,157 */
#define IWM_EEPROM_FAT_CHANNELS_52 11
#define IWM_EEPROM_FAT_CHANNEL_ENABLED (1 << 0)
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);
int iwm_eeprom_fat_channels(struct iwm_priv *iwm);
u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm);
#endif

View File

@ -1,416 +0,0 @@
/*
* 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 (%zu)\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));
if (!strcmp(img_name, iwm->bus_ops->umac_name))
sprintf(iwm->umac_version, "%02X.%02X",
ver->major, ver->minor);
if (!strcmp(img_name, iwm->bus_ops->lmac_name))
sprintf(iwm->lmac_version, "%02X.%02X",
ver->major, ver->minor);
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);
}
static int iwm_init_calib(struct iwm_priv *iwm, unsigned long cfg_bitmap,
unsigned long expected_bitmap, u8 rx_iq_cmd)
{
/* Read RX IQ calibration result from EEPROM */
if (test_bit(rx_iq_cmd, &cfg_bitmap)) {
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, cfg_bitmap);
while (iwm->calib_done_map != expected_bitmap) {
if (iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION,
IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT)) {
IWM_DBG_FW(iwm, DBG, "Initial calibration timeout\n");
return -ETIMEDOUT;
}
IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: "
"0x%lx, expected calibrations: 0x%lx\n",
iwm->calib_done_map, expected_bitmap);
}
return 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)
{
unsigned long init_calib_map, periodic_calib_map;
unsigned long expected_calib_map;
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;
}
init_calib_map = iwm->conf.calib_map & IWM_CALIB_MAP_INIT_MSK;
expected_calib_map = iwm->conf.expected_calib_map &
IWM_CALIB_MAP_INIT_MSK;
periodic_calib_map = IWM_CALIB_MAP_PER_LMAC(iwm->conf.calib_map);
ret = iwm_init_calib(iwm, init_calib_map, expected_calib_map,
CALIB_CFG_RX_IQ_IDX);
if (ret < 0) {
/* Let's try the old way */
ret = iwm_init_calib(iwm, expected_calib_map,
expected_calib_map,
PHY_CALIBRATE_RX_IQ_CMD);
if (ret < 0) {
IWM_ERR(iwm, "Calibration result timeout\n");
goto out;
}
}
/* 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, periodic_calib_map);
iwm_send_ct_kill_cfg(iwm, iwm->conf.ct_kill_entry,
iwm->conf.ct_kill_exit);
return 0;
out:
iwm_eeprom_exit(iwm);
return ret;
}

View File

@ -1,100 +0,0 @@
/*
* 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

View File

@ -1,470 +0,0 @@
/*
* 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 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 laid 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 laid 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 <linux/slab.h>
#include "iwm.h"
#include "bus.h"
#include "hal.h"
#include "umac.h"
#include "debug.h"
#include "trace.h"
static int 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);
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));
return cmd->seq_num;
}
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;
list_for_each_entry(cmd, &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;
list_for_each_entry(cmd, &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);
trace_iwm_tx_nonwifi_cmd(iwm, udma_hdr);
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;
}
trace_iwm_tx_wifi_cmd(iwm, umac_hdr);
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, seq_num;
cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
if (!cmd) {
IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
return -ENOMEM;
}
seq_num = 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 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 passthrough 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);
}

View File

@ -1,237 +0,0 @@
/*
* 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_HDI_IN_NW_CMD_OPCODE_POS 0
#define UDMA_HDI_IN_NW_CMD_OPCODE_SEED 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 (32 * 1024)
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_ */

View File

@ -1,367 +0,0 @@
/*
* 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"
#include "trace.h"
#define IWM_COPYRIGHT "Copyright(c) 2009 Intel Corporation"
#define IWM_AUTHOR "<ilw@linux.intel.com>"
#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 calib_map;
unsigned long expected_calib_map;
u8 ct_kill_entry;
u8 ct_kill_exit;
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;
u8 ibss_band;
u8 ibss_channel;
u8 mac_addr[ETH_ALEN];
};
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_tid_info {
__le16 last_seq_num;
bool stopped;
struct mutex mutex;
};
struct iwm_sta_info {
u8 addr[ETH_ALEN];
bool valid;
bool qos;
u8 color;
struct iwm_tid_info tid_info[IWM_UMAC_TID_NR];
};
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 */
} __packed;
struct iwm_key {
struct iwm_umac_key_hdr hdr;
u32 cipher;
u8 key[WLAN_MAX_KEY_LEN];
u8 seq[IW_ENCODE_SEQ_MAX_SIZE];
int key_len;
int seq_len;
};
#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_SME_CONNECTING 3
#define IWM_STATUS_ASSOCIATED 4
#define IWM_STATUS_RESETTING 5
struct iwm_tx_queue {
int id;
struct sk_buff_head queue;
struct sk_buff_head stopped_queue;
spinlock_t lock;
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;
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;
u32 txpower;
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;
spinlock_t ticket_lock;
struct list_head rx_packets[IWM_RX_ID_HASH];
spinlock_t packet_lock[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];
s8 default_key;
DECLARE_BITMAP(wifi_ntfy, WIFI_IF_NTFY_MAX);
wait_queue_head_t wifi_ntfy_queue;
wait_queue_head_t mlme_queue;
struct iw_statistics wstats;
struct delayed_work stats_request;
struct delayed_work disconnect;
struct delayed_work ct_kill_delay;
struct iwm_debugfs dbg;
u8 *eeprom;
struct timer_list watchdog;
struct work_struct reset_worker;
struct work_struct auth_retry_worker;
struct mutex mutex;
u8 *req_ie;
int req_ie_len;
u8 *resp_ie;
int resp_ie_len;
struct iwm_fw_error_hdr *last_fw_err;
char umac_version[8];
char lmac_version[8];
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)
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_if_add(struct iwm_priv *iwm);
void iwm_if_remove(struct iwm_priv *iwm);
int iwm_mode_to_nl80211_iftype(int mode);
int iwm_priv_init(struct iwm_priv *iwm);
void iwm_priv_deinit(struct iwm_priv *iwm);
void iwm_reset(struct iwm_priv *iwm);
void iwm_resetting(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 */
int iwm_tid_to_queue(u16 tid);
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);
#endif

View File

@ -1,484 +0,0 @@
/*
* 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;
} __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;
} __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 */
} __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;
} __packed;
struct iwm_lmac_cal_cfg_resp {
__le32 status;
} __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;
} __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;
} __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];
} __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)
/* CT kill config command */
struct iwm_ct_kill_cfg_cmd {
u32 exit_threshold;
u32 reserved;
u32 entry_threshold;
} __packed;
/* 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_ADDRESS_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,
};
enum {
CALIB_CFG_RX_BB_IDX = 0,
CALIB_CFG_DC_IDX = 1,
CALIB_CFG_LO_IDX = 2,
CALIB_CFG_TX_IQ_IDX = 3,
CALIB_CFG_RX_IQ_IDX = 4,
CALIB_CFG_NOISE_IDX = 5,
CALIB_CFG_CRYSTAL_IDX = 6,
CALIB_CFG_TEMPERATURE_IDX = 7,
CALIB_CFG_PAPD_IDX = 8,
CALIB_CFG_LAST_IDX = CALIB_CFG_PAPD_IDX,
CALIB_CFG_MODULE_NUM,
};
#define IWM_CALIB_MAP_INIT_MSK 0xFFFF
#define IWM_CALIB_MAP_PER_LMAC(m) ((m & 0xFF0000) >> 16)
#define IWM_CALIB_MAP_PER_UMAC(m) ((m & 0xFF000000) >> 24)
#define IWM_CALIB_OPCODE_TO_INDEX(op) (op - PHY_CALIBRATE_OPCODES_NUM)
struct iwm_lmac_calib_hdr {
u8 opcode;
u8 first_grp;
u8 grp_num;
u8 all_data_valid;
} __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;
} __packed;
struct iwm_calib_rxiq_group {
struct iwm_calib_rxiq_entry mode[IWM_CALIB_DC_MODES_NR];
} __packed;
struct iwm_lmac_calib_rxiq {
struct iwm_calib_rxiq_group group[IWM_LMAC_CALIB_FREQ_GROUPS_NR];
} __packed;
struct iwm_calib_rxiq {
struct iwm_lmac_calib_hdr hdr;
struct iwm_calib_rxiq_group group[IWM_CALIB_FREQ_GROUPS_NR];
} __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];
} __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;
} __packed;
#endif

View File

@ -1,847 +0,0 @@
/*
* 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/netdevice.h>
#include <linux/sched.h>
#include <linux/ieee80211.h>
#include <linux/wireless.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include "iwm.h"
#include "debug.h"
#include "bus.h"
#include "umac.h"
#include "commands.h"
#include "hal.h"
#include "fw.h"
#include "rx.h"
static struct iwm_conf def_iwm_conf = {
.sdio_ior_timeout = 5000,
.calib_map = BIT(CALIB_CFG_DC_IDX) |
BIT(CALIB_CFG_LO_IDX) |
BIT(CALIB_CFG_TX_IQ_IDX) |
BIT(CALIB_CFG_RX_IQ_IDX) |
BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD),
.expected_calib_map = BIT(PHY_CALIBRATE_DC_CMD) |
BIT(PHY_CALIBRATE_LO_CMD) |
BIT(PHY_CALIBRATE_TX_IQ_CMD) |
BIT(PHY_CALIBRATE_RX_IQ_CMD) |
BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD),
.ct_kill_entry = 110,
.ct_kill_exit = 110,
.reset_on_fatal_err = 1,
.auto_connect = 1,
.enable_qos = 1,
.mode = UMAC_MODE_BSS,
/* UMAC configuration */
.power_index = 0,
.frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
.rts_threshold = IEEE80211_MAX_RTS_THRESHOLD,
.cts_to_self = 0,
.assoc_timeout = 2,
.roam_timeout = 10,
.wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G |
WIRELESS_MODE_11N,
/* IBSS */
.ibss_band = UMAC_BAND_2GHZ,
.ibss_channel = 1,
.mac_addr = {0x00, 0x02, 0xb3, 0x01, 0x02, 0x03},
};
static bool modparam_reset;
module_param_named(reset, modparam_reset, bool, 0644);
MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])");
static bool modparam_wimax_enable = true;
module_param_named(wimax_enable, modparam_wimax_enable, bool, 0644);
MODULE_PARM_DESC(wimax_enable, "Enable wimax core (default 1 [wimax enabled])");
int iwm_mode_to_nl80211_iftype(int mode)
{
switch (mode) {
case UMAC_MODE_BSS:
return NL80211_IFTYPE_STATION;
case UMAC_MODE_IBSS:
return NL80211_IFTYPE_ADHOC;
default:
return NL80211_IFTYPE_UNSPECIFIED;
}
return 0;
}
static void iwm_statistics_request(struct work_struct *work)
{
struct iwm_priv *iwm =
container_of(work, struct iwm_priv, stats_request.work);
iwm_send_umac_stats_req(iwm, 0);
}
static void iwm_disconnect_work(struct work_struct *work)
{
struct iwm_priv *iwm =
container_of(work, struct iwm_priv, disconnect.work);
if (iwm->umac_profile_active)
iwm_invalidate_mlme_profile(iwm);
clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
iwm->umac_profile_active = false;
memset(iwm->bssid, 0, ETH_ALEN);
iwm->channel = 0;
iwm_link_off(iwm);
wake_up_interruptible(&iwm->mlme_queue);
cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL);
}
static void iwm_ct_kill_work(struct work_struct *work)
{
struct iwm_priv *iwm =
container_of(work, struct iwm_priv, ct_kill_delay.work);
struct wiphy *wiphy = iwm_to_wiphy(iwm);
IWM_INFO(iwm, "CT kill delay timeout\n");
wiphy_rfkill_set_hw_state(wiphy, false);
}
static int __iwm_up(struct iwm_priv *iwm);
static int __iwm_down(struct iwm_priv *iwm);
static void iwm_reset_worker(struct work_struct *work)
{
struct iwm_priv *iwm;
struct iwm_umac_profile *profile = NULL;
int uninitialized_var(ret), retry = 0;
iwm = container_of(work, struct iwm_priv, reset_worker);
/*
* XXX: The iwm->mutex is introduced purely for this reset work,
* because the other users for iwm_up and iwm_down are only netdev
* ndo_open and ndo_stop which are already protected by rtnl.
* Please remove iwm->mutex together if iwm_reset_worker() is not
* required in the future.
*/
if (!mutex_trylock(&iwm->mutex)) {
IWM_WARN(iwm, "We are in the middle of interface bringing "
"UP/DOWN. Skip driver resetting.\n");
return;
}
if (iwm->umac_profile_active) {
profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL);
if (profile)
memcpy(profile, iwm->umac_profile, sizeof(*profile));
else
IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
}
__iwm_down(iwm);
while (retry++ < 3) {
ret = __iwm_up(iwm);
if (!ret)
break;
schedule_timeout_uninterruptible(10 * HZ);
}
if (ret) {
IWM_WARN(iwm, "iwm_up() failed: %d\n", ret);
kfree(profile);
goto out;
}
if (profile) {
IWM_DBG_MLME(iwm, DBG, "Resend UMAC profile\n");
memcpy(iwm->umac_profile, profile, sizeof(*profile));
iwm_send_mlme_profile(iwm);
kfree(profile);
} else
clear_bit(IWM_STATUS_RESETTING, &iwm->status);
out:
mutex_unlock(&iwm->mutex);
}
static void iwm_auth_retry_worker(struct work_struct *work)
{
struct iwm_priv *iwm;
int i, ret;
iwm = container_of(work, struct iwm_priv, auth_retry_worker);
if (iwm->umac_profile_active) {
ret = iwm_invalidate_mlme_profile(iwm);
if (ret < 0)
return;
}
iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
ret = iwm_send_mlme_profile(iwm);
if (ret < 0)
return;
for (i = 0; i < IWM_NUM_KEYS; i++)
if (iwm->keys[i].key_len)
iwm_set_key(iwm, 0, &iwm->keys[i]);
iwm_set_tx_key(iwm, iwm->default_key);
}
static void iwm_watchdog(unsigned long data)
{
struct iwm_priv *iwm = (struct iwm_priv *)data;
IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n");
if (modparam_reset)
iwm_resetting(iwm);
}
int iwm_priv_init(struct iwm_priv *iwm)
{
int i, j;
char name[32];
iwm->status = 0;
INIT_LIST_HEAD(&iwm->pending_notif);
init_waitqueue_head(&iwm->notif_queue);
init_waitqueue_head(&iwm->nonwifi_queue);
init_waitqueue_head(&iwm->wifi_ntfy_queue);
init_waitqueue_head(&iwm->mlme_queue);
memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf));
spin_lock_init(&iwm->tx_credit.lock);
INIT_LIST_HEAD(&iwm->wifi_pending_cmd);
INIT_LIST_HEAD(&iwm->nonwifi_pending_cmd);
iwm->wifi_seq_num = UMAC_WIFI_SEQ_NUM_BASE;
iwm->nonwifi_seq_num = UMAC_NONWIFI_SEQ_NUM_BASE;
spin_lock_init(&iwm->cmd_lock);
iwm->scan_id = 1;
INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request);
INIT_DELAYED_WORK(&iwm->disconnect, iwm_disconnect_work);
INIT_DELAYED_WORK(&iwm->ct_kill_delay, iwm_ct_kill_work);
INIT_WORK(&iwm->reset_worker, iwm_reset_worker);
INIT_WORK(&iwm->auth_retry_worker, iwm_auth_retry_worker);
INIT_LIST_HEAD(&iwm->bss_list);
skb_queue_head_init(&iwm->rx_list);
INIT_LIST_HEAD(&iwm->rx_tickets);
spin_lock_init(&iwm->ticket_lock);
for (i = 0; i < IWM_RX_ID_HASH; i++) {
INIT_LIST_HEAD(&iwm->rx_packets[i]);
spin_lock_init(&iwm->packet_lock[i]);
}
INIT_WORK(&iwm->rx_worker, iwm_rx_worker);
iwm->rx_wq = create_singlethread_workqueue(KBUILD_MODNAME "_rx");
if (!iwm->rx_wq)
return -EAGAIN;
for (i = 0; i < IWM_TX_QUEUES; i++) {
INIT_WORK(&iwm->txq[i].worker, iwm_tx_worker);
snprintf(name, 32, KBUILD_MODNAME "_tx_%d", i);
iwm->txq[i].id = i;
iwm->txq[i].wq = create_singlethread_workqueue(name);
if (!iwm->txq[i].wq)
return -EAGAIN;
skb_queue_head_init(&iwm->txq[i].queue);
skb_queue_head_init(&iwm->txq[i].stopped_queue);
spin_lock_init(&iwm->txq[i].lock);
}
for (i = 0; i < IWM_NUM_KEYS; i++)
memset(&iwm->keys[i], 0, sizeof(struct iwm_key));
iwm->default_key = -1;
for (i = 0; i < IWM_STA_TABLE_NUM; i++)
for (j = 0; j < IWM_UMAC_TID_NR; j++) {
mutex_init(&iwm->sta_table[i].tid_info[j].mutex);
iwm->sta_table[i].tid_info[j].stopped = false;
}
init_timer(&iwm->watchdog);
iwm->watchdog.function = iwm_watchdog;
iwm->watchdog.data = (unsigned long)iwm;
mutex_init(&iwm->mutex);
iwm->last_fw_err = kzalloc(sizeof(struct iwm_fw_error_hdr),
GFP_KERNEL);
if (iwm->last_fw_err == NULL)
return -ENOMEM;
return 0;
}
void iwm_priv_deinit(struct iwm_priv *iwm)
{
int i;
for (i = 0; i < IWM_TX_QUEUES; i++)
destroy_workqueue(iwm->txq[i].wq);
destroy_workqueue(iwm->rx_wq);
kfree(iwm->last_fw_err);
}
/*
* We reset all the structures, and we reset the UMAC.
* After calling this routine, you're expected to reload
* the firmware.
*/
void iwm_reset(struct iwm_priv *iwm)
{
struct iwm_notif *notif, *next;
if (test_bit(IWM_STATUS_READY, &iwm->status))
iwm_target_reset(iwm);
if (test_bit(IWM_STATUS_RESETTING, &iwm->status)) {
iwm->status = 0;
set_bit(IWM_STATUS_RESETTING, &iwm->status);
} else
iwm->status = 0;
iwm->scan_id = 1;
list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
list_del(&notif->pending);
kfree(notif->buf);
kfree(notif);
}
iwm_cmd_flush(iwm);
flush_workqueue(iwm->rx_wq);
iwm_link_off(iwm);
}
void iwm_resetting(struct iwm_priv *iwm)
{
set_bit(IWM_STATUS_RESETTING, &iwm->status);
schedule_work(&iwm->reset_worker);
}
/*
* Notification code:
*
* We're faced with the following issue: Any host command can
* have an answer or not, and if there's an answer to expect,
* it can be treated synchronously or asynchronously.
* To work around the synchronous answer case, we implemented
* our notification mechanism.
* When a code path needs to wait for a command response
* synchronously, it calls notif_handle(), which waits for the
* right notification to show up, and then process it. Before
* starting to wait, it registered as a waiter for this specific
* answer (by toggling a bit in on of the handler_map), so that
* the rx code knows that it needs to send a notification to the
* waiting processes. It does so by calling iwm_notif_send(),
* which adds the notification to the pending notifications list,
* and then wakes the waiting processes up.
*/
int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size)
{
struct iwm_notif *notif;
notif = kzalloc(sizeof(struct iwm_notif), GFP_KERNEL);
if (!notif) {
IWM_ERR(iwm, "Couldn't alloc memory for notification\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&notif->pending);
notif->cmd = cmd;
notif->cmd_id = cmd_id;
notif->src = source;
notif->buf = kzalloc(buf_size, GFP_KERNEL);
if (!notif->buf) {
IWM_ERR(iwm, "Couldn't alloc notification buffer\n");
kfree(notif);
return -ENOMEM;
}
notif->buf_size = buf_size;
memcpy(notif->buf, buf, buf_size);
list_add_tail(&notif->pending, &iwm->pending_notif);
wake_up_interruptible(&iwm->notif_queue);
return 0;
}
static struct iwm_notif *iwm_notif_find(struct iwm_priv *iwm, u32 cmd,
u8 source)
{
struct iwm_notif *notif;
list_for_each_entry(notif, &iwm->pending_notif, pending) {
if ((notif->cmd_id == cmd) && (notif->src == source)) {
list_del(&notif->pending);
return notif;
}
}
return NULL;
}
static struct iwm_notif *iwm_notif_wait(struct iwm_priv *iwm, u32 cmd,
u8 source, long timeout)
{
int ret;
struct iwm_notif *notif;
unsigned long *map = NULL;
switch (source) {
case IWM_SRC_LMAC:
map = &iwm->lmac_handler_map[0];
break;
case IWM_SRC_UMAC:
map = &iwm->umac_handler_map[0];
break;
case IWM_SRC_UDMA:
map = &iwm->udma_handler_map[0];
break;
}
set_bit(cmd, map);
ret = wait_event_interruptible_timeout(iwm->notif_queue,
((notif = iwm_notif_find(iwm, cmd, source)) != NULL),
timeout);
clear_bit(cmd, map);
if (!ret)
return NULL;
return notif;
}
int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout)
{
int ret;
struct iwm_notif *notif;
notif = iwm_notif_wait(iwm, cmd, source, timeout);
if (!notif)
return -ETIME;
ret = iwm_rx_handle_resp(iwm, notif->buf, notif->buf_size, notif->cmd);
kfree(notif->buf);
kfree(notif);
return ret;
}
static int iwm_config_boot_params(struct iwm_priv *iwm)
{
struct iwm_udma_nonwifi_cmd target_cmd;
int ret;
/* check Wimax is off and config debug monitor */
if (!modparam_wimax_enable) {
u32 data1 = 0x1f;
u32 addr1 = 0x606BE258;
u32 data2_set = 0x0;
u32 data2_clr = 0x1;
u32 addr2 = 0x606BE100;
u32 data3 = 0x1;
u32 addr3 = 0x606BEC00;
target_cmd.resp = 0;
target_cmd.handle_by_hw = 0;
target_cmd.eop = 1;
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
target_cmd.addr = cpu_to_le32(addr1);
target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
target_cmd.op2 = 0;
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
if (ret < 0) {
IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
return ret;
}
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE;
target_cmd.addr = cpu_to_le32(addr2);
target_cmd.op1_sz = cpu_to_le32(data2_set);
target_cmd.op2 = cpu_to_le32(data2_clr);
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
if (ret < 0) {
IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
return ret;
}
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
target_cmd.addr = cpu_to_le32(addr3);
target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
target_cmd.op2 = 0;
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data3);
if (ret < 0) {
IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
return ret;
}
}
return 0;
}
void iwm_init_default_profile(struct iwm_priv *iwm,
struct iwm_umac_profile *profile)
{
memset(profile, 0, sizeof(struct iwm_umac_profile));
profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE;
profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE;
if (iwm->conf.enable_qos)
profile->flags |= cpu_to_le16(UMAC_PROFILE_QOS_ALLOWED);
profile->wireless_mode = iwm->conf.wireless_mode;
profile->mode = cpu_to_le32(iwm->conf.mode);
profile->ibss.atim = 0;
profile->ibss.beacon_interval = 100;
profile->ibss.join_only = 0;
profile->ibss.band = iwm->conf.ibss_band;
profile->ibss.channel = iwm->conf.ibss_channel;
}
void iwm_link_on(struct iwm_priv *iwm)
{
netif_carrier_on(iwm_to_ndev(iwm));
netif_tx_wake_all_queues(iwm_to_ndev(iwm));
iwm_send_umac_stats_req(iwm, 0);
}
void iwm_link_off(struct iwm_priv *iwm)
{
struct iw_statistics *wstats = &iwm->wstats;
int i;
netif_tx_stop_all_queues(iwm_to_ndev(iwm));
netif_carrier_off(iwm_to_ndev(iwm));
for (i = 0; i < IWM_TX_QUEUES; i++) {
skb_queue_purge(&iwm->txq[i].queue);
skb_queue_purge(&iwm->txq[i].stopped_queue);
iwm->txq[i].concat_count = 0;
iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf;
flush_workqueue(iwm->txq[i].wq);
}
iwm_rx_free(iwm);
cancel_delayed_work_sync(&iwm->stats_request);
memset(wstats, 0, sizeof(struct iw_statistics));
wstats->qual.updated = IW_QUAL_ALL_INVALID;
kfree(iwm->req_ie);
iwm->req_ie = NULL;
iwm->req_ie_len = 0;
kfree(iwm->resp_ie);
iwm->resp_ie = NULL;
iwm->resp_ie_len = 0;
del_timer_sync(&iwm->watchdog);
}
static void iwm_bss_list_clean(struct iwm_priv *iwm)
{
struct iwm_bss_info *bss, *next;
list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
list_del(&bss->node);
kfree(bss->bss);
kfree(bss);
}
}
static int iwm_channels_init(struct iwm_priv *iwm)
{
int ret;
ret = iwm_send_umac_channel_list(iwm);
if (ret) {
IWM_ERR(iwm, "Send channel list failed\n");
return ret;
}
ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST,
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Didn't get a channel list notification\n");
return ret;
}
return 0;
}
static int __iwm_up(struct iwm_priv *iwm)
{
int ret;
struct iwm_notif *notif_reboot, *notif_ack = NULL;
struct wiphy *wiphy = iwm_to_wiphy(iwm);
u32 wireless_mode;
ret = iwm_bus_enable(iwm);
if (ret) {
IWM_ERR(iwm, "Couldn't enable function\n");
return ret;
}
iwm_rx_setup_handlers(iwm);
/* Wait for initial BARKER_REBOOT from hardware */
notif_reboot = iwm_notif_wait(iwm, IWM_BARKER_REBOOT_NOTIFICATION,
IWM_SRC_UDMA, 2 * HZ);
if (!notif_reboot) {
IWM_ERR(iwm, "Wait for REBOOT_BARKER timeout\n");
goto err_disable;
}
/* We send the barker back */
ret = iwm_bus_send_chunk(iwm, notif_reboot->buf, 16);
if (ret) {
IWM_ERR(iwm, "REBOOT barker response failed\n");
kfree(notif_reboot);
goto err_disable;
}
kfree(notif_reboot->buf);
kfree(notif_reboot);
/* Wait for ACK_BARKER from hardware */
notif_ack = iwm_notif_wait(iwm, IWM_ACK_BARKER_NOTIFICATION,
IWM_SRC_UDMA, 2 * HZ);
if (!notif_ack) {
IWM_ERR(iwm, "Wait for ACK_BARKER timeout\n");
goto err_disable;
}
kfree(notif_ack->buf);
kfree(notif_ack);
/* We start to config static boot parameters */
ret = iwm_config_boot_params(iwm);
if (ret) {
IWM_ERR(iwm, "Config boot parameters failed\n");
goto err_disable;
}
ret = iwm_read_mac(iwm, iwm_to_ndev(iwm)->dev_addr);
if (ret) {
IWM_ERR(iwm, "MAC reading failed\n");
goto err_disable;
}
memcpy(iwm_to_ndev(iwm)->perm_addr, iwm_to_ndev(iwm)->dev_addr,
ETH_ALEN);
/* We can load the FWs */
ret = iwm_load_fw(iwm);
if (ret) {
IWM_ERR(iwm, "FW loading failed\n");
goto err_disable;
}
ret = iwm_eeprom_fat_channels(iwm);
if (ret) {
IWM_ERR(iwm, "Couldnt read HT channels EEPROM entries\n");
goto err_fw;
}
/*
* Read our SKU capabilities.
* If it's valid, we AND the configured wireless mode with the
* device EEPROM value as the current profile wireless mode.
*/
wireless_mode = iwm_eeprom_wireless_mode(iwm);
if (wireless_mode) {
iwm->conf.wireless_mode &= wireless_mode;
if (iwm->umac_profile)
iwm->umac_profile->wireless_mode =
iwm->conf.wireless_mode;
} else
IWM_ERR(iwm, "Wrong SKU capabilities: 0x%x\n",
*((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP)));
snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "L%s_U%s",
iwm->lmac_version, iwm->umac_version);
/* We configure the UMAC and enable the wifi module */
ret = iwm_send_umac_config(iwm,
cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) |
cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_LINK_EN) |
cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_MLME_EN));
if (ret) {
IWM_ERR(iwm, "UMAC config failed\n");
goto err_fw;
}
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Didn't get a wifi core status notification\n");
goto err_fw;
}
if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
IWM_DBG_BOOT(iwm, DBG, "Not all cores enabled:0x%x\n",
iwm->core_enabled);
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
if (ret) {
IWM_ERR(iwm, "Didn't get a core status notification\n");
goto err_fw;
}
if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
IWM_ERR(iwm, "Not all cores enabled: 0x%x\n",
iwm->core_enabled);
goto err_fw;
} else {
IWM_INFO(iwm, "All cores enabled\n");
}
}
ret = iwm_channels_init(iwm);
if (ret < 0) {
IWM_ERR(iwm, "Couldn't init channels\n");
goto err_fw;
}
/* Set the READY bit to indicate interface is brought up successfully */
set_bit(IWM_STATUS_READY, &iwm->status);
return 0;
err_fw:
iwm_eeprom_exit(iwm);
err_disable:
ret = iwm_bus_disable(iwm);
if (ret < 0)
IWM_ERR(iwm, "Couldn't disable function\n");
return -EIO;
}
int iwm_up(struct iwm_priv *iwm)
{
int ret;
mutex_lock(&iwm->mutex);
ret = __iwm_up(iwm);
mutex_unlock(&iwm->mutex);
return ret;
}
static int __iwm_down(struct iwm_priv *iwm)
{
int ret;
/* The interface is already down */
if (!test_bit(IWM_STATUS_READY, &iwm->status))
return 0;
if (iwm->scan_request) {
cfg80211_scan_done(iwm->scan_request, true);
iwm->scan_request = NULL;
}
clear_bit(IWM_STATUS_READY, &iwm->status);
iwm_eeprom_exit(iwm);
iwm_bss_list_clean(iwm);
iwm_init_default_profile(iwm, iwm->umac_profile);
iwm->umac_profile_active = false;
iwm->default_key = -1;
iwm->core_enabled = 0;
ret = iwm_bus_disable(iwm);
if (ret < 0) {
IWM_ERR(iwm, "Couldn't disable function\n");
return ret;
}
return 0;
}
int iwm_down(struct iwm_priv *iwm)
{
int ret;
mutex_lock(&iwm->mutex);
ret = __iwm_down(iwm);
mutex_unlock(&iwm->mutex);
return ret;
}

View File

@ -1,191 +0,0 @@
/*
* 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 <linux/slab.h>
#include "iwm.h"
#include "commands.h"
#include "cfg80211.h"
#include "debug.h"
static int iwm_open(struct net_device *ndev)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
return iwm_up(iwm);
}
static int iwm_stop(struct net_device *ndev)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);
return iwm_down(iwm);
}
/*
* 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 };
int iwm_tid_to_queue(u16 tid)
{
if (tid > IWM_UMAC_TID_NR - 2)
return -EINVAL;
return iwm_1d_to_queue[tid];
}
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 (IS_ERR(wdev))
return wdev;
iwm = wdev_to_iwm(wdev);
iwm->bus_ops = if_ops;
iwm->wdev = wdev;
ret = iwm_priv_init(iwm);
if (ret) {
dev_err(dev, "failed to init iwm_priv\n");
goto out_wdev;
}
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");
ret = -ENOMEM;
goto out_priv;
}
ndev->netdev_ops = &iwm_netdev_ops;
ndev->ieee80211_ptr = wdev;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile),
GFP_KERNEL);
if (!iwm->umac_profile) {
dev_err(dev, "Couldn't alloc memory for profile\n");
ret = -ENOMEM;
goto out_profile;
}
iwm_init_default_profile(iwm, iwm->umac_profile);
return iwm;
out_profile:
free_netdev(ndev);
out_priv:
iwm_priv_deinit(iwm);
out_wdev:
iwm_wdev_free(iwm);
return ERR_PTR(ret);
}
void iwm_if_free(struct iwm_priv *iwm)
{
if (!iwm_to_ndev(iwm))
return;
cancel_delayed_work_sync(&iwm->ct_kill_delay);
free_netdev(iwm_to_ndev(iwm));
iwm_priv_deinit(iwm);
kfree(iwm->umac_profile);
iwm->umac_profile = NULL;
iwm_wdev_free(iwm);
}
int iwm_if_add(struct iwm_priv *iwm)
{
struct net_device *ndev = iwm_to_ndev(iwm);
int ret;
ret = register_netdev(ndev);
if (ret < 0) {
dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret);
return ret;
}
return 0;
}
void iwm_if_remove(struct iwm_priv *iwm)
{
unregister_netdev(iwm_to_ndev(iwm));
}

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +0,0 @@
/*
* 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

View File

@ -1,509 +0,0 @@
/*
* 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>
*
*/
/*
* This is the SDIO bus specific hooks for iwm.
* It also is the module's entry point.
*
* Interesting code paths:
* iwm_sdio_probe() (Called by an SDIO bus scan)
* -> iwm_if_alloc() (netdev.c)
* -> iwm_wdev_alloc() (cfg80211.c, allocates and register our wiphy)
* -> wiphy_new()
* -> wiphy_register()
* -> alloc_netdev_mq()
* -> register_netdev()
*
* iwm_sdio_remove()
* -> iwm_if_free() (netdev.c)
* -> unregister_netdev()
* -> iwm_wdev_free() (cfg80211.c)
* -> wiphy_unregister()
* -> wiphy_free()
*
* iwm_sdio_isr() (called in process context from the SDIO core code)
* -> queue_work(.., isr_worker)
* -- [async] --> iwm_sdio_isr_worker()
* -> iwm_rx_handle()
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/debugfs.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "iwm.h"
#include "debug.h"
#include "bus.h"
#include "sdio.h"
static void iwm_sdio_isr_worker(struct work_struct *work)
{
struct iwm_sdio_priv *hw;
struct iwm_priv *iwm;
struct iwm_rx_info *rx_info;
struct sk_buff *skb;
u8 *rx_buf;
unsigned long rx_size;
hw = container_of(work, struct iwm_sdio_priv, isr_worker);
iwm = hw_to_iwm(hw);
while (!skb_queue_empty(&iwm->rx_list)) {
skb = skb_dequeue(&iwm->rx_list);
rx_info = skb_to_rx_info(skb);
rx_size = rx_info->rx_size;
rx_buf = skb->data;
IWM_HEXDUMP(iwm, DBG, SDIO, "RX: ", rx_buf, rx_size);
if (iwm_rx_handle(iwm, rx_buf, rx_size) < 0)
IWM_WARN(iwm, "RX error\n");
kfree_skb(skb);
}
}
static void iwm_sdio_isr(struct sdio_func *func)
{
struct iwm_priv *iwm;
struct iwm_sdio_priv *hw;
struct iwm_rx_info *rx_info;
struct sk_buff *skb;
unsigned long buf_size, read_size;
int ret;
u8 val;
hw = sdio_get_drvdata(func);
iwm = hw_to_iwm(hw);
buf_size = hw->blk_size;
/* We're checking the status */
val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret);
if (val == 0 || ret < 0) {
IWM_ERR(iwm, "Wrong INTR_STATUS\n");
return;
}
/* See if we have free buffers */
if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) {
IWM_ERR(iwm, "No buffer for more Rx frames\n");
return;
}
/* We first read the transaction size */
read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret);
read_size = read_size << 8;
if (ret < 0) {
IWM_ERR(iwm, "Couldn't read the xfer size\n");
return;
}
/* We need to clear the INT register */
sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret);
if (ret < 0) {
IWM_ERR(iwm, "Couldn't clear the INT register\n");
return;
}
while (buf_size < read_size)
buf_size <<= 1;
skb = dev_alloc_skb(buf_size);
if (!skb) {
IWM_ERR(iwm, "Couldn't alloc RX skb\n");
return;
}
rx_info = skb_to_rx_info(skb);
rx_info->rx_size = read_size;
rx_info->rx_buf_size = buf_size;
/* Now we can read the actual buffer */
ret = sdio_memcpy_fromio(func, skb_put(skb, read_size),
IWM_SDIO_DATA_ADDR, read_size);
/* The skb is put on a driver's specific Rx SKB list */
skb_queue_tail(&iwm->rx_list, skb);
/* We can now schedule the actual worker */
queue_work(hw->isr_wq, &hw->isr_worker);
}
static void iwm_sdio_rx_free(struct iwm_sdio_priv *hw)
{
struct iwm_priv *iwm = hw_to_iwm(hw);
flush_workqueue(hw->isr_wq);
skb_queue_purge(&iwm->rx_list);
}
/* Bus ops */
static int if_sdio_enable(struct iwm_priv *iwm)
{
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
int ret;
sdio_claim_host(hw->func);
ret = sdio_enable_func(hw->func);
if (ret) {
IWM_ERR(iwm, "Couldn't enable the device: is TOP driver "
"loaded and functional?\n");
goto release_host;
}
iwm_reset(iwm);
ret = sdio_claim_irq(hw->func, iwm_sdio_isr);
if (ret) {
IWM_ERR(iwm, "Failed to claim irq: %d\n", ret);
goto release_host;
}
sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
if (ret < 0) {
IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret);
goto release_irq;
}
sdio_release_host(hw->func);
IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n");
return 0;
release_irq:
sdio_release_irq(hw->func);
release_host:
sdio_release_host(hw->func);
return ret;
}
static int if_sdio_disable(struct iwm_priv *iwm)
{
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
int ret;
sdio_claim_host(hw->func);
sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
if (ret < 0)
IWM_WARN(iwm, "Couldn't disable INTR: %d\n", ret);
sdio_release_irq(hw->func);
sdio_disable_func(hw->func);
sdio_release_host(hw->func);
iwm_sdio_rx_free(hw);
iwm_reset(iwm);
IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n");
return 0;
}
static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
{
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
int aligned_count = ALIGN(count, hw->blk_size);
int ret;
if ((unsigned long)buf & 0x3) {
IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf);
/* TODO: Is this a hardware limitation? use get_unligned */
return -EINVAL;
}
sdio_claim_host(hw->func);
ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf,
aligned_count);
sdio_release_host(hw->func);
return ret;
}
static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct iwm_priv *iwm = filp->private_data;
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
char *buf;
u8 cccr;
int buf_len = 4096, ret;
size_t len = 0;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
sdio_claim_host(hw->func);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_IOEx: 0x%x\n", cccr);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_IORx: 0x%x\n", cccr);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_IENx: 0x%x\n", cccr);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_INTx: 0x%x\n", cccr);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_IF: 0x%x\n", cccr);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_CAPS: 0x%x\n", cccr);
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret);
if (ret) {
IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n");
goto err;
}
len += snprintf(buf + len, buf_len - len, "CCCR_CIS: 0x%x\n", cccr);
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
err:
sdio_release_host(hw->func);
kfree(buf);
return ret;
}
static const struct file_operations iwm_debugfs_sdio_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = iwm_debugfs_sdio_read,
.llseek = default_llseek,
};
static void if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir)
{
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
hw->cccr_dentry = debugfs_create_file("cccr", 0200,
parent_dir, iwm,
&iwm_debugfs_sdio_fops);
}
static void if_sdio_debugfs_exit(struct iwm_priv *iwm)
{
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
debugfs_remove(hw->cccr_dentry);
}
static struct iwm_if_ops if_sdio_ops = {
.enable = if_sdio_enable,
.disable = if_sdio_disable,
.send_chunk = if_sdio_send_chunk,
.debugfs_init = if_sdio_debugfs_init,
.debugfs_exit = if_sdio_debugfs_exit,
.umac_name = "iwmc3200wifi-umac-sdio.bin",
.calib_lmac_name = "iwmc3200wifi-calib-sdio.bin",
.lmac_name = "iwmc3200wifi-lmac-sdio.bin",
};
MODULE_FIRMWARE("iwmc3200wifi-umac-sdio.bin");
MODULE_FIRMWARE("iwmc3200wifi-calib-sdio.bin");
MODULE_FIRMWARE("iwmc3200wifi-lmac-sdio.bin");
static int iwm_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct iwm_priv *iwm;
struct iwm_sdio_priv *hw;
struct device *dev = &func->dev;
int ret;
/* check if TOP has already initialized the card */
sdio_claim_host(func);
ret = sdio_enable_func(func);
if (ret) {
dev_err(dev, "wait for TOP to enable the device\n");
sdio_release_host(func);
return ret;
}
ret = sdio_set_block_size(func, IWM_SDIO_BLK_SIZE);
sdio_disable_func(func);
sdio_release_host(func);
if (ret < 0) {
dev_err(dev, "Failed to set block size: %d\n", ret);
return ret;
}
iwm = iwm_if_alloc(sizeof(struct iwm_sdio_priv), dev, &if_sdio_ops);
if (IS_ERR(iwm)) {
dev_err(dev, "allocate SDIO interface failed\n");
return PTR_ERR(iwm);
}
hw = iwm_private(iwm);
hw->iwm = iwm;
iwm_debugfs_init(iwm);
sdio_set_drvdata(func, hw);
hw->func = func;
hw->blk_size = IWM_SDIO_BLK_SIZE;
hw->isr_wq = create_singlethread_workqueue(KBUILD_MODNAME "_sdio");
if (!hw->isr_wq) {
ret = -ENOMEM;
goto debugfs_exit;
}
INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
ret = iwm_if_add(iwm);
if (ret) {
dev_err(dev, "add SDIO interface failed\n");
goto destroy_wq;
}
dev_info(dev, "IWM SDIO probe\n");
return 0;
destroy_wq:
destroy_workqueue(hw->isr_wq);
debugfs_exit:
iwm_debugfs_exit(iwm);
iwm_if_free(iwm);
return ret;
}
static void iwm_sdio_remove(struct sdio_func *func)
{
struct iwm_sdio_priv *hw = sdio_get_drvdata(func);
struct iwm_priv *iwm = hw_to_iwm(hw);
struct device *dev = &func->dev;
iwm_if_remove(iwm);
destroy_workqueue(hw->isr_wq);
iwm_debugfs_exit(iwm);
iwm_if_free(iwm);
sdio_set_drvdata(func, NULL);
dev_info(dev, "IWM SDIO remove\n");
}
static const struct sdio_device_id iwm_sdio_ids[] = {
/* Global/AGN SKU */
{ SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1403) },
/* BGN SKU */
{ SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1408) },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids);
static struct sdio_driver iwm_sdio_driver = {
.name = "iwm_sdio",
.id_table = iwm_sdio_ids,
.probe = iwm_sdio_probe,
.remove = iwm_sdio_remove,
};
static int __init iwm_sdio_init_module(void)
{
return sdio_register_driver(&iwm_sdio_driver);
}
static void __exit iwm_sdio_exit_module(void)
{
sdio_unregister_driver(&iwm_sdio_driver);
}
module_init(iwm_sdio_init_module);
module_exit(iwm_sdio_exit_module);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(IWM_COPYRIGHT " " IWM_AUTHOR);

View File

@ -1,64 +0,0 @@
/*
* 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_SDIO_H__
#define __IWM_SDIO_H__
#define IWM_SDIO_DATA_ADDR 0x0
#define IWM_SDIO_INTR_ENABLE_ADDR 0x14
#define IWM_SDIO_INTR_STATUS_ADDR 0x13
#define IWM_SDIO_INTR_CLEAR_ADDR 0x13
#define IWM_SDIO_INTR_GET_SIZE_ADDR 0x2C
#define IWM_SDIO_BLK_SIZE 256
#define iwm_to_if_sdio(i) (struct iwm_sdio_priv *)(iwm->private)
struct iwm_sdio_priv {
struct sdio_func *func;
struct iwm_priv *iwm;
struct workqueue_struct *isr_wq;
struct work_struct isr_worker;
struct dentry *cccr_dentry;
unsigned int blk_size;
};
#endif

View File

@ -1,3 +0,0 @@
#include "iwm.h"
#define CREATE_TRACE_POINTS
#include "trace.h"

View File

@ -1,283 +0,0 @@
#if !defined(__IWM_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ)
#define __IWM_TRACE_H__
#include <linux/tracepoint.h>
#if !defined(CONFIG_IWM_TRACING)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
static inline void trace_ ## name(proto) {}
#endif
#undef TRACE_SYSTEM
#define TRACE_SYSTEM iwm
#define IWM_ENTRY __array(char, ndev_name, 16)
#define IWM_ASSIGN strlcpy(__entry->ndev_name, iwm_to_ndev(iwm)->name, 16)
#define IWM_PR_FMT "%s"
#define IWM_PR_ARG __entry->ndev_name
TRACE_EVENT(iwm_tx_nonwifi_cmd,
TP_PROTO(struct iwm_priv *iwm, struct iwm_udma_out_nonwifi_hdr *hdr),
TP_ARGS(iwm, hdr),
TP_STRUCT__entry(
IWM_ENTRY
__field(u8, opcode)
__field(u8, resp)
__field(u8, eot)
__field(u8, hw)
__field(u16, seq)
__field(u32, addr)
__field(u32, op1)
__field(u32, op2)
),
TP_fast_assign(
IWM_ASSIGN;
__entry->opcode = GET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE);
__entry->resp = GET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP);
__entry->eot = GET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT);
__entry->hw = GET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW);
__entry->seq = GET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM);
__entry->addr = le32_to_cpu(hdr->addr);
__entry->op1 = le32_to_cpu(hdr->op1_sz);
__entry->op2 = le32_to_cpu(hdr->op2);
),
TP_printk(
IWM_PR_FMT " Tx TARGET CMD: opcode 0x%x, resp %d, eot %d, "
"hw %d, seq 0x%x, addr 0x%x, op1 0x%x, op2 0x%x",
IWM_PR_ARG, __entry->opcode, __entry->resp, __entry->eot,
__entry->hw, __entry->seq, __entry->addr, __entry->op1,
__entry->op2
)
);
TRACE_EVENT(iwm_tx_wifi_cmd,
TP_PROTO(struct iwm_priv *iwm, struct iwm_umac_wifi_out_hdr *hdr),
TP_ARGS(iwm, hdr),
TP_STRUCT__entry(
IWM_ENTRY
__field(u8, opcode)
__field(u8, lmac)
__field(u8, resp)
__field(u8, eot)
__field(u8, ra_tid)
__field(u8, credit_group)
__field(u8, color)
__field(u16, seq)
),
TP_fast_assign(
IWM_ASSIGN;
__entry->opcode = hdr->sw_hdr.cmd.cmd;
__entry->lmac = 0;
__entry->seq = __le16_to_cpu(hdr->sw_hdr.cmd.seq_num);
__entry->resp = GET_VAL8(hdr->sw_hdr.cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ);
__entry->color = GET_VAL32(hdr->sw_hdr.meta_data, UMAC_FW_CMD_TX_STA_COLOR);
__entry->eot = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_OUT_CMD_EOT);
__entry->ra_tid = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_RATID);
__entry->credit_group = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_CREDIT_GRP);
if (__entry->opcode == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH ||
__entry->opcode == UMAC_CMD_OPCODE_WIFI_IF_WRAPPER) {
__entry->lmac = 1;
__entry->opcode = ((struct iwm_lmac_hdr *)(hdr + 1))->id;
}
),
TP_printk(
IWM_PR_FMT " Tx %cMAC CMD: opcode 0x%x, resp %d, eot %d, "
"seq 0x%x, sta_color 0x%x, ra_tid 0x%x, credit_group 0x%x",
IWM_PR_ARG, __entry->lmac ? 'L' : 'U', __entry->opcode,
__entry->resp, __entry->eot, __entry->seq, __entry->color,
__entry->ra_tid, __entry->credit_group
)
);
TRACE_EVENT(iwm_tx_packets,
TP_PROTO(struct iwm_priv *iwm, u8 *buf, int len),
TP_ARGS(iwm, buf, len),
TP_STRUCT__entry(
IWM_ENTRY
__field(u8, eot)
__field(u8, ra_tid)
__field(u8, credit_group)
__field(u8, color)
__field(u16, seq)
__field(u8, npkt)
__field(u32, bytes)
),
TP_fast_assign(
struct iwm_umac_wifi_out_hdr *hdr =
(struct iwm_umac_wifi_out_hdr *)buf;
IWM_ASSIGN;
__entry->eot = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_OUT_CMD_EOT);
__entry->ra_tid = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_RATID);
__entry->credit_group = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_CREDIT_GRP);
__entry->color = GET_VAL32(hdr->sw_hdr.meta_data, UMAC_FW_CMD_TX_STA_COLOR);
__entry->seq = __le16_to_cpu(hdr->sw_hdr.cmd.seq_num);
__entry->npkt = 1;
__entry->bytes = len;
if (!__entry->eot) {
int count;
u8 *ptr = buf;
__entry->npkt = 0;
while (ptr < buf + len) {
count = GET_VAL32(hdr->sw_hdr.meta_data,
UMAC_FW_CMD_BYTE_COUNT);
ptr += ALIGN(sizeof(*hdr) + count, 16);
hdr = (struct iwm_umac_wifi_out_hdr *)ptr;
__entry->npkt++;
}
}
),
TP_printk(
IWM_PR_FMT " Tx %spacket: eot %d, seq 0x%x, sta_color 0x%x, "
"ra_tid 0x%x, credit_group 0x%x, embedded_packets %d, %d bytes",
IWM_PR_ARG, !__entry->eot ? "concatenated " : "",
__entry->eot, __entry->seq, __entry->color, __entry->ra_tid,
__entry->credit_group, __entry->npkt, __entry->bytes
)
);
TRACE_EVENT(iwm_rx_nonwifi_cmd,
TP_PROTO(struct iwm_priv *iwm, void *buf, int len),
TP_ARGS(iwm, buf, len),
TP_STRUCT__entry(
IWM_ENTRY
__field(u8, opcode)
__field(u16, seq)
__field(u32, len)
),
TP_fast_assign(
struct iwm_udma_in_hdr *hdr = buf;
IWM_ASSIGN;
__entry->opcode = GET_VAL32(hdr->cmd, UDMA_HDI_IN_NW_CMD_OPCODE);
__entry->seq = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM);
__entry->len = len;
),
TP_printk(
IWM_PR_FMT " Rx TARGET RESP: opcode 0x%x, seq 0x%x, len 0x%x",
IWM_PR_ARG, __entry->opcode, __entry->seq, __entry->len
)
);
TRACE_EVENT(iwm_rx_wifi_cmd,
TP_PROTO(struct iwm_priv *iwm, struct iwm_umac_wifi_in_hdr *hdr),
TP_ARGS(iwm, hdr),
TP_STRUCT__entry(
IWM_ENTRY
__field(u8, cmd)
__field(u8, source)
__field(u16, seq)
__field(u32, count)
),
TP_fast_assign(
IWM_ASSIGN;
__entry->cmd = hdr->sw_hdr.cmd.cmd;
__entry->source = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
__entry->count = GET_VAL32(hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT);
__entry->seq = le16_to_cpu(hdr->sw_hdr.cmd.seq_num);
),
TP_printk(
IWM_PR_FMT " Rx %s RESP: cmd 0x%x, seq 0x%x, count 0x%x",
IWM_PR_ARG, __entry->source == UMAC_HDI_IN_SOURCE_FHRX ? "LMAC" :
__entry->source == UMAC_HDI_IN_SOURCE_FW ? "UMAC" : "UDMA",
__entry->cmd, __entry->seq, __entry->count
)
);
#define iwm_ticket_action_symbol \
{ IWM_RX_TICKET_DROP, "DROP" }, \
{ IWM_RX_TICKET_RELEASE, "RELEASE" }, \
{ IWM_RX_TICKET_SNIFFER, "SNIFFER" }, \
{ IWM_RX_TICKET_ENQUEUE, "ENQUEUE" }
TRACE_EVENT(iwm_rx_ticket,
TP_PROTO(struct iwm_priv *iwm, void *buf, int len),
TP_ARGS(iwm, buf, len),
TP_STRUCT__entry(
IWM_ENTRY
__field(u8, action)
__field(u8, reason)
__field(u16, id)
__field(u16, flags)
),
TP_fast_assign(
struct iwm_rx_ticket *ticket =
((struct iwm_umac_notif_rx_ticket *)buf)->tickets;
IWM_ASSIGN;
__entry->id = le16_to_cpu(ticket->id);
__entry->action = le16_to_cpu(ticket->action);
__entry->flags = le16_to_cpu(ticket->flags);
__entry->reason = (__entry->flags & IWM_RX_TICKET_DROP_REASON_MSK) >> IWM_RX_TICKET_DROP_REASON_POS;
),
TP_printk(
IWM_PR_FMT " Rx ticket: id 0x%x, action %s, %s 0x%x%s",
IWM_PR_ARG, __entry->id,
__print_symbolic(__entry->action, iwm_ticket_action_symbol),
__entry->reason ? "reason" : "flags",
__entry->reason ? __entry->reason : __entry->flags,
__entry->flags & IWM_RX_TICKET_AMSDU_MSK ? ", AMSDU frame" : ""
)
);
TRACE_EVENT(iwm_rx_packet,
TP_PROTO(struct iwm_priv *iwm, void *buf, int len),
TP_ARGS(iwm, buf, len),
TP_STRUCT__entry(
IWM_ENTRY
__field(u8, source)
__field(u16, id)
__field(u32, len)
),
TP_fast_assign(
struct iwm_umac_wifi_in_hdr *hdr = buf;
IWM_ASSIGN;
__entry->source = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
__entry->id = le16_to_cpu(hdr->sw_hdr.cmd.seq_num);
__entry->len = len - sizeof(*hdr);
),
TP_printk(
IWM_PR_FMT " Rx %s packet: id 0x%x, %d bytes",
IWM_PR_ARG, __entry->source == UMAC_HDI_IN_SOURCE_FHRX ?
"LMAC" : "UMAC", __entry->id, __entry->len
)
);
#endif
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>

View File

@ -1,529 +0,0 @@
/*
* 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>
*
*/
/*
* iwm Tx theory of operation:
*
* 1) We receive a 802.3 frame from the stack
* 2) We convert it to a 802.11 frame [iwm_xmit_frame]
* 3) We queue it to its corresponding tx queue [iwm_xmit_frame]
* 4) We schedule the tx worker. There is one worker per tx
* queue. [iwm_xmit_frame]
* 5) The tx worker is scheduled
* 6) We go through every queued skb on the tx queue, and for each
* and every one of them: [iwm_tx_worker]
* a) We check if we have enough Tx credits (see below for a Tx
* credits description) for the frame length. [iwm_tx_worker]
* b) If we do, we aggregate the Tx frame into a UDMA one, by
* concatenating one REPLY_TX command per Tx frame. [iwm_tx_worker]
* c) When we run out of credits, or when we reach the maximum
* concatenation size, we actually send the concatenated UDMA
* frame. [iwm_tx_worker]
*
* When we run out of Tx credits, the skbs are filling the tx queue,
* and eventually we will stop the netdev queue. [iwm_tx_worker]
* The tx queue is emptied as we're getting new tx credits, by
* scheduling the tx_worker. [iwm_tx_credit_inc]
* The netdev queue is started again when we have enough tx credits,
* and when our tx queue has some reasonable amout of space available
* (i.e. half of the max size). [iwm_tx_worker]
*/
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/ieee80211.h>
#include "iwm.h"
#include "debug.h"
#include "commands.h"
#include "hal.h"
#include "umac.h"
#include "bus.h"
#define IWM_UMAC_PAGE_ALLOC_WRAP 0xffff
#define BYTES_TO_PAGES(n) (1 + ((n) >> ilog2(IWM_UMAC_PAGE_SIZE)) - \
(((n) & (IWM_UMAC_PAGE_SIZE - 1)) == 0))
#define pool_id_to_queue(id) ((id < IWM_TX_CMD_QUEUE) ? id : id - 1)
#define queue_to_pool_id(q) ((q < IWM_TX_CMD_QUEUE) ? q : q + 1)
/* require to hold tx_credit lock */
static int iwm_tx_credit_get(struct iwm_tx_credit *tx_credit, int id)
{
struct pool_entry *pool = &tx_credit->pools[id];
struct spool_entry *spool = &tx_credit->spools[pool->sid];
int spool_pages;
/* number of pages can be taken from spool by this pool */
spool_pages = spool->max_pages - spool->alloc_pages +
max(pool->min_pages - pool->alloc_pages, 0);
return min(pool->max_pages - pool->alloc_pages, spool_pages);
}
static bool iwm_tx_credit_ok(struct iwm_priv *iwm, int id, int nb)
{
u32 npages = BYTES_TO_PAGES(nb);
if (npages <= iwm_tx_credit_get(&iwm->tx_credit, id))
return 1;
set_bit(id, &iwm->tx_credit.full_pools_map);
IWM_DBG_TX(iwm, DBG, "LINK: stop txq[%d], available credit: %d\n",
pool_id_to_queue(id),
iwm_tx_credit_get(&iwm->tx_credit, id));
return 0;
}
void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages)
{
struct pool_entry *pool;
struct spool_entry *spool;
int freed_pages;
int queue;
BUG_ON(id >= IWM_MACS_OUT_GROUPS);
pool = &iwm->tx_credit.pools[id];
spool = &iwm->tx_credit.spools[pool->sid];
freed_pages = total_freed_pages - pool->total_freed_pages;
IWM_DBG_TX(iwm, DBG, "Free %d pages for pool[%d]\n", freed_pages, id);
if (!freed_pages) {
IWM_DBG_TX(iwm, DBG, "No pages are freed by UMAC\n");
return;
} else if (freed_pages < 0)
freed_pages += IWM_UMAC_PAGE_ALLOC_WRAP + 1;
if (pool->alloc_pages > pool->min_pages) {
int spool_pages = pool->alloc_pages - pool->min_pages;
spool_pages = min(spool_pages, freed_pages);
spool->alloc_pages -= spool_pages;
}
pool->alloc_pages -= freed_pages;
pool->total_freed_pages = total_freed_pages;
IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
"Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
pool->total_freed_pages, pool->sid, spool->alloc_pages);
if (test_bit(id, &iwm->tx_credit.full_pools_map) &&
(pool->alloc_pages < pool->max_pages / 2)) {
clear_bit(id, &iwm->tx_credit.full_pools_map);
queue = pool_id_to_queue(id);
IWM_DBG_TX(iwm, DBG, "LINK: start txq[%d], available "
"credit: %d\n", queue,
iwm_tx_credit_get(&iwm->tx_credit, id));
queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
}
}
static void iwm_tx_credit_dec(struct iwm_priv *iwm, int id, int alloc_pages)
{
struct pool_entry *pool;
struct spool_entry *spool;
int spool_pages;
IWM_DBG_TX(iwm, DBG, "Allocate %d pages for pool[%d]\n",
alloc_pages, id);
BUG_ON(id >= IWM_MACS_OUT_GROUPS);
pool = &iwm->tx_credit.pools[id];
spool = &iwm->tx_credit.spools[pool->sid];
spool_pages = pool->alloc_pages + alloc_pages - pool->min_pages;
if (pool->alloc_pages >= pool->min_pages)
spool->alloc_pages += alloc_pages;
else if (spool_pages > 0)
spool->alloc_pages += spool_pages;
pool->alloc_pages += alloc_pages;
IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
"Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
pool->total_freed_pages, pool->sid, spool->alloc_pages);
}
int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb)
{
u32 npages = BYTES_TO_PAGES(nb);
int ret = 0;
spin_lock(&iwm->tx_credit.lock);
if (!iwm_tx_credit_ok(iwm, id, nb)) {
IWM_DBG_TX(iwm, DBG, "No credit available for pool[%d]\n", id);
ret = -ENOSPC;
goto out;
}
iwm_tx_credit_dec(iwm, id, npages);
out:
spin_unlock(&iwm->tx_credit.lock);
return ret;
}
/*
* Since we're on an SDIO or USB bus, we are not sharing memory
* for storing to be transmitted frames. The host needs to push
* them upstream. As a consequence there needs to be a way for
* the target to let us know if it can actually take more TX frames
* or not. This is what Tx credits are for.
*
* For each Tx HW queue, we have a Tx pool, and then we have one
* unique super pool (spool), which is actually a global pool of
* all the UMAC pages.
* For each Tx pool we have a min_pages, a max_pages fields, and a
* alloc_pages fields. The alloc_pages tracks the number of pages
* currently allocated from the tx pool.
* Here are the rules to check if given a tx frame we have enough
* tx credits for it:
* 1) We translate the frame length into a number of UMAC pages.
* Let's call them n_pages.
* 2) For the corresponding tx pool, we check if n_pages +
* pool->alloc_pages is higher than pool->min_pages. min_pages
* represent a set of pre-allocated pages on the tx pool. If
* that's the case, then we need to allocate those pages from
* the spool. We can do so until we reach spool->max_pages.
* 3) Each tx pool is not allowed to allocate more than pool->max_pages
* from the spool, so once we're over min_pages, we can allocate
* pages from the spool, but not more than max_pages.
*
* When the tx code path needs to send a tx frame, it checks first
* if it has enough tx credits, following those rules. [iwm_tx_credit_get]
* If it does, it then updates the pool and spool counters and
* then send the frame. [iwm_tx_credit_alloc and iwm_tx_credit_dec]
* On the other side, when the UMAC is done transmitting frames, it
* will send a credit update notification to the host. This is when
* the pool and spool counters gets to be decreased. [iwm_tx_credit_inc,
* called from rx.c:iwm_ntf_tx_credit_update]
*
*/
void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
struct iwm_umac_notif_alive *alive)
{
int i, sid, pool_pages;
spin_lock(&iwm->tx_credit.lock);
iwm->tx_credit.pool_nr = le16_to_cpu(alive->page_grp_count);
iwm->tx_credit.full_pools_map = 0;
memset(&iwm->tx_credit.spools[0], 0, sizeof(struct spool_entry));
IWM_DBG_TX(iwm, DBG, "Pools number is %d\n", iwm->tx_credit.pool_nr);
for (i = 0; i < iwm->tx_credit.pool_nr; i++) {
__le32 page_grp_state = alive->page_grp_state[i];
iwm->tx_credit.pools[i].id = GET_VAL32(page_grp_state,
UMAC_ALIVE_PAGE_STS_GRP_NUM);
iwm->tx_credit.pools[i].sid = GET_VAL32(page_grp_state,
UMAC_ALIVE_PAGE_STS_SGRP_NUM);
iwm->tx_credit.pools[i].min_pages = GET_VAL32(page_grp_state,
UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE);
iwm->tx_credit.pools[i].max_pages = GET_VAL32(page_grp_state,
UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE);
iwm->tx_credit.pools[i].alloc_pages = 0;
iwm->tx_credit.pools[i].total_freed_pages = 0;
sid = iwm->tx_credit.pools[i].sid;
pool_pages = iwm->tx_credit.pools[i].min_pages;
if (iwm->tx_credit.spools[sid].max_pages == 0) {
iwm->tx_credit.spools[sid].id = sid;
iwm->tx_credit.spools[sid].max_pages =
GET_VAL32(page_grp_state,
UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE);
iwm->tx_credit.spools[sid].alloc_pages = 0;
}
iwm->tx_credit.spools[sid].alloc_pages += pool_pages;
IWM_DBG_TX(iwm, DBG, "Pool idx: %d, id: %d, sid: %d, capacity "
"min: %d, max: %d, pool alloc: %d, total_free: %d, "
"super poll alloc: %d\n",
i, iwm->tx_credit.pools[i].id,
iwm->tx_credit.pools[i].sid,
iwm->tx_credit.pools[i].min_pages,
iwm->tx_credit.pools[i].max_pages,
iwm->tx_credit.pools[i].alloc_pages,
iwm->tx_credit.pools[i].total_freed_pages,
iwm->tx_credit.spools[sid].alloc_pages);
}
spin_unlock(&iwm->tx_credit.lock);
}
#define IWM_UDMA_HDR_LEN sizeof(struct iwm_umac_wifi_out_hdr)
static __le16 iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb,
int pool_id, u8 *buf)
{
struct iwm_umac_wifi_out_hdr *hdr = (struct iwm_umac_wifi_out_hdr *)buf;
struct iwm_udma_wifi_cmd udma_cmd;
struct iwm_umac_cmd umac_cmd;
struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
udma_cmd.count = cpu_to_le16(skb->len +
sizeof(struct iwm_umac_fw_cmd_hdr));
/* set EOP to 0 here. iwm_udma_wifi_hdr_set_eop() will be
* called later to set EOP for the last packet. */
udma_cmd.eop = 0;
udma_cmd.credit_group = pool_id;
udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
udma_cmd.lmac_offset = 0;
umac_cmd.id = REPLY_TX;
umac_cmd.count = cpu_to_le16(skb->len);
umac_cmd.color = tx_info->color;
umac_cmd.resp = 0;
umac_cmd.seq_num = cpu_to_le16(iwm_alloc_wifi_cmd_seq(iwm));
iwm_build_udma_wifi_hdr(iwm, &hdr->hw_hdr, &udma_cmd);
iwm_build_umac_hdr(iwm, &hdr->sw_hdr, &umac_cmd);
memcpy(buf + sizeof(*hdr), skb->data, skb->len);
return umac_cmd.seq_num;
}
static int iwm_tx_send_concat_packets(struct iwm_priv *iwm,
struct iwm_tx_queue *txq)
{
int ret;
if (!txq->concat_count)
return 0;
IWM_DBG_TX(iwm, DBG, "Send concatenated Tx: queue %d, %d bytes\n",
txq->id, txq->concat_count);
/* mark EOP for the last packet */
iwm_udma_wifi_hdr_set_eop(iwm, txq->concat_ptr, 1);
trace_iwm_tx_packets(iwm, txq->concat_buf, txq->concat_count);
ret = iwm_bus_send_chunk(iwm, txq->concat_buf, txq->concat_count);
txq->concat_count = 0;
txq->concat_ptr = txq->concat_buf;
return ret;
}
void iwm_tx_worker(struct work_struct *work)
{
struct iwm_priv *iwm;
struct iwm_tx_info *tx_info = NULL;
struct sk_buff *skb;
struct iwm_tx_queue *txq;
struct iwm_sta_info *sta_info;
struct iwm_tid_info *tid_info;
int cmdlen, ret, pool_id;
txq = container_of(work, struct iwm_tx_queue, worker);
iwm = container_of(txq, struct iwm_priv, txq[txq->id]);
pool_id = queue_to_pool_id(txq->id);
while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
!skb_queue_empty(&txq->queue)) {
spin_lock_bh(&txq->lock);
skb = skb_dequeue(&txq->queue);
spin_unlock_bh(&txq->lock);
tx_info = skb_to_tx_info(skb);
sta_info = &iwm->sta_table[tx_info->sta];
if (!sta_info->valid) {
IWM_ERR(iwm, "Trying to send a frame to unknown STA\n");
kfree_skb(skb);
continue;
}
tid_info = &sta_info->tid_info[tx_info->tid];
mutex_lock(&tid_info->mutex);
/*
* If the RAxTID is stopped, we queue the skb to the stopped
* queue.
* Whenever we'll get a UMAC notification to resume the tx flow
* for this RAxTID, we'll merge back the stopped queue into the
* regular queue. See iwm_ntf_stop_resume_tx() from rx.c.
*/
if (tid_info->stopped) {
IWM_DBG_TX(iwm, DBG, "%dx%d stopped\n",
tx_info->sta, tx_info->tid);
spin_lock_bh(&txq->lock);
skb_queue_tail(&txq->stopped_queue, skb);
spin_unlock_bh(&txq->lock);
mutex_unlock(&tid_info->mutex);
continue;
}
cmdlen = IWM_UDMA_HDR_LEN + skb->len;
IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: "
"%d, color: %d\n", txq->id, skb, tx_info->sta,
tx_info->color);
if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE)
iwm_tx_send_concat_packets(iwm, txq);
ret = iwm_tx_credit_alloc(iwm, pool_id, cmdlen);
if (ret) {
IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue "
"%d, Tx worker stopped\n", txq->id);
spin_lock_bh(&txq->lock);
skb_queue_head(&txq->queue, skb);
spin_unlock_bh(&txq->lock);
mutex_unlock(&tid_info->mutex);
break;
}
txq->concat_ptr = txq->concat_buf + txq->concat_count;
tid_info->last_seq_num =
iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr);
txq->concat_count += ALIGN(cmdlen, 16);
mutex_unlock(&tid_info->mutex);
kfree_skb(skb);
}
iwm_tx_send_concat_packets(iwm, txq);
if (__netif_subqueue_stopped(iwm_to_ndev(iwm), txq->id) &&
!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
(skb_queue_len(&txq->queue) < IWM_TX_LIST_SIZE / 2)) {
IWM_DBG_TX(iwm, DBG, "LINK: start netif_subqueue[%d]", txq->id);
netif_wake_subqueue(iwm_to_ndev(iwm), txq->id);
}
}
int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct iwm_priv *iwm = ndev_to_iwm(netdev);
struct wireless_dev *wdev = iwm_to_wdev(iwm);
struct iwm_tx_info *tx_info;
struct iwm_tx_queue *txq;
struct iwm_sta_info *sta_info;
u8 *dst_addr, sta_id;
u16 queue;
int ret;
if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: "
"not associated\n");
netif_tx_stop_all_queues(netdev);
goto drop;
}
queue = skb_get_queue_mapping(skb);
BUG_ON(queue >= IWM_TX_DATA_QUEUES); /* no iPAN yet */
txq = &iwm->txq[queue];
/* No free space for Tx, tx_worker is too slow */
if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) ||
(skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) {
IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue);
netif_stop_subqueue(netdev, queue);
return NETDEV_TX_BUSY;
}
ret = ieee80211_data_from_8023(skb, netdev->dev_addr, wdev->iftype,
iwm->bssid, 0);
if (ret) {
IWM_ERR(iwm, "build wifi header failed\n");
goto drop;
}
dst_addr = ((struct ieee80211_hdr *)(skb->data))->addr1;
for (sta_id = 0; sta_id < IWM_STA_TABLE_NUM; sta_id++) {
sta_info = &iwm->sta_table[sta_id];
if (sta_info->valid &&
!memcmp(dst_addr, sta_info->addr, ETH_ALEN))
break;
}
if (sta_id == IWM_STA_TABLE_NUM) {
IWM_ERR(iwm, "STA %pM not found in sta_table, Tx ignored\n",
dst_addr);
goto drop;
}
tx_info = skb_to_tx_info(skb);
tx_info->sta = sta_id;
tx_info->color = sta_info->color;
/* UMAC uses TID 8 (vs. 0) for non QoS packets */
if (sta_info->qos)
tx_info->tid = skb->priority;
else
tx_info->tid = IWM_UMAC_MGMT_TID;
spin_lock_bh(&iwm->txq[queue].lock);
skb_queue_tail(&iwm->txq[queue].queue, skb);
spin_unlock_bh(&iwm->txq[queue].lock);
queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
netdev->stats.tx_packets++;
netdev->stats.tx_bytes += skb->len;
return NETDEV_TX_OK;
drop:
netdev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}

View File

@ -1,789 +0,0 @@
/*
* 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_UMAC_H__
#define __IWM_UMAC_H__
struct iwm_udma_in_hdr {
__le32 cmd;
__le32 size;
} __packed;
struct iwm_udma_out_nonwifi_hdr {
__le32 cmd;
__le32 addr;
__le32 op1_sz;
__le32 op2;
} __packed;
struct iwm_udma_out_wifi_hdr {
__le32 cmd;
__le32 meta_data;
} __packed;
/* Sequence numbering */
#define UMAC_WIFI_SEQ_NUM_BASE 1
#define UMAC_WIFI_SEQ_NUM_MAX 0x4000
#define UMAC_NONWIFI_SEQ_NUM_BASE 1
#define UMAC_NONWIFI_SEQ_NUM_MAX 0x10
/* MAC address address */
#define WICO_MAC_ADDRESS_ADDR 0x604008F8
/* RA / TID */
#define UMAC_HDI_ACT_TBL_IDX_TID_POS 0
#define UMAC_HDI_ACT_TBL_IDX_TID_SEED 0xF
#define UMAC_HDI_ACT_TBL_IDX_RA_POS 4
#define UMAC_HDI_ACT_TBL_IDX_RA_SEED 0xF
#define UMAC_HDI_ACT_TBL_IDX_RA_UMAC 0xF
#define UMAC_HDI_ACT_TBL_IDX_TID_UMAC 0x9
#define UMAC_HDI_ACT_TBL_IDX_TID_LMAC 0xA
#define UMAC_HDI_ACT_TBL_IDX_HOST_CMD \
((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
(UMAC_HDI_ACT_TBL_IDX_TID_UMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
#define UMAC_HDI_ACT_TBL_IDX_UMAC_CMD \
((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
(UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
/* STA ID and color */
#define STA_ID_SEED (0x0f)
#define STA_ID_POS (0)
#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS)
#define STA_COLOR_SEED (0x7)
#define STA_COLOR_POS (4)
#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS)
#define STA_ID_N_COLOR_COLOR(id_n_color) \
(((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
#define STA_ID_N_COLOR_ID(id_n_color) \
(((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
/* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */
#define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0
#define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF
/* iwm_umac_notif_alive.page_grp_state Super group number -- bits [7:4] */
#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_POS 4
#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_SEED 0xF
/* iwm_umac_notif_alive.page_grp_state Group min size -- bits [15:8] */
#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_POS 8
#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_SEED 0xFF
/* iwm_umac_notif_alive.page_grp_state Group max size -- bits [23:16] */
#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_POS 16
#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_SEED 0xFF
/* iwm_umac_notif_alive.page_grp_state Super group max size -- bits [31:24] */
#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_POS 24
#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_SEED 0xFF
/* Barkers */
#define UMAC_REBOOT_BARKER 0xdeadbeef
#define UMAC_ACK_BARKER 0xfeedbabe
#define UMAC_PAD_TERMINAL 0xadadadad
/* UMAC JMP address */
#define UMAC_MU_FW_INST_DATA_12_ADDR 0xBF0000
/* iwm_umac_hdi_out_hdr.cmd OP code -- bits [3:0] */
#define UMAC_HDI_OUT_CMD_OPCODE_POS 0
#define UMAC_HDI_OUT_CMD_OPCODE_SEED 0xF
/* iwm_umac_hdi_out_hdr.cmd End-Of-Transfer -- bits [10:10] */
#define UMAC_HDI_OUT_CMD_EOT_POS 10
#define UMAC_HDI_OUT_CMD_EOT_SEED 0x1
/* iwm_umac_hdi_out_hdr.cmd UTFD only usage -- bits [11:11] */
#define UMAC_HDI_OUT_CMD_UTFD_ONLY_POS 11
#define UMAC_HDI_OUT_CMD_UTFD_ONLY_SEED 0x1
/* iwm_umac_hdi_out_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_POS 12
#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF
/* iwm_umac_hdi_out_hdr.cmd Signature -- bits [31:16] */
#define UMAC_HDI_OUT_CMD_SIGNATURE_POS 16
#define UMAC_HDI_OUT_CMD_SIGNATURE_SEED 0xFFFF
/* iwm_umac_hdi_out_hdr.meta_data Byte count -- bits [11:0] */
#define UMAC_HDI_OUT_BYTE_COUNT_POS 0
#define UMAC_HDI_OUT_BYTE_COUNT_SEED 0xFFF
/* iwm_umac_hdi_out_hdr.meta_data Credit group -- bits [15:12] */
#define UMAC_HDI_OUT_CREDIT_GRP_POS 12
#define UMAC_HDI_OUT_CREDIT_GRP_SEED 0xF
/* iwm_umac_hdi_out_hdr.meta_data RA/TID -- bits [23:16] */
#define UMAC_HDI_OUT_RATID_POS 16
#define UMAC_HDI_OUT_RATID_SEED 0xFF
/* iwm_umac_hdi_out_hdr.meta_data LMAC offset -- bits [31:24] */
#define UMAC_HDI_OUT_LMAC_OFFSET_POS 24
#define UMAC_HDI_OUT_LMAC_OFFSET_SEED 0xFF
/* Signature */
#define UMAC_HDI_OUT_SIGNATURE 0xCBBC
/* buffer alignment */
#define UMAC_HDI_BUF_ALIGN_MSK 0xF
/* iwm_umac_hdi_in_hdr.cmd OP code -- bits [3:0] */
#define UMAC_HDI_IN_CMD_OPCODE_POS 0
#define UMAC_HDI_IN_CMD_OPCODE_SEED 0xF
/* iwm_umac_hdi_in_hdr.cmd Non-WiFi API response -- bits [6:4] */
#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_POS 4
#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_SEED 0x7
/* iwm_umac_hdi_in_hdr.cmd WiFi API source -- bits [5:4] */
#define UMAC_HDI_IN_CMD_SOURCE_POS 4
#define UMAC_HDI_IN_CMD_SOURCE_SEED 0x3
/* iwm_umac_hdi_in_hdr.cmd WiFi API EOT -- bits [6:6] */
#define UMAC_HDI_IN_CMD_EOT_POS 6
#define UMAC_HDI_IN_CMD_EOT_SEED 0x1
/* iwm_umac_hdi_in_hdr.cmd timestamp present -- bits [7:7] */
#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_POS 7
#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_SEED 0x1
/* iwm_umac_hdi_in_hdr.cmd WiFi Non-last AMSDU -- bits [8:8] */
#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_POS 8
#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_SEED 0x1
/* iwm_umac_hdi_in_hdr.cmd WiFi HW sequence number -- bits [31:9] */
#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_POS 9
#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_SEED 0x7FFFFF
/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_POS 12
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF
/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW signature -- bits [16:31] */
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_POS 16
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_SEED 0xFFFF
/* Fixed Non-WiFi signature */
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG 0xCBBC
/* IN NTFY op-codes */
#define UMAC_NOTIFY_OPCODE_ALIVE 0xA1
#define UMAC_NOTIFY_OPCODE_INIT_COMPLETE 0xA2
#define UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS 0xA3
#define UMAC_NOTIFY_OPCODE_ERROR 0xA4
#define UMAC_NOTIFY_OPCODE_DEBUG 0xA5
#define UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER 0xB0
#define UMAC_NOTIFY_OPCODE_STATS 0xB1
#define UMAC_NOTIFY_OPCODE_PAGE_DEALLOC 0xB3
#define UMAC_NOTIFY_OPCODE_RX_TICKET 0xB4
#define UMAC_NOTIFY_OPCODE_MAX (UMAC_NOTIFY_OPCODE_RX_TICKET -\
UMAC_NOTIFY_OPCODE_ALIVE + 1)
#define UMAC_NOTIFY_OPCODE_FIRST (UMAC_NOTIFY_OPCODE_ALIVE)
/* HDI OUT OP CODE */
#define UMAC_HDI_OUT_OPCODE_PING 0x0
#define UMAC_HDI_OUT_OPCODE_READ 0x1
#define UMAC_HDI_OUT_OPCODE_WRITE 0x2
#define UMAC_HDI_OUT_OPCODE_JUMP 0x3
#define UMAC_HDI_OUT_OPCODE_REBOOT 0x4
#define UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT 0x5
#define UMAC_HDI_OUT_OPCODE_READ_PERSISTENT 0x6
#define UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE 0x7
/* #define UMAC_HDI_OUT_OPCODE_RESERVED 0x8..0xA */
#define UMAC_HDI_OUT_OPCODE_WRITE_AUX_REG 0xB
#define UMAC_HDI_OUT_OPCODE_WIFI 0xF
/* HDI IN OP CODE -- Non WiFi*/
#define UMAC_HDI_IN_OPCODE_PING 0x0
#define UMAC_HDI_IN_OPCODE_READ 0x1
#define UMAC_HDI_IN_OPCODE_WRITE 0x2
#define UMAC_HDI_IN_OPCODE_WRITE_PERSISTENT 0x5
#define UMAC_HDI_IN_OPCODE_READ_PERSISTENT 0x6
#define UMAC_HDI_IN_OPCODE_READ_MODIFY_WRITE 0x7
#define UMAC_HDI_IN_OPCODE_EP_MGMT 0x8
#define UMAC_HDI_IN_OPCODE_CREDIT_CHANGE 0x9
#define UMAC_HDI_IN_OPCODE_CTRL_DATABASE 0xA
#define UMAC_HDI_IN_OPCODE_WRITE_AUX_REG 0xB
#define UMAC_HDI_IN_OPCODE_NONWIFI_MAX \
(UMAC_HDI_IN_OPCODE_WRITE_AUX_REG + 1)
#define UMAC_HDI_IN_OPCODE_WIFI 0xF
/* HDI IN SOURCE */
#define UMAC_HDI_IN_SOURCE_FHRX 0x0
#define UMAC_HDI_IN_SOURCE_UDMA 0x1
#define UMAC_HDI_IN_SOURCE_FW 0x2
#define UMAC_HDI_IN_SOURCE_RESERVED 0x3
/* OUT CMD op-codes */
#define UMAC_CMD_OPCODE_ECHO 0x01
#define UMAC_CMD_OPCODE_HALT 0x02
#define UMAC_CMD_OPCODE_RESET 0x03
#define UMAC_CMD_OPCODE_BULK_EP_INACT_TIMEOUT 0x09
#define UMAC_CMD_OPCODE_URB_CANCEL_ACK 0x0A
#define UMAC_CMD_OPCODE_DCACHE_FLUSH 0x0B
#define UMAC_CMD_OPCODE_EEPROM_PROXY 0x0C
#define UMAC_CMD_OPCODE_TX_ECHO 0x0D
#define UMAC_CMD_OPCODE_DBG_MON 0x0E
#define UMAC_CMD_OPCODE_INTERNAL_TX 0x0F
#define UMAC_CMD_OPCODE_SET_PARAM_FIX 0x10
#define UMAC_CMD_OPCODE_SET_PARAM_VAR 0x11
#define UMAC_CMD_OPCODE_GET_PARAM 0x12
#define UMAC_CMD_OPCODE_DBG_EVENT_WRAPPER 0x13
#define UMAC_CMD_OPCODE_TARGET 0x14
#define UMAC_CMD_OPCODE_STATISTIC_REQUEST 0x15
#define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16
#define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17
#define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18
#define UMAC_CMD_OPCODE_STOP_RESUME_STA_TX 0x19
#define UMAC_CMD_OPCODE_TEST_BLOCK_ACK 0x1A
#define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA
#define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB
#define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC
#define UMAC_CMD_OPCODE_WIFI_IF_WRAPPER 0xFD
#define UMAC_CMD_OPCODE_WIFI_WRAPPER 0xFE
#define UMAC_CMD_OPCODE_WIFI_PASS_THROUGH 0xFF
/* UMAC WiFi interface op-codes */
#define UMAC_WIFI_IF_CMD_SET_PROFILE 0x11
#define UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE 0x12
#define UMAC_WIFI_IF_CMD_SET_EXCLUDE_LIST 0x13
#define UMAC_WIFI_IF_CMD_SCAN_REQUEST 0x14
#define UMAC_WIFI_IF_CMD_SCAN_CONFIG 0x15
#define UMAC_WIFI_IF_CMD_ADD_WEP40_KEY 0x16
#define UMAC_WIFI_IF_CMD_ADD_WEP104_KEY 0x17
#define UMAC_WIFI_IF_CMD_ADD_TKIP_KEY 0x18
#define UMAC_WIFI_IF_CMD_ADD_CCMP_KEY 0x19
#define UMAC_WIFI_IF_CMD_REMOVE_KEY 0x1A
#define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B
#define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C
#define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E
#define UMAC_WIFI_IF_CMD_PMKID_UPDATE 0x1F
#define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20
/* UMAC WiFi interface ports */
#define UMAC_WIFI_IF_FLG_PORT_DEF 0x00
#define UMAC_WIFI_IF_FLG_PORT_PAN 0x01
#define UMAC_WIFI_IF_FLG_PORT_PAN_INVALID WIFI_IF_FLG_PORT_DEF
/* UMAC WiFi interface actions */
#define UMAC_WIFI_IF_FLG_ACT_GET 0x10
#define UMAC_WIFI_IF_FLG_ACT_SET 0x20
/* iwm_umac_fw_cmd_hdr.meta_data byte count -- bits [11:0] */
#define UMAC_FW_CMD_BYTE_COUNT_POS 0
#define UMAC_FW_CMD_BYTE_COUNT_SEED 0xFFF
/* iwm_umac_fw_cmd_hdr.meta_data status -- bits [15:12] */
#define UMAC_FW_CMD_STATUS_POS 12
#define UMAC_FW_CMD_STATUS_SEED 0xF
/* iwm_umac_fw_cmd_hdr.meta_data full TX command by Driver -- bits [16:16] */
#define UMAC_FW_CMD_TX_DRV_FULL_CMD_POS 16
#define UMAC_FW_CMD_TX_DRV_FULL_CMD_SEED 0x1
/* iwm_umac_fw_cmd_hdr.meta_data TX command by FW -- bits [17:17] */
#define UMAC_FW_CMD_TX_FW_CMD_POS 17
#define UMAC_FW_CMD_TX_FW_CMD_SEED 0x1
/* iwm_umac_fw_cmd_hdr.meta_data TX plaintext mode -- bits [18:18] */
#define UMAC_FW_CMD_TX_PLAINTEXT_POS 18
#define UMAC_FW_CMD_TX_PLAINTEXT_SEED 0x1
/* iwm_umac_fw_cmd_hdr.meta_data STA color -- bits [22:20] */
#define UMAC_FW_CMD_TX_STA_COLOR_POS 20
#define UMAC_FW_CMD_TX_STA_COLOR_SEED 0x7
/* iwm_umac_fw_cmd_hdr.meta_data TX life time (TU) -- bits [31:24] */
#define UMAC_FW_CMD_TX_LIFETIME_TU_POS 24
#define UMAC_FW_CMD_TX_LIFETIME_TU_SEED 0xFF
/* iwm_dev_cmd_hdr.flags Response required -- bits [5:5] */
#define UMAC_DEV_CMD_FLAGS_RESP_REQ_POS 5
#define UMAC_DEV_CMD_FLAGS_RESP_REQ_SEED 0x1
/* iwm_dev_cmd_hdr.flags Aborted command -- bits [6:6] */
#define UMAC_DEV_CMD_FLAGS_ABORT_POS 6
#define UMAC_DEV_CMD_FLAGS_ABORT_SEED 0x1
/* iwm_dev_cmd_hdr.flags Internal command -- bits [7:7] */
#define DEV_CMD_FLAGS_FLD_INTERNAL_POS 7
#define DEV_CMD_FLAGS_FLD_INTERNAL_SEED 0x1
/* Rx */
/* Rx actions */
#define IWM_RX_TICKET_DROP 0x0
#define IWM_RX_TICKET_RELEASE 0x1
#define IWM_RX_TICKET_SNIFFER 0x2
#define IWM_RX_TICKET_ENQUEUE 0x3
/* Rx flags */
#define IWM_RX_TICKET_PAD_SIZE_MSK 0x2
#define IWM_RX_TICKET_SPECIAL_SNAP_MSK 0x4
#define IWM_RX_TICKET_AMSDU_MSK 0x8
#define IWM_RX_TICKET_DROP_REASON_POS 4
#define IWM_RX_TICKET_DROP_REASON_MSK (0x1F << IWM_RX_TICKET_DROP_REASON_POS)
#define IWM_RX_DROP_NO_DROP 0x0
#define IWM_RX_DROP_BAD_CRC 0x1
/* L2P no address match */
#define IWM_RX_DROP_LMAC_ADDR_FILTER 0x2
/* Multicast address not in list */
#define IWM_RX_DROP_MCAST_ADDR_FILTER 0x3
/* Control frames are not sent to the driver */
#define IWM_RX_DROP_CTL_FRAME 0x4
/* Our frame is back */
#define IWM_RX_DROP_OUR_TX 0x5
/* Association class filtering */
#define IWM_RX_DROP_CLASS_FILTER 0x6
/* Duplicated frame */
#define IWM_RX_DROP_DUPLICATE_FILTER 0x7
/* Decryption error */
#define IWM_RX_DROP_SEC_ERR 0x8
/* Unencrypted frame while encryption is on */
#define IWM_RX_DROP_SEC_NO_ENCRYPTION 0x9
/* Replay check failure */
#define IWM_RX_DROP_SEC_REPLAY_ERR 0xa
/* uCode and FW key color mismatch, check before replay */
#define IWM_RX_DROP_SEC_KEY_COLOR_MISMATCH 0xb
#define IWM_RX_DROP_SEC_TKIP_COUNTER_MEASURE 0xc
/* No fragmentations Db is found */
#define IWM_RX_DROP_FRAG_NO_RESOURCE 0xd
/* Fragmention Db has seqCtl mismatch Vs. non-1st frag */
#define IWM_RX_DROP_FRAG_ERR 0xe
#define IWM_RX_DROP_FRAG_LOST 0xf
#define IWM_RX_DROP_FRAG_COMPLETE 0x10
/* Should be handled by UMAC */
#define IWM_RX_DROP_MANAGEMENT 0x11
/* STA not found by UMAC */
#define IWM_RX_DROP_NO_STATION 0x12
/* NULL or QoS NULL */
#define IWM_RX_DROP_NULL_DATA 0x13
#define IWM_RX_DROP_BA_REORDER_OLD_SEQCTL 0x14
#define IWM_RX_DROP_BA_REORDER_DUPLICATE 0x15
struct iwm_rx_ticket {
__le16 action;
__le16 id;
__le16 flags;
u8 payload_offset; /* includes: MAC header, pad, IV */
u8 tail_len; /* includes: MIC, ICV, CRC (w/o STATUS) */
} __packed;
struct iwm_rx_mpdu_hdr {
__le16 len;
__le16 reserved;
} __packed;
/* UMAC SW WIFI API */
struct iwm_dev_cmd_hdr {
u8 cmd;
u8 flags;
__le16 seq_num;
} __packed;
struct iwm_umac_fw_cmd_hdr {
__le32 meta_data;
struct iwm_dev_cmd_hdr cmd;
} __packed;
struct iwm_umac_wifi_out_hdr {
struct iwm_udma_out_wifi_hdr hw_hdr;
struct iwm_umac_fw_cmd_hdr sw_hdr;
} __packed;
struct iwm_umac_nonwifi_out_hdr {
struct iwm_udma_out_nonwifi_hdr hw_hdr;
} __packed;
struct iwm_umac_wifi_in_hdr {
struct iwm_udma_in_hdr hw_hdr;
struct iwm_umac_fw_cmd_hdr sw_hdr;
} __packed;
struct iwm_umac_nonwifi_in_hdr {
struct iwm_udma_in_hdr hw_hdr;
__le32 time_stamp;
} __packed;
#define IWM_UMAC_PAGE_SIZE 0x200
/* Notify structures */
struct iwm_fw_version {
u8 minor;
u8 major;
__le16 id;
};
struct iwm_fw_build {
u8 type;
u8 subtype;
u8 platform;
u8 opt;
};
struct iwm_fw_alive_hdr {
struct iwm_fw_version ver;
struct iwm_fw_build build;
__le32 os_build;
__le32 log_hdr_addr;
__le32 log_buf_addr;
__le32 sys_timer_addr;
};
#define WAIT_NOTIF_TIMEOUT (2 * HZ)
#define SCAN_COMPLETE_TIMEOUT (3 * HZ)
#define UMAC_NTFY_ALIVE_STATUS_ERR 0xDEAD
#define UMAC_NTFY_ALIVE_STATUS_OK 0xCAFE
#define UMAC_NTFY_INIT_COMPLETE_STATUS_ERR 0xDEAD
#define UMAC_NTFY_INIT_COMPLETE_STATUS_OK 0xCAFE
#define UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN 0x40
#define UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN 0x80
#define IWM_MACS_OUT_GROUPS 6
#define IWM_MACS_OUT_SGROUPS 1
#define WIFI_IF_NTFY_ASSOC_START 0x80
#define WIFI_IF_NTFY_ASSOC_COMPLETE 0x81
#define WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE 0x82
#define WIFI_IF_NTFY_CONNECTION_TERMINATED 0x83
#define WIFI_IF_NTFY_SCAN_COMPLETE 0x84
#define WIFI_IF_NTFY_STA_TABLE_CHANGE 0x85
#define WIFI_IF_NTFY_EXTENDED_IE_REQUIRED 0x86
#define WIFI_IF_NTFY_RADIO_PREEMPTION 0x87
#define WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED 0x88
#define WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED 0x89
#define WIFI_IF_NTFY_LINK_QUALITY_STATISTICS 0x8A
#define WIFI_IF_NTFY_MGMT_FRAME 0x8B
/* DEBUG INDICATIONS */
#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START 0xE0
#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE 0xE1
#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START 0xE2
#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT 0xE3
#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START 0xE4
#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE 0xE5
#define WIFI_DBG_IF_NTFY_CNCT_ATC_START 0xE6
#define WIFI_DBG_IF_NTFY_COEX_NOTIFICATION 0xE7
#define WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP 0xE8
#define WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP 0xE9
#define WIFI_IF_NTFY_MAX 0xff
/* Notification structures */
struct iwm_umac_notif_wifi_if {
struct iwm_umac_wifi_in_hdr hdr;
u8 status;
u8 flags;
__le16 buf_size;
} __packed;
#define UMAC_ROAM_REASON_FIRST_SELECTION 0x1
#define UMAC_ROAM_REASON_AP_DEAUTH 0x2
#define UMAC_ROAM_REASON_AP_CONNECT_LOST 0x3
#define UMAC_ROAM_REASON_RSSI 0x4
#define UMAC_ROAM_REASON_AP_ASSISTED_ROAM 0x5
#define UMAC_ROAM_REASON_IBSS_COALESCING 0x6
struct iwm_umac_notif_assoc_start {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le32 roam_reason;
u8 bssid[ETH_ALEN];
u8 reserved[2];
} __packed;
#define UMAC_ASSOC_COMPLETE_SUCCESS 0x0
#define UMAC_ASSOC_COMPLETE_FAILURE 0x1
struct iwm_umac_notif_assoc_complete {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le32 status;
u8 bssid[ETH_ALEN];
u8 band;
u8 channel;
} __packed;
#define UMAC_PROFILE_INVALID_ASSOC_TIMEOUT 0x0
#define UMAC_PROFILE_INVALID_ROAM_TIMEOUT 0x1
#define UMAC_PROFILE_INVALID_REQUEST 0x2
#define UMAC_PROFILE_INVALID_RF_PREEMPTED 0x3
struct iwm_umac_notif_profile_invalidate {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le32 reason;
} __packed;
#define UMAC_SCAN_RESULT_SUCCESS 0x0
#define UMAC_SCAN_RESULT_ABORTED 0x1
#define UMAC_SCAN_RESULT_REJECTED 0x2
#define UMAC_SCAN_RESULT_FAILED 0x3
struct iwm_umac_notif_scan_complete {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le32 type;
__le32 result;
u8 seq_num;
} __packed;
#define UMAC_OPCODE_ADD_MODIFY 0x0
#define UMAC_OPCODE_REMOVE 0x1
#define UMAC_OPCODE_CLEAR_ALL 0x2
#define UMAC_STA_FLAG_QOS 0x1
struct iwm_umac_notif_sta_info {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le32 opcode;
u8 mac_addr[ETH_ALEN];
u8 sta_id; /* bits 0-3: station ID, bits 4-7: station color */
u8 flags;
} __packed;
#define UMAC_BAND_2GHZ 0
#define UMAC_BAND_5GHZ 1
#define UMAC_CHANNEL_WIDTH_20MHZ 0
#define UMAC_CHANNEL_WIDTH_40MHZ 1
struct iwm_umac_notif_bss_info {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le32 type;
__le32 timestamp;
__le16 table_idx;
__le16 frame_len;
u8 band;
u8 channel;
s8 rssi;
u8 reserved;
u8 frame_buf[1];
} __packed;
#define IWM_BSS_REMOVE_INDEX_MSK 0x0fff
#define IWM_BSS_REMOVE_FLAGS_MSK 0xfc00
#define IWM_BSS_REMOVE_FLG_AGE 0x1000
#define IWM_BSS_REMOVE_FLG_TIMEOUT 0x2000
#define IWM_BSS_REMOVE_FLG_TABLE_FULL 0x4000
struct iwm_umac_notif_bss_removed {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le32 count;
__le16 entries[0];
} __packed;
struct iwm_umac_notif_mgt_frame {
struct iwm_umac_notif_wifi_if mlme_hdr;
__le16 len;
u8 frame[1];
} __packed;
struct iwm_umac_notif_alive {
struct iwm_umac_wifi_in_hdr hdr;
__le16 status;
__le16 reserved1;
struct iwm_fw_alive_hdr alive_data;
__le16 reserved2;
__le16 page_grp_count;
__le32 page_grp_state[IWM_MACS_OUT_GROUPS];
} __packed;
struct iwm_umac_notif_init_complete {
struct iwm_umac_wifi_in_hdr hdr;
__le16 status;
__le16 reserved;
} __packed;
/* error categories */
enum {
UMAC_SYS_ERR_CAT_NONE = 0,
UMAC_SYS_ERR_CAT_BOOT,
UMAC_SYS_ERR_CAT_UMAC,
UMAC_SYS_ERR_CAT_UAXM,
UMAC_SYS_ERR_CAT_LMAC,
UMAC_SYS_ERR_CAT_MAX
};
struct iwm_fw_error_hdr {
__le32 category;
__le32 status;
__le32 pc;
__le32 blink1;
__le32 blink2;
__le32 ilink1;
__le32 ilink2;
__le32 data1;
__le32 data2;
__le32 line_num;
__le32 umac_status;
__le32 lmac_status;
__le32 sdio_status;
__le32 dbm_sample_ctrl;
__le32 dbm_buf_base;
__le32 dbm_buf_end;
__le32 dbm_buf_write_ptr;
__le32 dbm_buf_cycle_cnt;
} __packed;
struct iwm_umac_notif_error {
struct iwm_umac_wifi_in_hdr hdr;
struct iwm_fw_error_hdr err;
} __packed;
#define UMAC_DEALLOC_NTFY_CHANGES_CNT_POS 0
#define UMAC_DEALLOC_NTFY_CHANGES_CNT_SEED 0xff
#define UMAC_DEALLOC_NTFY_CHANGES_MSK_POS 8
#define UMAC_DEALLOC_NTFY_CHANGES_MSK_SEED 0xffffff
#define UMAC_DEALLOC_NTFY_PAGE_CNT_POS 0
#define UMAC_DEALLOC_NTFY_PAGE_CNT_SEED 0xffffff
#define UMAC_DEALLOC_NTFY_GROUP_NUM_POS 24
#define UMAC_DEALLOC_NTFY_GROUP_NUM_SEED 0xf
struct iwm_umac_notif_page_dealloc {
struct iwm_umac_wifi_in_hdr hdr;
__le32 changes;
__le32 grp_info[IWM_MACS_OUT_GROUPS];
} __packed;
struct iwm_umac_notif_wifi_status {
struct iwm_umac_wifi_in_hdr hdr;
__le16 status;
__le16 reserved;
} __packed;
struct iwm_umac_notif_rx_ticket {
struct iwm_umac_wifi_in_hdr hdr;
u8 num_tickets;
u8 reserved[3];
struct iwm_rx_ticket tickets[1];
} __packed;
/* Tx/Rx rates window (number of max of last update window per second) */
#define UMAC_NTF_RATE_SAMPLE_NR 4
/* Max numbers of bits required to go through all antennae in bitmasks */
#define UMAC_PHY_NUM_CHAINS 3
#define IWM_UMAC_MGMT_TID 8
#define IWM_UMAC_TID_NR 9 /* 8 TIDs + MGMT */
struct iwm_umac_notif_stats {
struct iwm_umac_wifi_in_hdr hdr;
__le32 flags;
__le32 timestamp;
__le16 tid_load[IWM_UMAC_TID_NR + 1]; /* 1 non-QoS + 1 dword align */
__le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR];
__le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR];
__le32 chain_energy[UMAC_PHY_NUM_CHAINS];
s32 rssi_dbm;
s32 noise_dbm;
__le32 supp_rates;
__le32 supp_ht_rates;
__le32 missed_beacons;
__le32 rx_beacons;
__le32 rx_dir_pkts;
__le32 rx_nondir_pkts;
__le32 rx_multicast;
__le32 rx_errors;
__le32 rx_drop_other_bssid;
__le32 rx_drop_decode;
__le32 rx_drop_reassembly;
__le32 rx_drop_bad_len;
__le32 rx_drop_overflow;
__le32 rx_drop_crc;
__le32 rx_drop_missed;
__le32 tx_dir_pkts;
__le32 tx_nondir_pkts;
__le32 tx_failure;
__le32 tx_errors;
__le32 tx_drop_max_retry;
__le32 tx_err_abort;
__le32 tx_err_carrier;
__le32 rx_bytes;
__le32 tx_bytes;
__le32 tx_power;
__le32 tx_max_power;
__le32 roam_threshold;
__le32 ap_assoc_nr;
__le32 scan_full;
__le32 scan_abort;
__le32 ap_nr;
__le32 roam_nr;
__le32 roam_missed_beacons;
__le32 roam_rssi;
__le32 roam_unassoc;
__le32 roam_deauth;
__le32 roam_ap_loadblance;
} __packed;
#define UMAC_STOP_TX_FLAG 0x1
#define UMAC_RESUME_TX_FLAG 0x2
#define LAST_SEQ_NUM_INVALID 0xFFFF
struct iwm_umac_notif_stop_resume_tx {
struct iwm_umac_wifi_in_hdr hdr;
u8 flags; /* UMAC_*_TX_FLAG_* */
u8 sta_id;
__le16 stop_resume_tid_msk; /* tid bitmask */
} __packed;
#define UMAC_MAX_NUM_PMKIDS 4
/* WiFi interface wrapper header */
struct iwm_umac_wifi_if {
u8 oid;
u8 flags;
__le16 buf_size;
} __packed;
#define IWM_SEQ_NUM_HOST_MSK 0x0000
#define IWM_SEQ_NUM_UMAC_MSK 0x4000
#define IWM_SEQ_NUM_LMAC_MSK 0x8000
#define IWM_SEQ_NUM_MSK 0xC000
#endif