Commit bdb903e4 authored by John W. Linville's avatar John W. Linville

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: default avatarJohn W. Linville <linville@tuxdriver.com>
Acked-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent f7ace5f0
......@@ -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
......
......@@ -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"
......
......@@ -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/
......
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.
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__
/*
* 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
This diff is collapsed.
/*
* 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
This diff is collapsed.
This diff is collapsed.
/*
* 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
This diff is collapsed.
/*
* 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);
}
/*
* 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
This diff is collapsed.
/*
* 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
This diff is collapsed.
/*
* 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_ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* 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));
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#include "iwm.h"
#define CREATE_TRACE_POINTS
#include "trace.h"
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment