#include <linux/sched.h>
#include <linux/slab.h>
#include <net/cfg80211-wext.h>
#include "ipw2200.h"
#include "ipw.h"
#ifndef KBUILD_EXTMOD
#define VK "k"
#else
#define VK
#endif
#ifdef CONFIG_IPW2200_DEBUG
#define VD "d"
#else
#define VD
#endif
#ifdef CONFIG_IPW2200_MONITOR
#define VM "m"
#else
#define VM
#endif
#ifdef CONFIG_IPW2200_PROMISCUOUS
#define VP "p"
#else
#define VP
#endif
#ifdef CONFIG_IPW2200_RADIOTAP
#define VR "r"
#else
#define VR
#endif
#ifdef CONFIG_IPW2200_QOS
#define VQ "q"
#else
#define VQ
#endif
#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
#define DRV_VERSION IPW2200_VERSION
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("ipw2200-ibss.fw");
#ifdef CONFIG_IPW2200_MONITOR
MODULE_FIRMWARE("ipw2200-sniffer.fw");
#endif
MODULE_FIRMWARE("ipw2200-bss.fw");
static int cmdlog = 0;
static int debug = 0;
static int default_channel = 0;
static int network_mode = 0;
static u32 ipw_debug_level;
static int associate;
static int auto_create = 1;
static int led_support = 1;
static int disable = 0;
static int bt_coexist = 0;
static int hwcrypto = 0;
static int roaming = 1;
static const char ipw_modes[] = {
'a', 'b', 'g', '?'
};
static int antenna = CFG_SYS_ANTENNA_BOTH;
#ifdef CONFIG_IPW2200_PROMISCUOUS
static int rtap_iface = 0;
#endif
static struct ieee80211_rate ipw2200_rates[] = {
{ .bitrate = 10 },
{ .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60 },
{ .bitrate = 90 },
{ .bitrate = 120 },
{ .bitrate = 180 },
{ .bitrate = 240 },
{ .bitrate = 360 },
{ .bitrate = 480 },
{ .bitrate = 540 }
};
#define ipw2200_a_rates (ipw2200_rates + 4)
#define ipw2200_num_a_rates 8
#define ipw2200_bg_rates (ipw2200_rates + 0)
#define ipw2200_num_bg_rates 12
#define ieee80211chan2mhz(x) \
(((x) <= 14) ? \
(((x) == 14) ? 2484 : ((x) * 5) + 2407) : \
((x) + 1000) * 5)
#ifdef CONFIG_IPW2200_QOS
static int qos_enable = 0;
static int qos_burst_enable = 0;
static int qos_no_ack_mask = 0;
static int burst_duration_CCK = 0;
static int burst_duration_OFDM = 0;
static struct libipw_qos_parameters def_qos_parameters_OFDM = {
{QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
QOS_TX3_CW_MIN_OFDM},
{QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
QOS_TX3_CW_MAX_OFDM},
{QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
{QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
{QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
};
static struct libipw_qos_parameters def_qos_parameters_CCK = {
{QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
QOS_TX3_CW_MIN_CCK},
{QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
QOS_TX3_CW_MAX_CCK},
{QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
{QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
{QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
QOS_TX3_TXOP_LIMIT_CCK}
};
static struct libipw_qos_parameters def_parameters_OFDM = {
{DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
DEF_TX3_CW_MIN_OFDM},
{DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
DEF_TX3_CW_MAX_OFDM},
{DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
{DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
{DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
};
static struct libipw_qos_parameters def_parameters_CCK = {
{DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
DEF_TX3_CW_MIN_CCK},
{DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
DEF_TX3_CW_MAX_CCK},
{DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
{DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
{DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
DEF_TX3_TXOP_LIMIT_CCK}
};
static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
static int from_priority_to_tx_queue[] = {
IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
};
static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters
*qos_param);
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element
*qos_param);
#endif /* CONFIG_IPW2200_QOS */
static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
static void ipw_remove_current_network(struct ipw_priv *priv);
static void ipw_rx(struct ipw_priv *priv);
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
struct clx2_tx_queue *txq, int qindex);
static int ipw_queue_reset(struct ipw_priv *priv);
static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, const void *buf,
int len, int sync);
static void ipw_tx_queue_free(struct ipw_priv *);
static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
static void ipw_rx_queue_replenish(void *);
static int ipw_up(struct ipw_priv *);
static void ipw_bg_up(struct work_struct *work);
static void ipw_down(struct ipw_priv *);
static void ipw_bg_down(struct work_struct *work);
static int ipw_config(struct ipw_priv *);
static int init_supported_rates(struct ipw_priv *priv,
struct ipw_supported_rates *prates);
static void ipw_set_hwcrypto_keys(struct ipw_priv *);
static void ipw_send_wep_keys(struct ipw_priv *, int);
static int snprint_line(char *buf, size_t count,
const u8 * data, u32 len, u32 ofs)
{
int out, i, j, l;
char c;
out = scnprintf(buf, count, "%08X", ofs);
for (l = 0, i = 0; i < 2; i++) {
out += scnprintf(buf + out, count - out, " ");
for (j = 0; j < 8 && l < len; j++, l++)
out += scnprintf(buf + out, count - out, "%02X ",
data[(i * 8 + j)]);
for (; j < 8; j++)
out += scnprintf(buf + out, count - out, " ");
}
out += scnprintf(buf + out, count - out, " ");
for (l = 0, i = 0; i < 2; i++) {
out += scnprintf(buf + out, count - out, " ");
for (j = 0; j < 8 && l < len; j++, l++) {
c = data[(i * 8 + j)];
if (!isascii(c) || !isprint(c))
c = '.';
out += scnprintf(buf + out, count - out, "%c", c);
}
for (; j < 8; j++)
out += scnprintf(buf + out, count - out, " ");
}
return out;
}
static void printk_buf(int level, const u8 * data, u32 len)
{
char line[81];
u32 ofs = 0;
if (!(ipw_debug_level & level))
return;
while (len) {
snprint_line(line, sizeof(line), &data[ofs],
min(len, 16U), ofs);
printk(KERN_DEBUG "%s\n", line);
ofs += 16;
len -= min(len, 16U);
}
}
static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
{
size_t out = size;
u32 ofs = 0;
int total = 0;
while (size && len) {
out = snprint_line(output, size, &data[ofs],
min_t(size_t, len, 16U), ofs);
ofs += 16;
output += out;
size -= out;
len -= min_t(size_t, len, 16U);
total += out;
}
return total;
}
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
{
IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
__LINE__, (u32) (b), (u32) (c));
_ipw_write_reg8(a, b, c);
}
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
{
IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
__LINE__, (u32) (b), (u32) (c));
_ipw_write_reg16(a, b, c);
}
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
{
IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
__LINE__, (u32) (b), (u32) (c));
_ipw_write_reg32(a, b, c);
}
static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs,
u8 val)
{
writeb(val, ipw->hw_base + ofs);
}
#define ipw_write8(ipw, ofs, val) do { \
IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, \
__LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write8(ipw, ofs, val); \
} while (0)
static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs,
u16 val)
{
writew(val, ipw->hw_base + ofs);
}
#define ipw_write16(ipw, ofs, val) do { \
IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, \
__LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write16(ipw, ofs, val); \
} while (0)
static inline void _ipw_write32(struct ipw_priv *ipw, unsigned long ofs,
u32 val)
{
writel(val, ipw->hw_base + ofs);
}
#define ipw_write32(ipw, ofs, val) do { \
IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, \
__LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write32(ipw, ofs, val); \
} while (0)
static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs)
{
return readb(ipw->hw_base + ofs);
}
#define ipw_read8(ipw, ofs) ({ \
IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", __FILE__, __LINE__, \
(u32)(ofs)); \
_ipw_read8(ipw, ofs); \
})
static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs)
{
return readl(ipw->hw_base + ofs);
}
#define ipw_read32(ipw, ofs) ({ \
IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", __FILE__, __LINE__, \
(u32)(ofs)); \
_ipw_read32(ipw, ofs); \
})
static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
#define ipw_read_indirect(a, b, c, d) ({ \
IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %u bytes\n", __FILE__, \
__LINE__, (u32)(b), (u32)(d)); \
_ipw_read_indirect(a, b, c, d); \
})
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
int num);
#define ipw_write_indirect(a, b, c, d) do { \
IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %u bytes\n", __FILE__, \
__LINE__, (u32)(b), (u32)(d)); \
_ipw_write_indirect(a, b, c, d); \
} while (0)
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
{
IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
_ipw_write32(priv, IPW_INDIRECT_DATA, value);
}
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;
u32 dif_len = reg - aligned_addr;
IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
_ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
}
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
{
u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;
u32 dif_len = (reg - aligned_addr) & (~0x1ul);
IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
_ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
}
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
u32 word;
_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
IPW_DEBUG_IO(" reg = 0x%8X :\n", reg);
word = _ipw_read32(priv, IPW_INDIRECT_DATA);
return (word >> ((reg & 0x3) * 8)) & 0xff;
}
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
{
u32 value;
IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
value = _ipw_read32(priv, IPW_INDIRECT_DATA);
IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x\n", reg, value);
return value;
}
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
int num)
{
u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;
u32 dif_len = addr - aligned_addr;
u32 i;
IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
if (num <= 0) {
return;
}
if (unlikely(dif_len)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
aligned_addr += 4;
}
_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
if (unlikely(num)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = 0; num > 0; i++, num--)
*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
}
}
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
int num)
{
u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;
u32 dif_len = addr - aligned_addr;
u32 i;
IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
if (num <= 0) {
return;
}
if (unlikely(dif_len)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
aligned_addr += 4;
}
_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
if (unlikely(num)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = 0; num > 0; i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
}
}
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
int num)
{
memcpy_toio((priv->hw_base + addr), buf, num);
}
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}
static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
}
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
{
if (priv->status & STATUS_INT_ENABLED)
return;
priv->status |= STATUS_INT_ENABLED;
ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
}
static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
{
if (!(priv->status & STATUS_INT_ENABLED))
return;
priv->status &= ~STATUS_INT_ENABLED;
ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
}
static inline void ipw_enable_interrupts(struct ipw_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->irq_lock, flags);
__ipw_enable_interrupts(priv);
spin_unlock_irqrestore(&priv->irq_lock, flags);
}
static inline void ipw_disable_interrupts(struct ipw_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->irq_lock, flags);
__ipw_disable_interrupts(priv);
spin_unlock_irqrestore(&priv->irq_lock, flags);
}
static char *ipw_error_desc(u32 val)
{
switch (val) {
case IPW_FW_ERROR_OK:
return "ERROR_OK";
case IPW_FW_ERROR_FAIL:
return "ERROR_FAIL";
case IPW_FW_ERROR_MEMORY_UNDERFLOW:
return "MEMORY_UNDERFLOW";
case IPW_FW_ERROR_MEMORY_OVERFLOW:
return "MEMORY_OVERFLOW";
case IPW_FW_ERROR_BAD_PARAM:
return "BAD_PARAM";
case IPW_FW_ERROR_BAD_CHECKSUM:
return "BAD_CHECKSUM";
case IPW_FW_ERROR_NMI_INTERRUPT:
return "NMI_INTERRUPT";
case IPW_FW_ERROR_BAD_DATABASE:
return "BAD_DATABASE";
case IPW_FW_ERROR_ALLOC_FAIL:
return "ALLOC_FAIL";
case IPW_FW_ERROR_DMA_UNDERRUN:
return "DMA_UNDERRUN";
case IPW_FW_ERROR_DMA_STATUS:
return "DMA_STATUS";
case IPW_FW_ERROR_DINO_ERROR:
return "DINO_ERROR";
case IPW_FW_ERROR_EEPROM_ERROR:
return "EEPROM_ERROR";
case IPW_FW_ERROR_SYSASSERT:
return "SYSASSERT";
case IPW_FW_ERROR_FATAL_ERROR:
return "FATAL_ERROR";
default:
return "UNKNOWN_ERROR";
}
}
static void ipw_dump_error_log(struct ipw_priv *priv,
struct ipw_fw_error *error)
{
u32 i;
if (!error) {
IPW_ERROR("Error allocating and capturing error log. "
"Nothing to dump.\n");
return;
}
IPW_ERROR("Start IPW Error Log Dump:\n");
IPW_ERROR("Status: 0x%08X, Config: %08X\n",
error->status, error->config);
for (i = 0; i < error->elem_len; i++)
IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
ipw_error_desc(error->elem[i].desc),
error->elem[i].time,
error->elem[i].blink1,
error->elem[i].blink2,
error->elem[i].link1,
error->elem[i].link2, error->elem[i].data);
for (i = 0; i < error->log_len; i++)
IPW_ERROR("%i\t0x%08x\t%i\n",
error->log[i].time,
error->log[i].data, error->log[i].event);
}
static inline int ipw_is_init(struct ipw_priv *priv)
{
return (priv->status & STATUS_INIT) ? 1 : 0;
}
static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
{
u32 addr, field_info, field_len, field_count, total_len;
IPW_DEBUG_ORD("ordinal = %i\n", ord);
if (!priv || !val || !len) {
IPW_DEBUG_ORD("Invalid argument\n");
return -EINVAL;
}
if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
IPW_DEBUG_ORD("Access ordinals before initialization\n");
return -EINVAL;
}
switch (IPW_ORD_TABLE_ID_MASK & ord) {
case IPW_ORD_TABLE_0_MASK:
ord &= IPW_ORD_TABLE_VALUE_MASK;
if (ord > priv->table0_len) {
IPW_DEBUG_ORD("ordinal value (%i) longer then "
"max (%i)\n", ord, priv->table0_len);
return -EINVAL;
}
if (*len < sizeof(u32)) {
IPW_DEBUG_ORD("ordinal buffer length too small, "
"need %zd\n", sizeof(u32));
return -EINVAL;
}
IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
ord, priv->table0_addr + (ord << 2));
*len = sizeof(u32);
ord <<= 2;
*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
break;
case IPW_ORD_TABLE_1_MASK:
ord &= IPW_ORD_TABLE_VALUE_MASK;
if (ord > priv->table1_len) {
IPW_DEBUG_ORD("ordinal value too long\n");
return -EINVAL;
}
if (*len < sizeof(u32)) {
IPW_DEBUG_ORD("ordinal buffer length too small, "
"need %zd\n", sizeof(u32));
return -EINVAL;
}
*((u32 *) val) =
ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
*len = sizeof(u32);
break;
case IPW_ORD_TABLE_2_MASK:
ord &= IPW_ORD_TABLE_VALUE_MASK;
if (ord > priv->table2_len) {
IPW_DEBUG_ORD("ordinal value too long\n");
return -EINVAL;
}
addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
field_info =
ipw_read_reg32(priv,
priv->table2_addr + (ord << 3) +
sizeof(u32));
field_len = *((u16 *) & field_info);
field_count = *(((u16 *) & field_info) + 1);
total_len = field_len * field_count;
if (total_len > *len) {
*len = total_len;
return -EINVAL;
}
*len = total_len;
if (!total_len)
return 0;
IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
"field_info = 0x%08x\n",
addr, total_len, field_info);
ipw_read_indirect(priv, addr, val, total_len);
break;
default:
IPW_DEBUG_ORD("Invalid ordinal!\n");
return -EINVAL;
}
return 0;
}
static void ipw_init_ordinals(struct ipw_priv *priv)
{
priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
priv->table0_len = ipw_read32(priv, priv->table0_addr);
IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
priv->table0_addr, priv->table0_len);
priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
priv->table1_addr, priv->table1_len);
priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
priv->table2_len &= 0x0000ffff;
IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
priv->table2_addr, priv->table2_len);
}
static u32 ipw_register_toggle(u32 reg)
{
reg &= ~IPW_START_STANDBY;
if (reg & IPW_GATE_ODMA)
reg &= ~IPW_GATE_ODMA;
if (reg & IPW_GATE_IDMA)
reg &= ~IPW_GATE_IDMA;
if (reg & IPW_GATE_ADMA)
reg &= ~IPW_GATE_ADMA;
return reg;
}
#define LD_TIME_LINK_ON msecs_to_jiffies(300)
#define LD_TIME_LINK_OFF msecs_to_jiffies(2700)
#define LD_TIME_ACT_ON msecs_to_jiffies(250)
static void ipw_led_link_on(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
return;
spin_lock_irqsave(&priv->lock, flags);
if (!(priv->status & STATUS_RF_KILL_MASK) &&
!(priv->status & STATUS_LED_LINK_ON)) {
IPW_DEBUG_LED("Link LED On\n");
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led |= priv->led_association_on;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
priv->status |= STATUS_LED_LINK_ON;
if (!(priv->status & STATUS_ASSOCIATED))
schedule_delayed_work(&priv->led_link_off,
LD_TIME_LINK_ON);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_bg_led_link_on(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, led_link_on.work);
mutex_lock(&priv->mutex);
ipw_led_link_on(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_led_link_off(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
return;
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_LED_LINK_ON) {
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led &= priv->led_association_off;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
IPW_DEBUG_LED("Link LED Off\n");
priv->status &= ~STATUS_LED_LINK_ON;
if (!(priv->status & STATUS_RF_KILL_MASK) &&
!(priv->status & STATUS_ASSOCIATED))
schedule_delayed_work(&priv->led_link_on,
LD_TIME_LINK_OFF);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_bg_led_link_off(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, led_link_off.work);
mutex_lock(&priv->mutex);
ipw_led_link_off(priv);
mutex_unlock(&priv->mutex);
}
static void __ipw_led_activity_on(struct ipw_priv *priv)
{
u32 led;
if (priv->config & CFG_NO_LED)
return;
if (priv->status & STATUS_RF_KILL_MASK)
return;
if (!(priv->status & STATUS_LED_ACT_ON)) {
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led |= priv->led_activity_on;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
IPW_DEBUG_LED("Activity LED On\n");
priv->status |= STATUS_LED_ACT_ON;
cancel_delayed_work(&priv->led_act_off);
schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
} else {
cancel_delayed_work(&priv->led_act_off);
schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
}
}
#if 0
void ipw_led_activity_on(struct ipw_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
__ipw_led_activity_on(priv);
spin_unlock_irqrestore(&priv->lock, flags);
}
#endif /* 0 */
static void ipw_led_activity_off(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
if (priv->config & CFG_NO_LED)
return;
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_LED_ACT_ON) {
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led &= priv->led_activity_off;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
IPW_DEBUG_LED("Activity LED Off\n");
priv->status &= ~STATUS_LED_ACT_ON;
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_bg_led_activity_off(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, led_act_off.work);
mutex_lock(&priv->mutex);
ipw_led_activity_off(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_led_band_on(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
if (priv->config & CFG_NO_LED ||
priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
return;
spin_lock_irqsave(&priv->lock, flags);
led = ipw_read_reg32(priv, IPW_EVENT_REG);
if (priv->assoc_network->mode == IEEE_A) {
led |= priv->led_ofdm_on;
led &= priv->led_association_off;
IPW_DEBUG_LED("Mode LED On: 802.11a\n");
} else if (priv->assoc_network->mode == IEEE_G) {
led |= priv->led_ofdm_on;
led |= priv->led_association_on;
IPW_DEBUG_LED("Mode LED On: 802.11g\n");
} else {
led &= priv->led_ofdm_off;
led |= priv->led_association_on;
IPW_DEBUG_LED("Mode LED On: 802.11b\n");
}
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_led_band_off(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
return;
spin_lock_irqsave(&priv->lock, flags);
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led &= priv->led_ofdm_off;
led &= priv->led_association_off;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_led_radio_on(struct ipw_priv *priv)
{
ipw_led_link_on(priv);
}
static void ipw_led_radio_off(struct ipw_priv *priv)
{
ipw_led_activity_off(priv);
ipw_led_link_off(priv);
}
static void ipw_led_link_up(struct ipw_priv *priv)
{
ipw_led_link_on(priv);
}
static void ipw_led_link_down(struct ipw_priv *priv)
{
ipw_led_activity_off(priv);
ipw_led_link_off(priv);
if (priv->status & STATUS_RF_KILL_MASK)
ipw_led_radio_off(priv);
}
static void ipw_led_init(struct ipw_priv *priv)
{
priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
priv->led_activity_on = IPW_ACTIVITY_LED;
priv->led_activity_off = ~(IPW_ACTIVITY_LED);
priv->led_association_on = IPW_ASSOCIATED_LED;
priv->led_association_off = ~(IPW_ASSOCIATED_LED);
priv->led_ofdm_on = IPW_OFDM_LED;
priv->led_ofdm_off = ~(IPW_OFDM_LED);
switch (priv->nic_type) {
case EEPROM_NIC_TYPE_1:
priv->led_activity_on = IPW_ASSOCIATED_LED;
priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
priv->led_association_on = IPW_ACTIVITY_LED;
priv->led_association_off = ~(IPW_ACTIVITY_LED);
if (!(priv->config & CFG_NO_LED))
ipw_led_band_on(priv);
return;
case EEPROM_NIC_TYPE_3:
case EEPROM_NIC_TYPE_2:
case EEPROM_NIC_TYPE_4:
case EEPROM_NIC_TYPE_0:
break;
default:
IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
priv->nic_type);
priv->nic_type = EEPROM_NIC_TYPE_0;
break;
}
if (!(priv->config & CFG_NO_LED)) {
if (priv->status & STATUS_ASSOCIATED)
ipw_led_link_on(priv);
else
ipw_led_link_off(priv);
}
}
static void ipw_led_shutdown(struct ipw_priv *priv)
{
ipw_led_activity_off(priv);
ipw_led_link_off(priv);
ipw_led_band_off(priv);
cancel_delayed_work(&priv->led_link_on);
cancel_delayed_work(&priv->led_link_off);
cancel_delayed_work(&priv->led_act_off);
}
static ssize_t debug_level_show(struct device_driver *d, char *buf)
{
return sprintf(buf, "0x%08X\n", ipw_debug_level);
}
static ssize_t debug_level_store(struct device_driver *d, const char *buf,
size_t count)
{
unsigned long val;
int result = kstrtoul(buf, 0, &val);
if (result == -EINVAL)
printk(KERN_INFO DRV_NAME
": %s is not in hex or decimal form.\n", buf);
else if (result == -ERANGE)
printk(KERN_INFO DRV_NAME
": %s has overflowed.\n", buf);
else
ipw_debug_level = val;
return count;
}
static DRIVER_ATTR_RW(debug_level);
static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
{
return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
}
static void ipw_capture_event_log(struct ipw_priv *priv,
u32 log_len, struct ipw_event *log)
{
u32 base;
if (log_len) {
base = ipw_read32(priv, IPW_EVENT_LOG);
ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
(u8 *) log, sizeof(*log) * log_len);
}
}
static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
{
struct ipw_fw_error *error;
u32 log_len = ipw_get_event_log_len(priv);
u32 base = ipw_read32(priv, IPW_ERROR_LOG);
u32 elem_len = ipw_read_reg32(priv, base);
error = kmalloc(size_add(struct_size(error, elem, elem_len),
array_size(sizeof(*error->log), log_len)),
GFP_ATOMIC);
if (!error) {
IPW_ERROR("Memory allocation for firmware error log "
"failed.\n");
return NULL;
}
error->jiffies = jiffies;
error->status = priv->status;
error->config = priv->config;
error->elem_len = elem_len;
error->log_len = log_len;
error->log = (struct ipw_event *)(error->elem + elem_len);
ipw_capture_event_log(priv, log_len, error->log);
if (elem_len)
ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
sizeof(*error->elem) * elem_len);
return error;
}
static ssize_t event_log_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
u32 log_len = ipw_get_event_log_len(priv);
u32 log_size;
struct ipw_event *log;
u32 len = 0, i;
log_size = PAGE_SIZE / sizeof(*log) > log_len ?
sizeof(*log) * log_len : PAGE_SIZE;
log = kzalloc(log_size, GFP_KERNEL);
if (!log) {
IPW_ERROR("Unable to allocate memory for log\n");
return 0;
}
log_len = log_size / sizeof(*log);
ipw_capture_event_log(priv, log_len, log);
len += scnprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
for (i = 0; i < log_len; i++)
len += scnprintf(buf + len, PAGE_SIZE - len,
"\n%08X%08X%08X",
log[i].time, log[i].event, log[i].data);
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
kfree(log);
return len;
}
static DEVICE_ATTR_RO(event_log);
static ssize_t error_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
u32 len = 0, i;
if (!priv->error)
return 0;
len += scnprintf(buf + len, PAGE_SIZE - len,
"%08lX%08X%08X%08X",
priv->error->jiffies,
priv->error->status,
priv->error->config, priv->error->elem_len);
for (i = 0; i < priv->error->elem_len; i++)
len += scnprintf(buf + len, PAGE_SIZE - len,
"\n%08X%08X%08X%08X%08X%08X%08X",
priv->error->elem[i].time,
priv->error->elem[i].desc,
priv->error->elem[i].blink1,
priv->error->elem[i].blink2,
priv->error->elem[i].link1,
priv->error->elem[i].link2,
priv->error->elem[i].data);
len += scnprintf(buf + len, PAGE_SIZE - len,
"\n%08X", priv->error->log_len);
for (i = 0; i < priv->error->log_len; i++)
len += scnprintf(buf + len, PAGE_SIZE - len,
"\n%08X%08X%08X",
priv->error->log[i].time,
priv->error->log[i].event,
priv->error->log[i].data);
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
return len;
}
static ssize_t error_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
kfree(priv->error);
priv->error = NULL;
return count;
}
static DEVICE_ATTR_RW(error);
static ssize_t cmd_log_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
u32 len = 0, i;
if (!priv->cmdlog)
return 0;
for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
(i != priv->cmdlog_pos) && (len < PAGE_SIZE);
i = (i + 1) % priv->cmdlog_len) {
len +=
scnprintf(buf + len, PAGE_SIZE - len,
"\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
priv->cmdlog[i].cmd.len);
len +=
snprintk_buf(buf + len, PAGE_SIZE - len,
(u8 *) priv->cmdlog[i].cmd.param,
priv->cmdlog[i].cmd.len);
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
}
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
return len;
}
static DEVICE_ATTR_RO(cmd_log);
#ifdef CONFIG_IPW2200_PROMISCUOUS
static void ipw_prom_free(struct ipw_priv *priv);
static int ipw_prom_alloc(struct ipw_priv *priv);
static ssize_t rtap_iface_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
int rc = 0;
if (count < 1)
return -EINVAL;
switch (buf[0]) {
case '0':
if (!rtap_iface)
return count;
if (netif_running(priv->prom_net_dev)) {
IPW_WARNING("Interface is up. Cannot unregister.\n");
return count;
}
ipw_prom_free(priv);
rtap_iface = 0;
break;
case '1':
if (rtap_iface)
return count;
rc = ipw_prom_alloc(priv);
if (!rc)
rtap_iface = 1;
break;
default:
return -EINVAL;
}
if (rc) {
IPW_ERROR("Failed to register promiscuous network "
"device (error %d).\n", rc);
}
return count;
}
static ssize_t rtap_iface_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
if (rtap_iface)
return sprintf(buf, "%s", priv->prom_net_dev->name);
else {
buf[0] = '-';
buf[1] = '1';
buf[2] = '\0';
return 3;
}
}
static DEVICE_ATTR_ADMIN_RW(rtap_iface);
static ssize_t rtap_filter_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
if (!priv->prom_priv) {
IPW_ERROR("Attempting to set filter without "
"rtap_iface enabled.\n");
return -EPERM;
}
priv->prom_priv->filter = simple_strtol(buf, NULL, 0);
IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n",
BIT_ARG16(priv->prom_priv->filter));
return count;
}
static ssize_t rtap_filter_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
return sprintf(buf, "0x%04X",
priv->prom_priv ? priv->prom_priv->filter : 0);
}
static DEVICE_ATTR_ADMIN_RW(rtap_filter);
#endif
static ssize_t scan_age_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
return sprintf(buf, "%d\n", priv->ieee->scan_age);
}
static ssize_t scan_age_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
struct net_device *dev = priv->net_dev;
IPW_DEBUG_INFO("enter\n");
unsigned long val;
int result = kstrtoul(buf, 0, &val);
if (result == -EINVAL || result == -ERANGE) {
IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
} else {
priv->ieee->scan_age = val;
IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
}
IPW_DEBUG_INFO("exit\n");
return count;
}
static DEVICE_ATTR_RW(scan_age);
static ssize_t led_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
}
static ssize_t led_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
IPW_DEBUG_INFO("enter\n");
if (count == 0)
return 0;
if (*buf == 0) {
IPW_DEBUG_LED("Disabling LED control.\n");
priv->config |= CFG_NO_LED;
ipw_led_shutdown(priv);
} else {
IPW_DEBUG_LED("Enabling LED control.\n");
priv->config &= ~CFG_NO_LED;
ipw_led_init(priv);
}
IPW_DEBUG_INFO("exit\n");
return count;
}
static DEVICE_ATTR_RW(led);
static ssize_t status_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct ipw_priv *p = dev_get_drvdata(d);
return sprintf(buf, "0x%08x\n", (int)p->status);
}
static DEVICE_ATTR_RO(status);
static ssize_t cfg_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct ipw_priv *p = dev_get_drvdata(d);
return sprintf(buf, "0x%08x\n", (int)p->config);
}
static DEVICE_ATTR_RO(cfg);
static ssize_t nic_type_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
return sprintf(buf, "TYPE: %d\n", priv->nic_type);
}
static DEVICE_ATTR_RO(nic_type);
static ssize_t ucode_version_show(struct device *d,
struct device_attribute *attr, char *buf)
{
u32 len = sizeof(u32), tmp = 0;
struct ipw_priv *p = dev_get_drvdata(d);
if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
return 0;
return sprintf(buf, "0x%08x\n", tmp);
}
static DEVICE_ATTR_RO(ucode_version);
static ssize_t rtc_show(struct device *d, struct device_attribute *attr,
char *buf)
{
u32 len = sizeof(u32), tmp = 0;
struct ipw_priv *p = dev_get_drvdata(d);
if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
return 0;
return sprintf(buf, "0x%08x\n", tmp);
}
static DEVICE_ATTR_RO(rtc);
static ssize_t eeprom_delay_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct ipw_priv *p = dev_get_drvdata(d);
int n = p->eeprom_delay;
return sprintf(buf, "%i\n", n);
}
static ssize_t eeprom_delay_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *p = dev_get_drvdata(d);
sscanf(buf, "%i", &p->eeprom_delay);
return strnlen(buf, count);
}
static DEVICE_ATTR_RW(eeprom_delay);
static ssize_t command_event_reg_show(struct device *d,
struct device_attribute *attr, char *buf)
{
u32 reg = 0;
struct ipw_priv *p = dev_get_drvdata(d);
reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT);
return sprintf(buf, "0x%08x\n", reg);
}
static ssize_t command_event_reg_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
u32 reg;
struct ipw_priv *p = dev_get_drvdata(d);
sscanf(buf, "%x", ®);
ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg);
return strnlen(buf, count);
}
static DEVICE_ATTR_RW(command_event_reg);
static ssize_t mem_gpio_reg_show(struct device *d,
struct device_attribute *attr, char *buf)
{
u32 reg = 0;
struct ipw_priv *p = dev_get_drvdata(d);
reg = ipw_read_reg32(p, 0x301100);
return sprintf(buf, "0x%08x\n", reg);
}
static ssize_t mem_gpio_reg_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
u32 reg;
struct ipw_priv *p = dev_get_drvdata(d);
sscanf(buf, "%x", ®);
ipw_write_reg32(p, 0x301100, reg);
return strnlen(buf, count);
}
static DEVICE_ATTR_RW(mem_gpio_reg);
static ssize_t indirect_dword_show(struct device *d,
struct device_attribute *attr, char *buf)
{
u32 reg = 0;
struct ipw_priv *priv = dev_get_drvdata(d);
if (priv->status & STATUS_INDIRECT_DWORD)
reg = ipw_read_reg32(priv, priv->indirect_dword);
else
reg = 0;
return sprintf(buf, "0x%08x\n", reg);
}
static ssize_t indirect_dword_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
sscanf(buf, "%x", &priv->indirect_dword);
priv->status |= STATUS_INDIRECT_DWORD;
return strnlen(buf, count);
}
static DEVICE_ATTR_RW(indirect_dword);
static ssize_t indirect_byte_show(struct device *d,
struct device_attribute *attr, char *buf)
{
u8 reg = 0;
struct ipw_priv *priv = dev_get_drvdata(d);
if (priv->status & STATUS_INDIRECT_BYTE)
reg = ipw_read_reg8(priv, priv->indirect_byte);
else
reg = 0;
return sprintf(buf, "0x%02x\n", reg);
}
static ssize_t indirect_byte_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
sscanf(buf, "%x", &priv->indirect_byte);
priv->status |= STATUS_INDIRECT_BYTE;
return strnlen(buf, count);
}
static DEVICE_ATTR_RW(indirect_byte);
static ssize_t direct_dword_show(struct device *d,
struct device_attribute *attr, char *buf)
{
u32 reg = 0;
struct ipw_priv *priv = dev_get_drvdata(d);
if (priv->status & STATUS_DIRECT_DWORD)
reg = ipw_read32(priv, priv->direct_dword);
else
reg = 0;
return sprintf(buf, "0x%08x\n", reg);
}
static ssize_t direct_dword_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
sscanf(buf, "%x", &priv->direct_dword);
priv->status |= STATUS_DIRECT_DWORD;
return strnlen(buf, count);
}
static DEVICE_ATTR_RW(direct_dword);
static int rf_kill_active(struct ipw_priv *priv)
{
if (0 == (ipw_read32(priv, 0x30) & 0x10000)) {
priv->status |= STATUS_RF_KILL_HW;
wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
} else {
priv->status &= ~STATUS_RF_KILL_HW;
wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
}
return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
}
static ssize_t rf_kill_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
(rf_kill_active(priv) ? 0x2 : 0x0);
return sprintf(buf, "%i\n", val);
}
static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
{
if ((disable_radio ? 1 : 0) ==
((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
return 0;
IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
disable_radio ? "OFF" : "ON");
if (disable_radio) {
priv->status |= STATUS_RF_KILL_SW;
cancel_delayed_work(&priv->request_scan);
cancel_delayed_work(&priv->request_direct_scan);
cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->scan_event);
schedule_work(&priv->down);
} else {
priv->status &= ~STATUS_RF_KILL_SW;
if (rf_kill_active(priv)) {
IPW_DEBUG_RF_KILL("Can not turn radio back on - "
"disabled by HW switch\n");
cancel_delayed_work(&priv->rf_kill);
schedule_delayed_work(&priv->rf_kill,
round_jiffies_relative(2 * HZ));
} else
schedule_work(&priv->up);
}
return 1;
}
static ssize_t rf_kill_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
ipw_radio_kill_sw(priv, buf[0] == '1');
return count;
}
static DEVICE_ATTR_RW(rf_kill);
static ssize_t speed_scan_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
int pos = 0, len = 0;
if (priv->config & CFG_SPEED_SCAN) {
while (priv->speed_scan[pos] != 0)
len += sprintf(&buf[len], "%d ",
priv->speed_scan[pos++]);
return len + sprintf(&buf[len], "\n");
}
return sprintf(buf, "0\n");
}
static ssize_t speed_scan_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
int channel, pos = 0;
const char *p = buf;
while ((channel = simple_strtol(p, NULL, 0))) {
if (pos == MAX_SPEED_SCAN - 1) {
priv->speed_scan[pos] = 0;
break;
}
if (libipw_is_valid_channel(priv->ieee, channel))
priv->speed_scan[pos++] = channel;
else
IPW_WARNING("Skipping invalid channel request: %d\n",
channel);
p = strchr(p, ' ');
if (!p)
break;
while (*p == ' ' || *p == '\t')
p++;
}
if (pos == 0)
priv->config &= ~CFG_SPEED_SCAN;
else {
priv->speed_scan_pos = 0;
priv->config |= CFG_SPEED_SCAN;
}
return count;
}
static DEVICE_ATTR_RW(speed_scan);
static ssize_t net_stats_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0');
}
static ssize_t net_stats_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
if (buf[0] == '1')
priv->config |= CFG_NET_STATS;
else
priv->config &= ~CFG_NET_STATS;
return count;
}
static DEVICE_ATTR_RW(net_stats);
static ssize_t channels_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct ipw_priv *priv = dev_get_drvdata(d);
const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
int len = 0, i;
len = sprintf(&buf[len],
"Displaying %d channels in 2.4Ghz band "
"(802.11bg):\n", geo->bg_channels);
for (i = 0; i < geo->bg_channels; i++) {
len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n",
geo->bg[i].channel,
geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT ?
" (radar spectrum)" : "",
((geo->bg[i].flags & LIBIPW_CH_NO_IBSS) ||
(geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT))
? "" : ", IBSS",
geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY ?
"passive only" : "active/passive",
geo->bg[i].flags & LIBIPW_CH_B_ONLY ?
"B" : "B/G");
}
len += sprintf(&buf[len],
"Displaying %d channels in 5.2Ghz band "
"(802.11a):\n", geo->a_channels);
for (i = 0; i < geo->a_channels; i++) {
len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n",
geo->a[i].channel,
geo->a[i].flags & LIBIPW_CH_RADAR_DETECT ?
" (radar spectrum)" : "",
((geo->a[i].flags & LIBIPW_CH_NO_IBSS) ||
(geo->a[i].flags & LIBIPW_CH_RADAR_DETECT))
? "" : ", IBSS",
geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY ?
"passive only" : "active/passive");
}
return len;
}
static DEVICE_ATTR_ADMIN_RO(channels);
static void notify_wx_assoc_event(struct ipw_priv *priv)
{
union iwreq_data wrqu;
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
if (priv->status & STATUS_ASSOCIATED)
memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
else
eth_zero_addr(wrqu.ap_addr.sa_data);
wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
}
static void ipw_irq_tasklet(struct tasklet_struct *t)
{
struct ipw_priv *priv = from_tasklet(priv, t, irq_tasklet);
u32 inta, inta_mask, handled = 0;
unsigned long flags;
spin_lock_irqsave(&priv->irq_lock, flags);
inta = ipw_read32(priv, IPW_INTA_RW);
inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
if (inta == 0xFFFFFFFF) {
IPW_WARNING("TASKLET INTA == 0xFFFFFFFF\n");
inta = 0;
}
inta &= (IPW_INTA_MASK_ALL & inta_mask);
inta |= priv->isr_inta;
spin_unlock_irqrestore(&priv->irq_lock, flags);
spin_lock_irqsave(&priv->lock, flags);
if (inta & IPW_INTA_BIT_RX_TRANSFER) {
ipw_rx(priv);
handled |= IPW_INTA_BIT_RX_TRANSFER;
}
if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) {
IPW_DEBUG_HC("Command completed.\n");
ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
priv->status &= ~STATUS_HCMD_ACTIVE;
wake_up_interruptible(&priv->wait_command_queue);
handled |= IPW_INTA_BIT_TX_CMD_QUEUE;
}
if (inta & IPW_INTA_BIT_TX_QUEUE_1) {
IPW_DEBUG_TX("TX_QUEUE_1\n");
ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
handled |= IPW_INTA_BIT_TX_QUEUE_1;
}
if (inta & IPW_INTA_BIT_TX_QUEUE_2) {
IPW_DEBUG_TX("TX_QUEUE_2\n");
ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
handled |= IPW_INTA_BIT_TX_QUEUE_2;
}
if (inta & IPW_INTA_BIT_TX_QUEUE_3) {
IPW_DEBUG_TX("TX_QUEUE_3\n");
ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
handled |= IPW_INTA_BIT_TX_QUEUE_3;
}
if (inta & IPW_INTA_BIT_TX_QUEUE_4) {
IPW_DEBUG_TX("TX_QUEUE_4\n");
ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
handled |= IPW_INTA_BIT_TX_QUEUE_4;
}
if (inta & IPW_INTA_BIT_STATUS_CHANGE) {
IPW_WARNING("STATUS_CHANGE\n");
handled |= IPW_INTA_BIT_STATUS_CHANGE;
}
if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) {
IPW_WARNING("TX_PERIOD_EXPIRED\n");
handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED;
}
if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
IPW_WARNING("HOST_CMD_DONE\n");
handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
}
if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) {
IPW_WARNING("FW_INITIALIZATION_DONE\n");
handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE;
}
if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
IPW_WARNING("PHY_OFF_DONE\n");
handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
}
if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
priv->status |= STATUS_RF_KILL_HW;
wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
wake_up_interruptible(&priv->wait_command_queue);
priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
cancel_delayed_work(&priv->request_scan);
cancel_delayed_work(&priv->request_direct_scan);
cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->scan_event);
schedule_work(&priv->link_down);
schedule_delayed_work(&priv->rf_kill, 2 * HZ);
handled |= IPW_INTA_BIT_RF_KILL_DONE;
}
if (inta & IPW_INTA_BIT_FATAL_ERROR) {
IPW_WARNING("Firmware error detected. Restarting.\n");
if (priv->error) {
IPW_DEBUG_FW("Sysfs 'error' log already exists.\n");
if (ipw_debug_level & IPW_DL_FW_ERRORS) {
struct ipw_fw_error *error =
ipw_alloc_error_log(priv);
ipw_dump_error_log(priv, error);
kfree(error);
}
} else {
priv->error = ipw_alloc_error_log(priv);
if (priv->error)
IPW_DEBUG_FW("Sysfs 'error' log captured.\n");
else
IPW_DEBUG_FW("Error allocating sysfs 'error' "
"log.\n");
if (ipw_debug_level & IPW_DL_FW_ERRORS)
ipw_dump_error_log(priv, priv->error);
}
if (priv->ieee->sec.encrypt) {
priv->status &= ~STATUS_ASSOCIATED;
notify_wx_assoc_event(priv);
}
priv->status &= ~STATUS_INIT;
priv->status &= ~STATUS_HCMD_ACTIVE;
wake_up_interruptible(&priv->wait_command_queue);
schedule_work(&priv->adapter_restart);
handled |= IPW_INTA_BIT_FATAL_ERROR;
}
if (inta & IPW_INTA_BIT_PARITY_ERROR) {
IPW_ERROR("Parity error\n");
handled |= IPW_INTA_BIT_PARITY_ERROR;
}
if (handled != inta) {
IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
}
spin_unlock_irqrestore(&priv->lock, flags);
ipw_enable_interrupts(priv);
}
#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
static char *get_cmd_string(u8 cmd)
{
switch (cmd) {
IPW_CMD(HOST_COMPLETE);
IPW_CMD(POWER_DOWN);
IPW_CMD(SYSTEM_CONFIG);
IPW_CMD(MULTICAST_ADDRESS);
IPW_CMD(SSID);
IPW_CMD(ADAPTER_ADDRESS);
IPW_CMD(PORT_TYPE);
IPW_CMD(RTS_THRESHOLD);
IPW_CMD(FRAG_THRESHOLD);
IPW_CMD(POWER_MODE);
IPW_CMD(WEP_KEY);
IPW_CMD(TGI_TX_KEY);
IPW_CMD(SCAN_REQUEST);
IPW_CMD(SCAN_REQUEST_EXT);
IPW_CMD(ASSOCIATE);
IPW_CMD(SUPPORTED_RATES);
IPW_CMD(SCAN_ABORT);
IPW_CMD(TX_FLUSH);
IPW_CMD(QOS_PARAMETERS);
IPW_CMD(DINO_CONFIG);
IPW_CMD(RSN_CAPABILITIES);
IPW_CMD(RX_KEY);
IPW_CMD(CARD_DISABLE);
IPW_CMD(SEED_NUMBER);
IPW_CMD(TX_POWER);
IPW_CMD(COUNTRY_INFO);
IPW_CMD(AIRONET_INFO);
IPW_CMD(AP_TX_POWER);
IPW_CMD(CCKM_INFO);
IPW_CMD(CCX_VER_INFO);
IPW_CMD(SET_CALIBRATION);
IPW_CMD(SENSITIVITY_CALIB);
IPW_CMD(RETRY_LIMIT);
IPW_CMD(IPW_PRE_POWER_DOWN);
IPW_CMD(VAP_BEACON_TEMPLATE);
IPW_CMD(VAP_DTIM_PERIOD);
IPW_CMD(EXT_SUPPORTED_RATES);
IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
IPW_CMD(VAP_QUIET_INTERVALS);
IPW_CMD(VAP_CHANNEL_SWITCH);
IPW_CMD(VAP_MANDATORY_CHANNELS);
IPW_CMD(VAP_CELL_PWR_LIMIT);
IPW_CMD(VAP_CF_PARAM_SET);
IPW_CMD(VAP_SET_BEACONING_STATE);
IPW_CMD(MEASUREMENT);
IPW_CMD(POWER_CAPABILITY);
IPW_CMD(SUPPORTED_CHANNELS);
IPW_CMD(TPC_REPORT);
IPW_CMD(WME_INFO);
IPW_CMD(PRODUCTION_COMMAND);
default:
return "UNKNOWN";
}
}
#define HOST_COMPLETE_TIMEOUT HZ
static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
{
int rc = 0;
unsigned long flags;
unsigned long now, end;
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_HCMD_ACTIVE) {
IPW_ERROR("Failed to send %s: Already sending a command.\n",
get_cmd_string(cmd->cmd));
spin_unlock_irqrestore(&priv->lock, flags);
return -EAGAIN;
}
priv->status |= STATUS_HCMD_ACTIVE;
if (priv->cmdlog) {
priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
cmd->len);
priv->cmdlog[priv->cmdlog_pos].retcode = -1;
}
IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
priv->status);
#ifndef DEBUG_CMD_WEP_KEY
if (cmd->cmd == IPW_CMD_WEP_KEY)
IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n");
else
#endif
printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0);
if (rc) {
priv->status &= ~STATUS_HCMD_ACTIVE;
IPW_ERROR("Failed to send %s: Reason %d\n",
get_cmd_string(cmd->cmd), rc);
spin_unlock_irqrestore(&priv->lock, flags);
goto exit;
}
spin_unlock_irqrestore(&priv->lock, flags);
now = jiffies;
end = now + HOST_COMPLETE_TIMEOUT;
again:
rc = wait_event_interruptible_timeout(priv->wait_command_queue,
!(priv->
status & STATUS_HCMD_ACTIVE),
end - now);
if (rc < 0) {
now = jiffies;
if (time_before(now, end))
goto again;
rc = 0;
}
if (rc == 0) {
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_HCMD_ACTIVE) {
IPW_ERROR("Failed to send %s: Command timed out.\n",
get_cmd_string(cmd->cmd));
priv->status &= ~STATUS_HCMD_ACTIVE;
spin_unlock_irqrestore(&priv->lock, flags);
rc = -EIO;
goto exit;
}
spin_unlock_irqrestore(&priv->lock, flags);
} else
rc = 0;
if (priv->status & STATUS_RF_KILL_HW) {
IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
get_cmd_string(cmd->cmd));
rc = -EIO;
goto exit;
}
exit:
if (priv->cmdlog) {
priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
priv->cmdlog_pos %= priv->cmdlog_len;
}
return rc;
}
static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command)
{
struct host_cmd cmd = {
.cmd = command,
};
return __ipw_send_cmd(priv, &cmd);
}
static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len,
const void *data)
{
struct host_cmd cmd = {
.cmd = command,
.len = len,
.param = data,
};
return __ipw_send_cmd(priv, &cmd);
}
static int ipw_send_host_complete(struct ipw_priv *priv)
{
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE);
}
static int ipw_send_system_config(struct ipw_priv *priv)
{
return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG,
sizeof(priv->sys_config),
&priv->sys_config);
}
static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
{
if (!priv || !ssid) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE),
ssid);
}
static int ipw_send_adapter_address(struct ipw_priv *priv, const u8 * mac)
{
if (!priv || !mac) {
IPW_ERROR("Invalid args\n");
return -1;
}
IPW_DEBUG_INFO("%s: Setting MAC to %pM\n",
priv->net_dev->name, mac);
return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN, mac);
}
static void ipw_adapter_restart(void *adapter)
{
struct ipw_priv *priv = adapter;
if (priv->status & STATUS_RF_KILL_MASK)
return;
ipw_down(priv);
if (priv->assoc_network &&
(priv->assoc_network->capability & WLAN_CAPABILITY_IBSS))
ipw_remove_current_network(priv);
if (ipw_up(priv)) {
IPW_ERROR("Failed to up device\n");
return;
}
}
static void ipw_bg_adapter_restart(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, adapter_restart);
mutex_lock(&priv->mutex);
ipw_adapter_restart(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_abort_scan(struct ipw_priv *priv);
#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
static void ipw_scan_check(void *data)
{
struct ipw_priv *priv = data;
if (priv->status & STATUS_SCAN_ABORTING) {
IPW_DEBUG_SCAN("Scan completion watchdog resetting "
"adapter after (%dms).\n",
jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG));
schedule_work(&priv->adapter_restart);
} else if (priv->status & STATUS_SCANNING) {
IPW_DEBUG_SCAN("Scan completion watchdog aborting scan "
"after (%dms).\n",
jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG));
ipw_abort_scan(priv);
schedule_delayed_work(&priv->scan_check, HZ);
}
}
static void ipw_bg_scan_check(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, scan_check.work);
mutex_lock(&priv->mutex);
ipw_scan_check(priv);
mutex_unlock(&priv->mutex);
}
static int ipw_send_scan_request_ext(struct ipw_priv *priv,
struct ipw_scan_request_ext *request)
{
return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT,
sizeof(*request), request);
}
static int ipw_send_scan_abort(struct ipw_priv *priv)
{
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT);
}
static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
{
struct ipw_sensitivity_calib calib = {
.beacon_rssi_raw = cpu_to_le16(sens),
};
return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib),
&calib);
}
static int ipw_send_associate(struct ipw_priv *priv,
struct ipw_associate *associate)
{
if (!priv || !associate) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(*associate),
associate);
}
static int ipw_send_supported_rates(struct ipw_priv *priv,
struct ipw_supported_rates *rates)
{
if (!priv || !rates) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates),
rates);
}
static int ipw_set_random_seed(struct ipw_priv *priv)
{
u32 val;
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
get_random_bytes(&val, sizeof(val));
return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val);
}
static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
{
__le32 v = cpu_to_le32(phy_off);
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(v), &v);
}
static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
{
if (!priv || !power) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power), power);
}
static int ipw_set_tx_power(struct ipw_priv *priv)
{
const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
struct ipw_tx_power tx_power;
s8 max_power;
int i;
memset(&tx_power, 0, sizeof(tx_power));
tx_power.ieee_mode = IPW_G_MODE;
tx_power.num_channels = geo->bg_channels;
for (i = 0; i < geo->bg_channels; i++) {
max_power = geo->bg[i].max_power;
tx_power.channels_tx_power[i].channel_number =
geo->bg[i].channel;
tx_power.channels_tx_power[i].tx_power = max_power ?
min(max_power, priv->tx_power) : priv->tx_power;
}
if (ipw_send_tx_power(priv, &tx_power))
return -EIO;
tx_power.ieee_mode = IPW_B_MODE;
if (ipw_send_tx_power(priv, &tx_power))
return -EIO;
if (priv->ieee->abg_true) {
tx_power.ieee_mode = IPW_A_MODE;
tx_power.num_channels = geo->a_channels;
for (i = 0; i < tx_power.num_channels; i++) {
max_power = geo->a[i].max_power;
tx_power.channels_tx_power[i].channel_number =
geo->a[i].channel;
tx_power.channels_tx_power[i].tx_power = max_power ?
min(max_power, priv->tx_power) : priv->tx_power;
}
if (ipw_send_tx_power(priv, &tx_power))
return -EIO;
}
return 0;
}
static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
{
struct ipw_rts_threshold rts_threshold = {
.rts_threshold = cpu_to_le16(rts),
};
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD,
sizeof(rts_threshold), &rts_threshold);
}
static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
{
struct ipw_frag_threshold frag_threshold = {
.frag_threshold = cpu_to_le16(frag),
};
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD,
sizeof(frag_threshold), &frag_threshold);
}
static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
{
__le32 param;
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
switch (mode) {
case IPW_POWER_BATTERY:
param = cpu_to_le32(IPW_POWER_INDEX_3);
break;
case IPW_POWER_AC:
param = cpu_to_le32(IPW_POWER_MODE_CAM);
break;
default:
param = cpu_to_le32(mode);
break;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param),
¶m);
}
static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
{
struct ipw_retry_limit retry_limit = {
.short_retry_limit = slimit,
.long_retry_limit = llimit
};
if (!priv) {
IPW_ERROR("Invalid args\n");
return -1;
}
return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit),
&retry_limit);
}
static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
{
ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
udelay(p->eeprom_delay);
}
static void eeprom_cs(struct ipw_priv *priv)
{
eeprom_write_reg(priv, 0);
eeprom_write_reg(priv, EEPROM_BIT_CS);
eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
eeprom_write_reg(priv, EEPROM_BIT_CS);
}
static void eeprom_disable_cs(struct ipw_priv *priv)
{
eeprom_write_reg(priv, EEPROM_BIT_CS);
eeprom_write_reg(priv, 0);
eeprom_write_reg(priv, EEPROM_BIT_SK);
}
static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
{
int d = (bit ? EEPROM_BIT_DI : 0);
eeprom_write_reg(p, EEPROM_BIT_CS | d);
eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
}
static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
{
int i;
eeprom_cs(priv);
eeprom_write_bit(priv, 1);
eeprom_write_bit(priv, op & 2);
eeprom_write_bit(priv, op & 1);
for (i = 7; i >= 0; i--) {
eeprom_write_bit(priv, addr & (1 << i));
}
}
static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
{
int i;
u16 r = 0;
eeprom_op(priv, EEPROM_CMD_READ, addr);
eeprom_write_reg(priv, EEPROM_BIT_CS);
for (i = 0; i < 16; i++) {
u32 data = 0;
eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
eeprom_write_reg(priv, EEPROM_BIT_CS);
data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
}
eeprom_write_reg(priv, 0);
eeprom_disable_cs(priv);
return r;
}
static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
{
memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN);
}
static void ipw_read_eeprom(struct ipw_priv *priv)
{
int i;
__le16 *eeprom = (__le16 *) priv->eeprom;
IPW_DEBUG_TRACE(">>\n");
for (i = 0; i < 128; i++)
eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i));
IPW_DEBUG_TRACE("<<\n");
}
static void ipw_eeprom_init_sram(struct ipw_priv *priv)
{
int i;
IPW_DEBUG_TRACE(">>\n");
if (priv->eeprom[EEPROM_VERSION] != 0) {
IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
} else {
IPW_DEBUG_INFO("Enabling FW initialization of SRAM\n");
ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
}
IPW_DEBUG_TRACE("<<\n");
}
static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
{
count >>= 2;
if (!count)
return;
_ipw_write32(priv, IPW_AUTOINC_ADDR, start);
while (count--)
_ipw_write32(priv, IPW_AUTOINC_DATA, 0);
}
static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
{
ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL,
CB_NUMBER_OF_ELEMENTS_SMALL *
sizeof(struct command_block));
}
static int ipw_fw_dma_enable(struct ipw_priv *priv)
{
IPW_DEBUG_FW(">> :\n");
ipw_fw_dma_reset_command_blocks(priv);
ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
IPW_DEBUG_FW("<< :\n");
return 0;
}
static void ipw_fw_dma_abort(struct ipw_priv *priv)
{
u32 control = 0;
IPW_DEBUG_FW(">> :\n");
control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
priv->sram_desc.last_cb_index = 0;
IPW_DEBUG_FW("<<\n");
}
static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
struct command_block *cb)
{
u32 address =
IPW_SHARED_SRAM_DMA_CONTROL +
(sizeof(struct command_block) * index);
IPW_DEBUG_FW(">> :\n");
ipw_write_indirect(priv, address, (u8 *) cb,
(int)sizeof(struct command_block));
IPW_DEBUG_FW("<< :\n");
return 0;
}
static int ipw_fw_dma_kick(struct ipw_priv *priv)
{
u32 control = 0;
u32 index = 0;
IPW_DEBUG_FW(">> :\n");
for (index = 0; index < priv->sram_desc.last_cb_index; index++)
ipw_fw_dma_write_command_block(priv, index,
&priv->sram_desc.cb_list[index]);
ipw_clear_bit(priv, IPW_RESET_REG,
IPW_RESET_REG_MASTER_DISABLED |
IPW_RESET_REG_STOP_MASTER);
control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
IPW_DEBUG_FW("<< :\n");
return 0;
}
static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
{
u32 address;
u32 register_value = 0;
u32 cb_fields_address = 0;
IPW_DEBUG_FW(">> :\n");
address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
IPW_DEBUG_FW_INFO("Current CB is 0x%x\n", address);
register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x\n", register_value);
cb_fields_address = address;
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Control Field is 0x%x\n", register_value);
cb_fields_address += sizeof(u32);
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x\n", register_value);
cb_fields_address += sizeof(u32);
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x\n",
register_value);
cb_fields_address += sizeof(u32);
register_value = ipw_read_reg32(priv, cb_fields_address);
IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x\n", register_value);
IPW_DEBUG_FW(">> :\n");
}
static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
{
u32 current_cb_address = 0;
u32 current_cb_index = 0;
IPW_DEBUG_FW("<< :\n");
current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) /
sizeof(struct command_block);
IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X\n",
current_cb_index, current_cb_address);
IPW_DEBUG_FW(">> :\n");
return current_cb_index;
}
static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
u32 src_address,
u32 dest_address,
u32 length,
int interrupt_enabled, int is_last)
{
u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
CB_DEST_SIZE_LONG;
struct command_block *cb;
u32 last_cb_element = 0;
IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
src_address, dest_address, length);
if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
return -1;
last_cb_element = priv->sram_desc.last_cb_index;
cb = &priv->sram_desc.cb_list[last_cb_element];
priv->sram_desc.last_cb_index++;
if (interrupt_enabled)
control |= CB_INT_ENABLED;
if (is_last)
control |= CB_LAST_VALID;
control |= length;
cb->status = control ^ src_address ^ dest_address;
cb->dest_addr = dest_address;
cb->source_addr = src_address;
cb->control = control;
return 0;
}
static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, dma_addr_t *src_address,
int nr, u32 dest_address, u32 len)
{
int ret, i;
u32 size;
IPW_DEBUG_FW(">>\n");
IPW_DEBUG_FW_INFO("nr=%d dest_address=0x%x len=0x%x\n",
nr, dest_address, len);
for (i = 0; i < nr; i++) {
size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH);
ret = ipw_fw_dma_add_command_block(priv, src_address[i],
dest_address +
i * CB_MAX_LENGTH, size,
0, 0);
if (ret) {
IPW_DEBUG_FW_INFO(": Failed\n");
return -1;
} else
IPW_DEBUG_FW_INFO(": Added new cb\n");
}
IPW_DEBUG_FW("<<\n");
return 0;
}
static int ipw_fw_dma_wait(struct ipw_priv *priv)
{
u32 current_index = 0, previous_index;
u32 watchdog = 0;
IPW_DEBUG_FW(">> :\n");
current_index = ipw_fw_dma_command_block_index(priv);
IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n",
(int)priv->sram_desc.last_cb_index);
while (current_index < priv->sram_desc.last_cb_index) {
udelay(50);
previous_index = current_index;
current_index = ipw_fw_dma_command_block_index(priv);
if (previous_index < current_index) {
watchdog = 0;
continue;
}
if (++watchdog > 400) {
IPW_DEBUG_FW_INFO("Timeout\n");
ipw_fw_dma_dump_command_block(priv);
ipw_fw_dma_abort(priv);
return -1;
}
}
ipw_fw_dma_abort(priv);
ipw_set_bit(priv, IPW_RESET_REG,
IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
IPW_DEBUG_FW("<< dmaWaitSync\n");
return 0;
}
static void ipw_remove_current_network(struct ipw_priv *priv)
{
struct list_head *element, *safe;
struct libipw_network *network = NULL;
unsigned long flags;
spin_lock_irqsave(&priv->ieee->lock, flags);
list_for_each_safe(element, safe, &priv->ieee->network_list) {
network = list_entry(element, struct libipw_network, list);
if (ether_addr_equal(network->bssid, priv->bssid)) {
list_del(element);
list_add_tail(&network->list,
&priv->ieee->network_free_list);
}
}
spin_unlock_irqrestore(&priv->ieee->lock, flags);
}
static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
int timeout)
{
int i = 0;
do {
if ((ipw_read32(priv, addr) & mask) == mask)
return i;
mdelay(10);
i += 10;
} while (i < timeout);
return -ETIME;
}
static int ipw_stop_master(struct ipw_priv *priv)
{
int rc;
IPW_DEBUG_TRACE(">>\n");
ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
rc = ipw_poll_bit(priv, IPW_RESET_REG,
IPW_RESET_REG_MASTER_DISABLED, 100);
if (rc < 0) {
IPW_ERROR("wait for stop master failed after 100ms\n");
return -1;
}
IPW_DEBUG_INFO("stop master %dms\n", rc);
return rc;
}
static void ipw_arc_release(struct ipw_priv *priv)
{
IPW_DEBUG_TRACE(">>\n");
mdelay(5);
ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
mdelay(5);
}
struct fw_chunk {
__le32 address;
__le32 length;
};
static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
{
int rc = 0, i, addr;
u8 cr = 0;
__le16 *image;
image = (__le16 *) data;
IPW_DEBUG_TRACE(">>\n");
rc = ipw_stop_master(priv);
if (rc < 0)
return rc;
for (addr = IPW_SHARED_LOWER_BOUND;
addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
ipw_write32(priv, addr, 0);
}
memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
ipw_arc_release(priv);
ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
mdelay(1);
ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
mdelay(1);
ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
mdelay(1);
ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0);
ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS);
mdelay(1);
for (i = 0; i < len / 2; i++)
ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
le16_to_cpu(image[i]));
ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
for (i = 0; i < 100; i++) {
cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
if (cr & DINO_RXFIFO_DATA)
break;
mdelay(1);
}
if (cr & DINO_RXFIFO_DATA) {
__le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
response_buffer[i] =
cpu_to_le32(ipw_read_reg32(priv,
IPW_BASEBAND_RX_FIFO_READ));
memcpy(&priv->dino_alive, response_buffer,
sizeof(priv->dino_alive));
if (priv->dino_alive.alive_command == 1
&& priv->dino_alive.ucode_valid == 1) {
rc = 0;
IPW_DEBUG_INFO
("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
"of %02d/%02d/%02d %02d:%02d\n",
priv->dino_alive.software_revision,
priv->dino_alive.software_revision,
priv->dino_alive.device_identifier,
priv->dino_alive.device_identifier,
priv->dino_alive.time_stamp[0],
priv->dino_alive.time_stamp[1],
priv->dino_alive.time_stamp[2],
priv->dino_alive.time_stamp[3],
priv->dino_alive.time_stamp[4]);
} else {
IPW_DEBUG_INFO("Microcode is not alive\n");
rc = -EINVAL;
}
} else {
IPW_DEBUG_INFO("No alive response from DINO\n");
rc = -ETIME;
}
ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
return rc;
}
static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
{
int ret = -1;
int offset = 0;
struct fw_chunk *chunk;
int total_nr = 0;
int i;
struct dma_pool *pool;
void **virts;
dma_addr_t *phys;
IPW_DEBUG_TRACE("<< :\n");
virts = kmalloc_array(CB_NUMBER_OF_ELEMENTS_SMALL, sizeof(void *),
GFP_KERNEL);
if (!virts)
return -ENOMEM;
phys = kmalloc_array(CB_NUMBER_OF_ELEMENTS_SMALL, sizeof(dma_addr_t),
GFP_KERNEL);
if (!phys) {
kfree(virts);
return -ENOMEM;
}
pool = dma_pool_create("ipw2200", &priv->pci_dev->dev, CB_MAX_LENGTH, 0,
0);
if (!pool) {
IPW_ERROR("dma_pool_create failed\n");
kfree(phys);
kfree(virts);
return -ENOMEM;
}
ret = ipw_fw_dma_enable(priv);
BUG_ON(priv->sram_desc.last_cb_index > 0);
do {
u32 chunk_len;
u8 *start;
int size;
int nr = 0;
chunk = (struct fw_chunk *)(data + offset);
offset += sizeof(struct fw_chunk);
chunk_len = le32_to_cpu(chunk->length);
start = data + offset;
nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH;
for (i = 0; i < nr; i++) {
virts[total_nr] = dma_pool_alloc(pool, GFP_KERNEL,
&phys[total_nr]);
if (!virts[total_nr]) {
ret = -ENOMEM;
goto out;
}
size = min_t(u32, chunk_len - i * CB_MAX_LENGTH,
CB_MAX_LENGTH);
memcpy(virts[total_nr], start, size);
start += size;
total_nr++;
BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL);
}
ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr],
nr, le32_to_cpu(chunk->address),
chunk_len);
if (ret) {
IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
goto out;
}
offset += chunk_len;
} while (offset < len);
ret = ipw_fw_dma_kick(priv);
if (ret) {
IPW_ERROR("dmaKick Failed\n");
goto out;
}
ret = ipw_fw_dma_wait(priv);
if (ret) {
IPW_ERROR("dmaWaitSync Failed\n");
goto out;
}
out:
for (i = 0; i < total_nr; i++)
dma_pool_free(pool, virts[i], phys[i]);
dma_pool_destroy(pool);
kfree(phys);
kfree(virts);
return ret;
}
static int ipw_stop_nic(struct ipw_priv *priv)
{
int rc = 0;
ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
rc = ipw_poll_bit(priv, IPW_RESET_REG,
IPW_RESET_REG_MASTER_DISABLED, 500);
if (rc < 0) {
IPW_ERROR("wait for reg master disabled failed after 500ms\n");
return rc;
}
ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
return rc;
}
static void ipw_start_nic(struct ipw_priv *priv)
{
IPW_DEBUG_TRACE(">>\n");
ipw_clear_bit(priv, IPW_RESET_REG,
IPW_RESET_REG_MASTER_DISABLED |
IPW_RESET_REG_STOP_MASTER |
CBD_RESET_REG_PRINCETON_RESET);
ipw_set_bit(priv, IPW_GP_CNTRL_RW,
IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
IPW_DEBUG_TRACE("<<\n");
}
static int ipw_init_nic(struct ipw_priv *priv)
{
int rc;
IPW_DEBUG_TRACE(">>\n");
ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
ipw_write32(priv, IPW_READ_INT_REGISTER,
IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
if (rc < 0)
IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
udelay(10);
ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
IPW_DEBUG_TRACE(">>\n");
return 0;
}
static int ipw_reset_nic(struct ipw_priv *priv)
{
int rc = 0;
unsigned long flags;
IPW_DEBUG_TRACE(">>\n");
rc = ipw_init_nic(priv);
spin_lock_irqsave(&priv->lock, flags);
priv->status &= ~STATUS_HCMD_ACTIVE;
wake_up_interruptible(&priv->wait_command_queue);
priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
wake_up_interruptible(&priv->wait_state);
spin_unlock_irqrestore(&priv->lock, flags);
IPW_DEBUG_TRACE("<<\n");
return rc;
}
struct ipw_fw {
__le32 ver;
__le32 boot_size;
__le32 ucode_size;
__le32 fw_size;
u8 data[];
};
static int ipw_get_fw(struct ipw_priv *priv,
const struct firmware **raw, const char *name)
{
struct ipw_fw *fw;
int rc;
rc = request_firmware(raw, name, &priv->pci_dev->dev);
if (rc < 0) {
IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc);
return rc;
}
if ((*raw)->size < sizeof(*fw)) {
IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size);
return -EINVAL;
}
fw = (void *)(*raw)->data;
if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) +
le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) {
IPW_ERROR("%s is too small or corrupt (%zd)\n",
name, (*raw)->size);
return -EINVAL;
}
IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n",
name,
le32_to_cpu(fw->ver) >> 16,
le32_to_cpu(fw->ver) & 0xff,
(*raw)->size - sizeof(*fw));
return 0;
}
#define IPW_RX_BUF_SIZE (3000)
static void ipw_rx_queue_reset(struct ipw_priv *priv,
struct ipw_rx_queue *rxq)
{
unsigned long flags;
int i;
spin_lock_irqsave(&rxq->lock, flags);
INIT_LIST_HEAD(&rxq->rx_free);
INIT_LIST_HEAD(&rxq->rx_used);
for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
if (rxq->pool[i].skb != NULL) {
dma_unmap_single(&priv->pci_dev->dev,
rxq->pool[i].dma_addr,
IPW_RX_BUF_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_irq(rxq->pool[i].skb);
rxq->pool[i].skb = NULL;
}
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
}
rxq->read = rxq->write = 0;
rxq->free_count = 0;
spin_unlock_irqrestore(&rxq->lock, flags);
}
#ifdef CONFIG_PM
static int fw_loaded = 0;
static const struct firmware *raw = NULL;
static void free_firmware(void)
{
if (fw_loaded) {
release_firmware(raw);
raw = NULL;
fw_loaded = 0;
}
}
#else
#define free_firmware() do {} while (0)
#endif
static int ipw_load(struct ipw_priv *priv)
{
#ifndef CONFIG_PM
const struct firmware *raw = NULL;
#endif
struct ipw_fw *fw;
u8 *boot_img, *ucode_img, *fw_img;
u8 *name = NULL;
int rc = 0, retries = 3;
switch (priv->ieee->iw_mode) {
case IW_MODE_ADHOC:
name = "ipw2200-ibss.fw";
break;
#ifdef CONFIG_IPW2200_MONITOR
case IW_MODE_MONITOR:
name = "ipw2200-sniffer.fw";
break;
#endif
case IW_MODE_INFRA:
name = "ipw2200-bss.fw";
break;
}
if (!name) {
rc = -EINVAL;
goto error;
}
#ifdef CONFIG_PM
if (!fw_loaded) {
#endif
rc = ipw_get_fw(priv, &raw, name);
if (rc < 0)
goto error;
#ifdef CONFIG_PM
}
#endif
fw = (void *)raw->data;
boot_img = &fw->data[0];
ucode_img = &fw->data[le32_to_cpu(fw->boot_size)];
fw_img = &fw->data[le32_to_cpu(fw->boot_size) +
le32_to_cpu(fw->ucode_size)];
if (!priv->rxq)
priv->rxq = ipw_rx_queue_alloc(priv);
else
ipw_rx_queue_reset(priv, priv->rxq);
if (!priv->rxq) {
IPW_ERROR("Unable to initialize Rx queue\n");
rc = -ENOMEM;
goto error;
}
retry:
ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
priv->status &= ~STATUS_INT_ENABLED;
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
ipw_stop_nic(priv);
rc = ipw_reset_nic(priv);
if (rc < 0) {
IPW_ERROR("Unable to reset NIC\n");
goto error;
}
ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size));
if (rc < 0) {
IPW_ERROR("Unable to load boot firmware: %d\n", rc);
goto error;
}
ipw_start_nic(priv);
rc = ipw_poll_bit(priv, IPW_INTA_RW,
IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
if (rc < 0) {
IPW_ERROR("device failed to boot initial fw image\n");
goto error;
}
IPW_DEBUG_INFO("initial device response after %dms\n", rc);
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size));
if (rc < 0) {
IPW_ERROR("Unable to load ucode: %d\n", rc);
goto error;
}
ipw_stop_nic(priv);
rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size));
if (rc < 0) {
IPW_ERROR("Unable to load firmware: %d\n", rc);
goto error;
}
#ifdef CONFIG_PM
fw_loaded = 1;
#endif
ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
rc = ipw_queue_reset(priv);
if (rc < 0) {
IPW_ERROR("Unable to initialize queues\n");
goto error;
}
ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
ipw_start_nic(priv);
if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
if (retries > 0) {
IPW_WARNING("Parity error. Retrying init.\n");
retries--;
goto retry;
}
IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
rc = -EIO;
goto error;
}
rc = ipw_poll_bit(priv, IPW_INTA_RW,
IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
if (rc < 0) {
IPW_ERROR("device failed to start within 500ms\n");
goto error;
}
IPW_DEBUG_INFO("device response after %dms\n", rc);
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
priv->eeprom_delay = 1;
ipw_read_eeprom(priv);
ipw_eeprom_init_sram(priv);
ipw_enable_interrupts(priv);
ipw_rx_queue_replenish(priv);
ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
#ifndef CONFIG_PM
release_firmware(raw);
#endif
return 0;
error:
if (priv->rxq) {
ipw_rx_queue_free(priv, priv->rxq);
priv->rxq = NULL;
}
ipw_tx_queue_free(priv);
release_firmware(raw);
#ifdef CONFIG_PM
fw_loaded = 0;
raw = NULL;
#endif
return rc;
}
static int ipw_rx_queue_space(const struct ipw_rx_queue *q)
{
int s = q->read - q->write;
if (s <= 0)
s += RX_QUEUE_SIZE;
s -= 2;
if (s < 0)
s = 0;
return s;
}
static inline int ipw_tx_queue_space(const struct clx2_queue *q)
{
int s = q->last_used - q->first_empty;
if (s <= 0)
s += q->n_bd;
s -= 2;
if (s < 0)
s = 0;
return s;
}
static inline int ipw_queue_inc_wrap(int index, int n_bd)
{
return (++index == n_bd) ? 0 : index;
}
static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
int count, u32 read, u32 write, u32 base, u32 size)
{
q->n_bd = count;
q->low_mark = q->n_bd / 4;
if (q->low_mark < 4)
q->low_mark = 4;
q->high_mark = q->n_bd / 8;
if (q->high_mark < 2)
q->high_mark = 2;
q->first_empty = q->last_used = 0;
q->reg_r = read;
q->reg_w = write;
ipw_write32(priv, base, q->dma_addr);
ipw_write32(priv, size, count);
ipw_write32(priv, read, 0);
ipw_write32(priv, write, 0);
_ipw_read32(priv, 0x90);
}
static int ipw_queue_tx_init(struct ipw_priv *priv,
struct clx2_tx_queue *q,
int count, u32 read, u32 write, u32 base, u32 size)
{
struct pci_dev *dev = priv->pci_dev;
q->txb = kmalloc_array(count, sizeof(q->txb[0]), GFP_KERNEL);
if (!q->txb)
return -ENOMEM;
q->bd =
dma_alloc_coherent(&dev->dev, sizeof(q->bd[0]) * count,
&q->q.dma_addr, GFP_KERNEL);
if (!q->bd) {
IPW_ERROR("dma_alloc_coherent(%zd) failed\n",
sizeof(q->bd[0]) * count);
kfree(q->txb);
q->txb = NULL;
return -ENOMEM;
}
ipw_queue_init(priv, &q->q, count, read, write, base, size);
return 0;
}
static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
struct clx2_tx_queue *txq)
{
struct tfd_frame *bd = &txq->bd[txq->q.last_used];
struct pci_dev *dev = priv->pci_dev;
int i;
if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
return;
if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
IPW_ERROR("Too many chunks: %i\n",
le32_to_cpu(bd->u.data.num_chunks));
return;
}
for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
dma_unmap_single(&dev->dev,
le32_to_cpu(bd->u.data.chunk_ptr[i]),
le16_to_cpu(bd->u.data.chunk_len[i]),
DMA_TO_DEVICE);
if (txq->txb[txq->q.last_used]) {
libipw_txb_free(txq->txb[txq->q.last_used]);
txq->txb[txq->q.last_used] = NULL;
}
}
}
static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
{
struct clx2_queue *q = &txq->q;
struct pci_dev *dev = priv->pci_dev;
if (q->n_bd == 0)
return;
for (; q->first_empty != q->last_used;
q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
ipw_queue_tx_free_tfd(priv, txq);
}
dma_free_coherent(&dev->dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
q->dma_addr);
kfree(txq->txb);
memset(txq, 0, sizeof(*txq));
}
static void ipw_tx_queue_free(struct ipw_priv *priv)
{
ipw_queue_tx_free(priv, &priv->txq_cmd);
ipw_queue_tx_free(priv, &priv->txq[0]);
ipw_queue_tx_free(priv, &priv->txq[1]);
ipw_queue_tx_free(priv, &priv->txq[2]);
ipw_queue_tx_free(priv, &priv->txq[3]);
}
static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
{
bssid[0] = priv->mac_addr[0];
bssid[1] = priv->mac_addr[1];
bssid[2] = priv->mac_addr[2];
get_random_bytes(&bssid[3], ETH_ALEN - 3);
bssid[0] &= 0xfe;
bssid[0] |= 0x02;
}
static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
{
struct ipw_station_entry entry;
int i;
for (i = 0; i < priv->num_stations; i++) {
if (ether_addr_equal(priv->stations[i], bssid)) {
priv->missed_adhoc_beacons = 0;
if (!(priv->config & CFG_STATIC_CHANNEL))
priv->config &= ~CFG_ADHOC_PERSIST;
return i;
}
}
if (i == MAX_STATIONS)
return IPW_INVALID_STATION;
IPW_DEBUG_SCAN("Adding AdHoc station: %pM\n", bssid);
entry.reserved = 0;
entry.support_mode = 0;
memcpy(entry.mac_addr, bssid, ETH_ALEN);
memcpy(priv->stations[i], bssid, ETH_ALEN);
ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
&entry, sizeof(entry));
priv->num_stations++;
return i;
}
static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
{
int i;
for (i = 0; i < priv->num_stations; i++)
if (ether_addr_equal(priv->stations[i], bssid))
return i;
return IPW_INVALID_STATION;
}
static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
{
int err;
if (priv->status & STATUS_ASSOCIATING) {
IPW_DEBUG_ASSOC("Disassociating while associating.\n");
schedule_work(&priv->disassociate);
return;
}
if (!(priv->status & STATUS_ASSOCIATED)) {
IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
return;
}
IPW_DEBUG_ASSOC("Disassociation attempt from %pM "
"on channel %d.\n",
priv->assoc_request.bssid,
priv->assoc_request.channel);
priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
priv->status |= STATUS_DISASSOCIATING;
if (quiet)
priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
else
priv->assoc_request.assoc_type = HC_DISASSOCIATE;
err = ipw_send_associate(priv, &priv->assoc_request);
if (err) {
IPW_DEBUG_HC("Attempt to send [dis]associate command "
"failed.\n");
return;
}
}
static int ipw_disassociate(void *data)
{
struct ipw_priv *priv = data;
if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
return 0;
ipw_send_disassociate(data, 0);
netif_carrier_off(priv->net_dev);
return 1;
}
static void ipw_bg_disassociate(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, disassociate);
mutex_lock(&priv->mutex);
ipw_disassociate(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_system_config(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, system_config);
#ifdef CONFIG_IPW2200_PROMISCUOUS
if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
priv->sys_config.accept_all_data_frames = 1;
priv->sys_config.accept_non_directed_frames = 1;
priv->sys_config.accept_all_mgmt_bcpr = 1;
priv->sys_config.accept_all_mgmt_frames = 1;
}
#endif
ipw_send_system_config(priv);
}
struct ipw_status_code {
u16 status;
const char *reason;
};
static const struct ipw_status_code ipw_status_codes[] = {
{0x00, "Successful"},
{0x01, "Unspecified failure"},
{0x0A, "Cannot support all requested capabilities in the "
"Capability information field"},
{0x0B, "Reassociation denied due to inability to confirm that "
"association exists"},
{0x0C, "Association denied due to reason outside the scope of this "
"standard"},
{0x0D,
"Responding station does not support the specified authentication "
"algorithm"},
{0x0E,
"Received an Authentication frame with authentication sequence "
"transaction sequence number out of expected sequence"},
{0x0F, "Authentication rejected because of challenge failure"},
{0x10, "Authentication rejected due to timeout waiting for next "
"frame in sequence"},
{0x11, "Association denied because AP is unable to handle additional "
"associated stations"},
{0x12,
"Association denied due to requesting station not supporting all "
"of the datarates in the BSSBasicServiceSet Parameter"},
{0x13,
"Association denied due to requesting station not supporting "
"short preamble operation"},
{0x14,
"Association denied due to requesting station not supporting "
"PBCC encoding"},
{0x15,
"Association denied due to requesting station not supporting "
"channel agility"},
{0x19,
"Association denied due to requesting station not supporting "
"short slot operation"},
{0x1A,
"Association denied due to requesting station not supporting "
"DSSS-OFDM operation"},
{0x28, "Invalid Information Element"},
{0x29, "Group Cipher is not valid"},
{0x2A, "Pairwise Cipher is not valid"},
{0x2B, "AKMP is not valid"},
{0x2C, "Unsupported RSN IE version"},
{0x2D, "Invalid RSN IE Capabilities"},
{0x2E, "Cipher suite is rejected per security policy"},
};
static const char *ipw_get_status_code(u16 status)
{
int i;
for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
if (ipw_status_codes[i].status == (status & 0xff))
return ipw_status_codes[i].reason;
return "Unknown status value.";
}
static inline void average_init(struct average *avg)
{
memset(avg, 0, sizeof(*avg));
}
#define DEPTH_RSSI 8
#define DEPTH_NOISE 16
static s16 exponential_average(s16 prev_avg, s16 val, u8 depth)
{
return ((depth-1)*prev_avg + val)/depth;
}
static void average_add(struct average *avg, s16 val)
{
avg->sum -= avg->entries[avg->pos];
avg->sum += val;
avg->entries[avg->pos++] = val;
if (unlikely(avg->pos == AVG_ENTRIES)) {
avg->init = 1;
avg->pos = 0;
}
}
static s16 average_value(struct average *avg)
{
if (!unlikely(avg->init)) {
if (avg->pos)
return avg->sum / avg->pos;
return 0;
}
return avg->sum / AVG_ENTRIES;
}
static void ipw_reset_stats(struct ipw_priv *priv)
{
u32 len = sizeof(u32);
priv->quality = 0;
average_init(&priv->average_missed_beacons);
priv->exp_avg_rssi = -60;
priv->exp_avg_noise = -85 + 0x100;
priv->last_rate = 0;
priv->last_missed_beacons = 0;
priv->last_rx_packets = 0;
priv->last_tx_packets = 0;
priv->last_tx_failures = 0;
ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
&priv->last_rx_err, &len);
ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
&priv->last_tx_failures, &len);
priv->missed_adhoc_beacons = 0;
priv->missed_beacons = 0;
priv->tx_packets = 0;
priv->rx_packets = 0;
}
static u32 ipw_get_max_rate(struct ipw_priv *priv)
{
u32 i = 0x80000000;
u32 mask = priv->rates_mask;
if (priv->assoc_request.ieee_mode == IPW_B_MODE)
mask &= LIBIPW_CCK_RATES_MASK;
while (i && !(mask & i))
i >>= 1;
switch (i) {
case LIBIPW_CCK_RATE_1MB_MASK:
return 1000000;
case LIBIPW_CCK_RATE_2MB_MASK:
return 2000000;
case LIBIPW_CCK_RATE_5MB_MASK:
return 5500000;
case LIBIPW_OFDM_RATE_6MB_MASK:
return 6000000;
case LIBIPW_OFDM_RATE_9MB_MASK:
return 9000000;
case LIBIPW_CCK_RATE_11MB_MASK:
return 11000000;
case LIBIPW_OFDM_RATE_12MB_MASK:
return 12000000;
case LIBIPW_OFDM_RATE_18MB_MASK:
return 18000000;
case LIBIPW_OFDM_RATE_24MB_MASK:
return 24000000;
case LIBIPW_OFDM_RATE_36MB_MASK:
return 36000000;
case LIBIPW_OFDM_RATE_48MB_MASK:
return 48000000;
case LIBIPW_OFDM_RATE_54MB_MASK:
return 54000000;
}
if (priv->ieee->mode == IEEE_B)
return 11000000;
else
return 54000000;
}
static u32 ipw_get_current_rate(struct ipw_priv *priv)
{
u32 rate, len = sizeof(rate);
int err;
if (!(priv->status & STATUS_ASSOCIATED))
return 0;
if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
&len);
if (err) {
IPW_DEBUG_INFO("failed querying ordinals.\n");
return 0;
}
} else
return ipw_get_max_rate(priv);
switch (rate) {
case IPW_TX_RATE_1MB:
return 1000000;
case IPW_TX_RATE_2MB:
return 2000000;
case IPW_TX_RATE_5MB:
return 5500000;
case IPW_TX_RATE_6MB:
return 6000000;
case IPW_TX_RATE_9MB:
return 9000000;
case IPW_TX_RATE_11MB:
return 11000000;
case IPW_TX_RATE_12MB:
return 12000000;
case IPW_TX_RATE_18MB:
return 18000000;
case IPW_TX_RATE_24MB:
return 24000000;
case IPW_TX_RATE_36MB:
return 36000000;
case IPW_TX_RATE_48MB:
return 48000000;
case IPW_TX_RATE_54MB:
return 54000000;
}
return 0;
}
#define IPW_STATS_INTERVAL (2 * HZ)
static void ipw_gather_stats(struct ipw_priv *priv)
{
u32 rx_err, rx_err_delta, rx_packets_delta;
u32 tx_failures, tx_failures_delta, tx_packets_delta;
u32 missed_beacons_percent, missed_beacons_delta;
u32 quality = 0;
u32 len = sizeof(u32);
s16 rssi;
u32 beacon_quality, signal_quality, tx_quality, rx_quality,
rate_quality;
u32 max_rate;
if (!(priv->status & STATUS_ASSOCIATED)) {
priv->quality = 0;
return;
}
ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
&priv->missed_beacons, &len);
missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
priv->last_missed_beacons = priv->missed_beacons;
if (priv->assoc_request.beacon_interval) {
missed_beacons_percent = missed_beacons_delta *
(HZ * le16_to_cpu(priv->assoc_request.beacon_interval)) /
(IPW_STATS_INTERVAL * 10);
} else {
missed_beacons_percent = 0;
}
average_add(&priv->average_missed_beacons, missed_beacons_percent);
ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
rx_err_delta = rx_err - priv->last_rx_err;
priv->last_rx_err = rx_err;
ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
tx_failures_delta = tx_failures - priv->last_tx_failures;
priv->last_tx_failures = tx_failures;
rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
priv->last_rx_packets = priv->rx_packets;
tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
priv->last_tx_packets = priv->tx_packets;
#define BEACON_THRESHOLD 5
beacon_quality = 100 - missed_beacons_percent;
if (beacon_quality < BEACON_THRESHOLD)
beacon_quality = 0;
else
beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
(100 - BEACON_THRESHOLD);
IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
beacon_quality, missed_beacons_percent);
priv->last_rate = ipw_get_current_rate(priv);
max_rate = ipw_get_max_rate(priv);
rate_quality = priv->last_rate * 40 / max_rate + 60;
IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
rate_quality, priv->last_rate / 1000000);
if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
rx_quality = 100 - (rx_err_delta * 100) /
(rx_packets_delta + rx_err_delta);
else
rx_quality = 100;
IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
rx_quality, rx_err_delta, rx_packets_delta);
if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
tx_quality = 100 - (tx_failures_delta * 100) /
(tx_packets_delta + tx_failures_delta);
else
tx_quality = 100;
IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
tx_quality, tx_failures_delta, tx_packets_delta);
rssi = priv->exp_avg_rssi;
signal_quality =
(100 *
(priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
(priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
(priv->ieee->perfect_rssi - rssi) *
(15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
62 * (priv->ieee->perfect_rssi - rssi))) /
((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
(priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
if (signal_quality > 100)
signal_quality = 100;
else if (signal_quality < 1)
signal_quality = 0;
IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
signal_quality, rssi);
quality = min(rx_quality, signal_quality);
quality = min(tx_quality, quality);
quality = min(rate_quality, quality);
quality = min(beacon_quality, quality);
if (quality == beacon_quality)
IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
quality);
if (quality == rate_quality)
IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
quality);
if (quality == tx_quality)
IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
quality);
if (quality == rx_quality)
IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
quality);
if (quality == signal_quality)
IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
quality);
priv->quality = quality;
schedule_delayed_work(&priv->gather_stats, IPW_STATS_INTERVAL);
}
static void ipw_bg_gather_stats(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, gather_stats.work);
mutex_lock(&priv->mutex);
ipw_gather_stats(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_handle_missed_beacon(struct ipw_priv *priv,
int missed_count)
{
priv->notif_missed_beacons = missed_count;
if (missed_count > priv->disassociate_threshold &&
priv->status & STATUS_ASSOCIATED) {
IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
IPW_DL_STATE | IPW_DL_ASSOC,
"Missed beacon: %d - disassociate\n", missed_count);
priv->status &= ~STATUS_ROAMING;
if (priv->status & STATUS_SCANNING) {
IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
IPW_DL_STATE,
"Aborting scan with missed beacon.\n");
schedule_work(&priv->abort_scan);
}
schedule_work(&priv->disassociate);
return;
}
if (priv->status & STATUS_ROAMING) {
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
"Missed beacon: %d - roam in progress\n",
missed_count);
return;
}
if (roaming &&
(missed_count > priv->roaming_threshold &&
missed_count <= priv->disassociate_threshold)) {
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
"Missed beacon: %d - initiate "
"roaming\n", missed_count);
if (!(priv->status & STATUS_ROAMING)) {
priv->status |= STATUS_ROAMING;
if (!(priv->status & STATUS_SCANNING))
schedule_delayed_work(&priv->request_scan, 0);
}
return;
}
if (priv->status & STATUS_SCANNING &&
missed_count > IPW_MB_SCAN_CANCEL_THRESHOLD) {
IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
"Aborting scan with missed beacon.\n");
schedule_work(&priv->abort_scan);
}
IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
}
static void ipw_scan_event(struct work_struct *work)
{
union iwreq_data wrqu;
struct ipw_priv *priv =
container_of(work, struct ipw_priv, scan_event.work);
wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
}
static void handle_scan_event(struct ipw_priv *priv)
{
if (!priv->user_requested_scan) {
schedule_delayed_work(&priv->scan_event,
round_jiffies_relative(msecs_to_jiffies(4000)));
} else {
priv->user_requested_scan = 0;
mod_delayed_work(system_wq, &priv->scan_event, 0);
}
}
static void ipw_rx_notification(struct ipw_priv *priv,
struct ipw_rx_notification *notif)
{
u16 size = le16_to_cpu(notif->size);
IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size);
switch (notif->subtype) {
case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
struct notif_association *assoc = ¬if->u.assoc;
switch (assoc->state) {
case CMAS_ASSOCIATED:{
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC,
"associated: '%*pE' %pM\n",
priv->essid_len, priv->essid,
priv->bssid);
switch (priv->ieee->iw_mode) {
case IW_MODE_INFRA:
memcpy(priv->ieee->bssid,
priv->bssid, ETH_ALEN);
break;
case IW_MODE_ADHOC:
memcpy(priv->ieee->bssid,
priv->bssid, ETH_ALEN);
priv->num_stations = 0;
IPW_DEBUG_ASSOC
("queueing adhoc check\n");
schedule_delayed_work(
&priv->adhoc_check,
le16_to_cpu(priv->
assoc_request.
beacon_interval));
break;
}
priv->status &= ~STATUS_ASSOCIATING;
priv->status |= STATUS_ASSOCIATED;
schedule_work(&priv->system_config);
#ifdef CONFIG_IPW2200_QOS
#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_control))
if ((priv->status & STATUS_AUTH) &&
(IPW_GET_PACKET_STYPE(¬if->u.raw)
== IEEE80211_STYPE_ASSOC_RESP)) {
if ((sizeof
(struct
libipw_assoc_response)
<= size)
&& (size <= 2314)) {
struct
libipw_rx_stats
stats = {
.len = size - 1,
};
IPW_DEBUG_QOS
("QoS Associate "
"size %d\n", size);
libipw_rx_mgt(priv->
ieee,
(struct
libipw_hdr_4addr
*)
¬if->u.raw, &stats);
}
}
#endif
schedule_work(&priv->link_up);
break;
}
case CMAS_AUTHENTICATED:{
if (priv->
status & (STATUS_ASSOCIATED |
STATUS_AUTH)) {
struct notif_authenticate *auth
= ¬if->u.auth;
IPW_DEBUG(IPW_DL_NOTIF |
IPW_DL_STATE |
IPW_DL_ASSOC,
"deauthenticated: '%*pE' %pM: (0x%04X) - %s\n",
priv->essid_len,
priv->essid,
priv->bssid,
le16_to_cpu(auth->status),
ipw_get_status_code
(le16_to_cpu
(auth->status)));
priv->status &=
~(STATUS_ASSOCIATING |
STATUS_AUTH |
STATUS_ASSOCIATED);
schedule_work(&priv->link_down);
break;
}
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC,
"authenticated: '%*pE' %pM\n",
priv->essid_len, priv->essid,
priv->bssid);
break;
}
case CMAS_INIT:{
if (priv->status & STATUS_AUTH) {
struct
libipw_assoc_response
*resp;
resp =
(struct
libipw_assoc_response
*)¬if->u.raw;
IPW_DEBUG(IPW_DL_NOTIF |
IPW_DL_STATE |
IPW_DL_ASSOC,
"association failed (0x%04X): %s\n",
le16_to_cpu(resp->status),
ipw_get_status_code
(le16_to_cpu
(resp->status)));
}
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC,
"disassociated: '%*pE' %pM\n",
priv->essid_len, priv->essid,
priv->bssid);
priv->status &=
~(STATUS_DISASSOCIATING |
STATUS_ASSOCIATING |
STATUS_ASSOCIATED | STATUS_AUTH);
if (priv->assoc_network
&& (priv->assoc_network->
capability &
WLAN_CAPABILITY_IBSS))
ipw_remove_current_network
(priv);
schedule_work(&priv->link_down);
break;
}
case CMAS_RX_ASSOC_RESP:
break;
default:
IPW_ERROR("assoc: unknown (%d)\n",
assoc->state);
break;
}
break;
}
case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
struct notif_authenticate *auth = ¬if->u.auth;
switch (auth->state) {
case CMAS_AUTHENTICATED:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
"authenticated: '%*pE' %pM\n",
priv->essid_len, priv->essid,
priv->bssid);
priv->status |= STATUS_AUTH;
break;
case CMAS_INIT:
if (priv->status & STATUS_AUTH) {
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC,
"authentication failed (0x%04X): %s\n",
le16_to_cpu(auth->status),
ipw_get_status_code(le16_to_cpu
(auth->
status)));
}
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC,
"deauthenticated: '%*pE' %pM\n",
priv->essid_len, priv->essid,
priv->bssid);
priv->status &= ~(STATUS_ASSOCIATING |
STATUS_AUTH |
STATUS_ASSOCIATED);
schedule_work(&priv->link_down);
break;
case CMAS_TX_AUTH_SEQ_1:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "AUTH_SEQ_1\n");
break;
case CMAS_RX_AUTH_SEQ_2:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "AUTH_SEQ_2\n");
break;
case CMAS_AUTH_SEQ_1_PASS:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
break;
case CMAS_AUTH_SEQ_1_FAIL:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
break;
case CMAS_TX_AUTH_SEQ_3:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "AUTH_SEQ_3\n");
break;
case CMAS_RX_AUTH_SEQ_4:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
break;
case CMAS_AUTH_SEQ_2_PASS:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
break;
case CMAS_AUTH_SEQ_2_FAIL:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
break;
case CMAS_TX_ASSOC:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "TX_ASSOC\n");
break;
case CMAS_RX_ASSOC_RESP:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
break;
case CMAS_ASSOCIATED:
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
IPW_DL_ASSOC, "ASSOCIATED\n");
break;
default:
IPW_DEBUG_NOTIF("auth: failure - %d\n",
auth->state);
break;
}
break;
}
case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
struct notif_channel_result *x =
¬if->u.channel_result;
if (size == sizeof(*x)) {
IPW_DEBUG_SCAN("Scan result for channel %d\n",
x->channel_num);
} else {
IPW_DEBUG_SCAN("Scan result of wrong size %d "
"(should be %zd)\n",
size, sizeof(*x));
}
break;
}
case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
struct notif_scan_complete *x = ¬if->u.scan_complete;
if (size == sizeof(*x)) {
IPW_DEBUG_SCAN
("Scan completed: type %d, %d channels, "
"%d status\n", x->scan_type,
x->num_channels, x->status);
} else {
IPW_ERROR("Scan completed of wrong size %d "
"(should be %zd)\n",
size, sizeof(*x));
}
priv->status &=
~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
wake_up_interruptible(&priv->wait_state);
cancel_delayed_work(&priv->scan_check);
if (priv->status & STATUS_EXIT_PENDING)
break;
priv->ieee->scans++;
#ifdef CONFIG_IPW2200_MONITOR
if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
priv->status |= STATUS_SCAN_FORCED;
schedule_delayed_work(&priv->request_scan, 0);
break;
}
priv->status &= ~STATUS_SCAN_FORCED;
#endif /* CONFIG_IPW2200_MONITOR */
if (priv->status & STATUS_DIRECT_SCAN_PENDING)
schedule_delayed_work(&priv->request_direct_scan, 0);
if (!(priv->status & (STATUS_ASSOCIATED |
STATUS_ASSOCIATING |
STATUS_ROAMING |
STATUS_DISASSOCIATING)))
schedule_work(&priv->associate);
else if (priv->status & STATUS_ROAMING) {
if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
schedule_work(&priv->roam);
else
priv->status &= ~STATUS_ROAMING;
} else if (priv->status & STATUS_SCAN_PENDING)
schedule_delayed_work(&priv->request_scan, 0);
else if (priv->config & CFG_BACKGROUND_SCAN
&& priv->status & STATUS_ASSOCIATED)
schedule_delayed_work(&priv->request_scan,
round_jiffies_relative(HZ));
if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
handle_scan_event(priv);
break;
}
case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
struct notif_frag_length *x = ¬if->u.frag_len;
if (size == sizeof(*x))
IPW_ERROR("Frag length: %d\n",
le16_to_cpu(x->frag_length));
else
IPW_ERROR("Frag length of wrong size %d "
"(should be %zd)\n",
size, sizeof(*x));
break;
}
case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
struct notif_link_deterioration *x =
¬if->u.link_deterioration;
if (size == sizeof(*x)) {
IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
"link deterioration: type %d, cnt %d\n",
x->silence_notification_type,
x->silence_count);
memcpy(&priv->last_link_deterioration, x,
sizeof(*x));
} else {
IPW_ERROR("Link Deterioration of wrong size %d "
"(should be %zd)\n",
size, sizeof(*x));
}
break;
}
case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
IPW_ERROR("Dino config\n");
if (priv->hcmd
&& priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
break;
}
case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
struct notif_beacon_state *x = ¬if->u.beacon_state;
if (size != sizeof(*x)) {
IPW_ERROR
("Beacon state of wrong size %d (should "
"be %zd)\n", size, sizeof(*x));
break;
}
if (le32_to_cpu(x->state) ==
HOST_NOTIFICATION_STATUS_BEACON_MISSING)
ipw_handle_missed_beacon(priv,
le32_to_cpu(x->
number));
break;
}
case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
struct notif_tgi_tx_key *x = ¬if->u.tgi_tx_key;
if (size == sizeof(*x)) {
IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
"0x%02x station %d\n",
x->key_state, x->security_type,
x->station_index);
break;
}
IPW_ERROR
("TGi Tx Key of wrong size %d (should be %zd)\n",
size, sizeof(*x));
break;
}
case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
struct notif_calibration *x = ¬if->u.calibration;
if (size == sizeof(*x)) {
memcpy(&priv->calib, x, sizeof(*x));
IPW_DEBUG_INFO("TODO: Calibration\n");
break;
}
IPW_ERROR
("Calibration of wrong size %d (should be %zd)\n",
size, sizeof(*x));
break;
}
case HOST_NOTIFICATION_NOISE_STATS:{
if (size == sizeof(u32)) {
priv->exp_avg_noise =
exponential_average(priv->exp_avg_noise,
(u8) (le32_to_cpu(notif->u.noise.value) & 0xff),
DEPTH_NOISE);
break;
}
IPW_ERROR
("Noise stat is wrong size %d (should be %zd)\n",
size, sizeof(u32));
break;
}
default:
IPW_DEBUG_NOTIF("Unknown notification: "
"subtype=%d,flags=0x%2x,size=%d\n",
notif->subtype, notif->flags, size);
}
}
static int ipw_queue_reset(struct ipw_priv *priv)
{
int rc = 0;
int nTx = 64, nTxCmd = 8;
ipw_tx_queue_free(priv);
rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
IPW_TX_CMD_QUEUE_READ_INDEX,
IPW_TX_CMD_QUEUE_WRITE_INDEX,
IPW_TX_CMD_QUEUE_BD_BASE,
IPW_TX_CMD_QUEUE_BD_SIZE);
if (rc) {
IPW_ERROR("Tx Cmd queue init failed\n");
goto error;
}
rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
IPW_TX_QUEUE_0_READ_INDEX,
IPW_TX_QUEUE_0_WRITE_INDEX,
IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
if (rc) {
IPW_ERROR("Tx 0 queue init failed\n");
goto error;
}
rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
IPW_TX_QUEUE_1_READ_INDEX,
IPW_TX_QUEUE_1_WRITE_INDEX,
IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
if (rc) {
IPW_ERROR("Tx 1 queue init failed\n");
goto error;
}
rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
IPW_TX_QUEUE_2_READ_INDEX,
IPW_TX_QUEUE_2_WRITE_INDEX,
IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
if (rc) {
IPW_ERROR("Tx 2 queue init failed\n");
goto error;
}
rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
IPW_TX_QUEUE_3_READ_INDEX,
IPW_TX_QUEUE_3_WRITE_INDEX,
IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
if (rc) {
IPW_ERROR("Tx 3 queue init failed\n");
goto error;
}
priv->rx_bufs_min = 0;
priv->rx_pend_max = 0;
return rc;
error:
ipw_tx_queue_free(priv);
return rc;
}
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
struct clx2_tx_queue *txq, int qindex)
{
u32 hw_tail;
int used;
struct clx2_queue *q = &txq->q;
hw_tail = ipw_read32(priv, q->reg_r);
if (hw_tail >= q->n_bd) {
IPW_ERROR
("Read index for DMA queue (%d) is out of range [0-%d)\n",
hw_tail, q->n_bd);
goto done;
}
for (; q->last_used != hw_tail;
q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
ipw_queue_tx_free_tfd(priv, txq);
priv->tx_packets++;
}
done:
if ((ipw_tx_queue_space(q) > q->low_mark) &&
(qindex >= 0))
netif_wake_queue(priv->net_dev);
used = q->first_empty - q->last_used;
if (used < 0)
used += q->n_bd;
return used;
}
static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, const void *buf,
int len, int sync)
{
struct clx2_tx_queue *txq = &priv->txq_cmd;
struct clx2_queue *q = &txq->q;
struct tfd_frame *tfd;
if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) {
IPW_ERROR("No space for Tx\n");
return -EBUSY;
}
tfd = &txq->bd[q->first_empty];
txq->txb[q->first_empty] = NULL;
memset(tfd, 0, sizeof(*tfd));
tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
priv->hcmd_seq++;
tfd->u.cmd.index = hcmd;
tfd->u.cmd.length = len;
memcpy(tfd->u.cmd.payload, buf, len);
q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
ipw_write32(priv, q->reg_w, q->first_empty);
_ipw_read32(priv, 0x90);
return 0;
}
static void ipw_rx_queue_restock(struct ipw_priv *priv)
{
struct ipw_rx_queue *rxq = priv->rxq;
struct list_head *element;
struct ipw_rx_mem_buffer *rxb;
unsigned long flags;
int write;
spin_lock_irqsave(&rxq->lock, flags);
write = rxq->write;
while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
element = rxq->rx_free.next;
rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
list_del(element);
ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
rxb->dma_addr);
rxq->queue[rxq->write] = rxb;
rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
rxq->free_count--;
}
spin_unlock_irqrestore(&rxq->lock, flags);
if (rxq->free_count <= RX_LOW_WATERMARK)
schedule_work(&priv->rx_replenish);
if (write != rxq->write)
ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
}
static void ipw_rx_queue_replenish(void *data)
{
struct ipw_priv *priv = data;
struct ipw_rx_queue *rxq = priv->rxq;
struct list_head *element;
struct ipw_rx_mem_buffer *rxb;
unsigned long flags;
spin_lock_irqsave(&rxq->lock, flags);
while (!list_empty(&rxq->rx_used)) {
element = rxq->rx_used.next;
rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
if (!rxb->skb) {
printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
priv->net_dev->name);
break;
}
list_del(element);
rxb->dma_addr =
dma_map_single(&priv->pci_dev->dev, rxb->skb->data,
IPW_RX_BUF_SIZE, DMA_FROM_DEVICE);
list_add_tail(&rxb->list, &rxq->rx_free);
rxq->free_count++;
}
spin_unlock_irqrestore(&rxq->lock, flags);
ipw_rx_queue_restock(priv);
}
static void ipw_bg_rx_queue_replenish(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, rx_replenish);
mutex_lock(&priv->mutex);
ipw_rx_queue_replenish(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
{
int i;
if (!rxq)
return;
for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
if (rxq->pool[i].skb != NULL) {
dma_unmap_single(&priv->pci_dev->dev,
rxq->pool[i].dma_addr,
IPW_RX_BUF_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb(rxq->pool[i].skb);
}
}
kfree(rxq);
}
static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
{
struct ipw_rx_queue *rxq;
int i;
rxq = kzalloc(sizeof(*rxq), GFP_KERNEL);
if (unlikely(!rxq)) {
IPW_ERROR("memory allocation failed\n");
return NULL;
}
spin_lock_init(&rxq->lock);
INIT_LIST_HEAD(&rxq->rx_free);
INIT_LIST_HEAD(&rxq->rx_used);
for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
rxq->read = rxq->write = 0;
rxq->free_count = 0;
return rxq;
}
static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
{
rate &= ~LIBIPW_BASIC_RATE_MASK;
if (ieee_mode == IEEE_A) {
switch (rate) {
case LIBIPW_OFDM_RATE_6MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ?
1 : 0;
case LIBIPW_OFDM_RATE_9MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ?
1 : 0;
case LIBIPW_OFDM_RATE_12MB:
return priv->
rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_18MB:
return priv->
rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_24MB:
return priv->
rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_36MB:
return priv->
rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_48MB:
return priv->
rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_54MB:
return priv->
rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0;
default:
return 0;
}
}
switch (rate) {
case LIBIPW_CCK_RATE_1MB:
return priv->rates_mask & LIBIPW_CCK_RATE_1MB_MASK ? 1 : 0;
case LIBIPW_CCK_RATE_2MB:
return priv->rates_mask & LIBIPW_CCK_RATE_2MB_MASK ? 1 : 0;
case LIBIPW_CCK_RATE_5MB:
return priv->rates_mask & LIBIPW_CCK_RATE_5MB_MASK ? 1 : 0;
case LIBIPW_CCK_RATE_11MB:
return priv->rates_mask & LIBIPW_CCK_RATE_11MB_MASK ? 1 : 0;
}
if (ieee_mode == IEEE_B)
return 0;
switch (rate) {
case LIBIPW_OFDM_RATE_6MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_9MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_12MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_18MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_24MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_36MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_48MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0;
case LIBIPW_OFDM_RATE_54MB:
return priv->rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0;
}
return 0;
}
static int ipw_compatible_rates(struct ipw_priv *priv,
const struct libipw_network *network,
struct ipw_supported_rates *rates)
{
int num_rates, i;
memset(rates, 0, sizeof(*rates));
num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
rates->num_rates = 0;
for (i = 0; i < num_rates; i++) {
if (!ipw_is_rate_in_mask(priv, network->mode,
network->rates[i])) {
if (network->rates[i] & LIBIPW_BASIC_RATE_MASK) {
IPW_DEBUG_SCAN("Adding masked mandatory "
"rate %02X\n",
network->rates[i]);
rates->supported_rates[rates->num_rates++] =
network->rates[i];
continue;
}
IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
network->rates[i], priv->rates_mask);
continue;
}
rates->supported_rates[rates->num_rates++] = network->rates[i];
}
num_rates = min(network->rates_ex_len,
(u8) (IPW_MAX_RATES - num_rates));
for (i = 0; i < num_rates; i++) {
if (!ipw_is_rate_in_mask(priv, network->mode,
network->rates_ex[i])) {
if (network->rates_ex[i] & LIBIPW_BASIC_RATE_MASK) {
IPW_DEBUG_SCAN("Adding masked mandatory "
"rate %02X\n",
network->rates_ex[i]);
rates->supported_rates[rates->num_rates++] =
network->rates[i];
continue;
}
IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
network->rates_ex[i], priv->rates_mask);
continue;
}
rates->supported_rates[rates->num_rates++] =
network->rates_ex[i];
}
return 1;
}
static void ipw_copy_rates(struct ipw_supported_rates *dest,
const struct ipw_supported_rates *src)
{
u8 i;
for (i = 0; i < src->num_rates; i++)
dest->supported_rates[i] = src->supported_rates[i];
dest->num_rates = src->num_rates;
}
static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
u8 modulation, u32 rate_mask)
{
u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ?
LIBIPW_BASIC_RATE_MASK : 0;
if (rate_mask & LIBIPW_CCK_RATE_1MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_1MB;
if (rate_mask & LIBIPW_CCK_RATE_2MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_2MB;
if (rate_mask & LIBIPW_CCK_RATE_5MB_MASK)
rates->supported_rates[rates->num_rates++] = basic_mask |
LIBIPW_CCK_RATE_5MB;
if (rate_mask & LIBIPW_CCK_RATE_11MB_MASK)
rates->supported_rates[rates->num_rates++] = basic_mask |
LIBIPW_CCK_RATE_11MB;
}
static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
u8 modulation, u32 rate_mask)
{
u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ?
LIBIPW_BASIC_RATE_MASK : 0;
if (rate_mask & LIBIPW_OFDM_RATE_6MB_MASK)
rates->supported_rates[rates->num_rates++] = basic_mask |
LIBIPW_OFDM_RATE_6MB;
if (rate_mask & LIBIPW_OFDM_RATE_9MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_OFDM_RATE_9MB;
if (rate_mask & LIBIPW_OFDM_RATE_12MB_MASK)
rates->supported_rates[rates->num_rates++] = basic_mask |
LIBIPW_OFDM_RATE_12MB;
if (rate_mask & LIBIPW_OFDM_RATE_18MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_OFDM_RATE_18MB;
if (rate_mask & LIBIPW_OFDM_RATE_24MB_MASK)
rates->supported_rates[rates->num_rates++] = basic_mask |
LIBIPW_OFDM_RATE_24MB;
if (rate_mask & LIBIPW_OFDM_RATE_36MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_OFDM_RATE_36MB;
if (rate_mask & LIBIPW_OFDM_RATE_48MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_OFDM_RATE_48MB;
if (rate_mask & LIBIPW_OFDM_RATE_54MB_MASK)
rates->supported_rates[rates->num_rates++] =
LIBIPW_OFDM_RATE_54MB;
}
struct ipw_network_match {
struct libipw_network *network;
struct ipw_supported_rates rates;
};
static int ipw_find_adhoc_network(struct ipw_priv *priv,
struct ipw_network_match *match,
struct libipw_network *network,
int roaming)
{
struct ipw_supported_rates rates;
if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
!(network->capability & WLAN_CAPABILITY_IBSS))) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded due to capability mismatch.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
if (unlikely(roaming)) {
if ((network->ssid_len != match->network->ssid_len) ||
memcmp(network->ssid, match->network->ssid,
network->ssid_len)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of non-network ESSID.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
} else {
if ((priv->config & CFG_STATIC_ESSID) &&
((network->ssid_len != priv->essid_len) ||
memcmp(network->ssid, priv->essid,
min(network->ssid_len, priv->essid_len)))) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n",
network->ssid_len, network->ssid,
network->bssid, priv->essid_len,
priv->essid);
return 0;
}
}
if (network->time_stamp[0] < match->network->time_stamp[0]) {
IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n",
match->network->ssid_len, match->network->ssid);
return 0;
} else if (network->time_stamp[1] < match->network->time_stamp[1]) {
IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n",
match->network->ssid_len, match->network->ssid);
return 0;
}
if (priv->ieee->scan_age != 0 &&
time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of age: %ums.\n",
network->ssid_len, network->ssid,
network->bssid,
jiffies_to_msecs(jiffies -
network->last_scanned));
return 0;
}
if ((priv->config & CFG_STATIC_CHANNEL) &&
(network->channel != priv->channel)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n",
network->ssid_len, network->ssid,
network->bssid,
network->channel, priv->channel);
return 0;
}
if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n",
network->ssid_len, network->ssid,
network->bssid,
priv->
capability & CAP_PRIVACY_ON ? "on" : "off",
network->
capability & WLAN_CAPABILITY_PRIVACY ? "on" :
"off");
return 0;
}
if (ether_addr_equal(network->bssid, priv->bssid)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of the same BSSID match: %pM.\n",
network->ssid_len, network->ssid,
network->bssid, priv->bssid);
return 0;
}
if (!libipw_is_valid_mode(priv->ieee, network->mode)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
if (!ipw_compatible_rates(priv, network, &rates)) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
if (rates.num_rates == 0) {
IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of no compatible rates.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
ipw_copy_rates(&match->rates, &rates);
match->network = network;
IPW_DEBUG_MERGE("Network '%*pE (%pM)' is a viable match.\n",
network->ssid_len, network->ssid, network->bssid);
return 1;
}
static void ipw_merge_adhoc_network(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, merge_networks);
struct libipw_network *network = NULL;
struct ipw_network_match match = {
.network = priv->assoc_network
};
if ((priv->status & STATUS_ASSOCIATED) &&
(priv->ieee->iw_mode == IW_MODE_ADHOC)) {
unsigned long flags;
spin_lock_irqsave(&priv->ieee->lock, flags);
list_for_each_entry(network, &priv->ieee->network_list, list) {
if (network != priv->assoc_network)
ipw_find_adhoc_network(priv, &match, network,
1);
}
spin_unlock_irqrestore(&priv->ieee->lock, flags);
if (match.network == priv->assoc_network) {
IPW_DEBUG_MERGE("No better ADHOC in this network to "
"merge to.\n");
return;
}
mutex_lock(&priv->mutex);
if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
IPW_DEBUG_MERGE("remove network %*pE\n",
priv->essid_len, priv->essid);
ipw_remove_current_network(priv);
}
ipw_disassociate(priv);
priv->assoc_network = match.network;
mutex_unlock(&priv->mutex);
return;
}
}
static int ipw_best_network(struct ipw_priv *priv,
struct ipw_network_match *match,
struct libipw_network *network, int roaming)
{
struct ipw_supported_rates rates;
if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
!(network->capability & WLAN_CAPABILITY_ESS)) ||
(priv->ieee->iw_mode == IW_MODE_ADHOC &&
!(network->capability & WLAN_CAPABILITY_IBSS))) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded due to capability mismatch.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
if (unlikely(roaming)) {
if ((network->ssid_len != match->network->ssid_len) ||
memcmp(network->ssid, match->network->ssid,
network->ssid_len)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of non-network ESSID.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
} else {
if ((priv->config & CFG_STATIC_ESSID) &&
((network->ssid_len != priv->essid_len) ||
memcmp(network->ssid, priv->essid,
min(network->ssid_len, priv->essid_len)))) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n",
network->ssid_len, network->ssid,
network->bssid, priv->essid_len,
priv->essid);
return 0;
}
}
if (match->network && match->network->stats.rssi > network->stats.rssi) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because '%*pE (%pM)' has a stronger signal.\n",
network->ssid_len, network->ssid,
network->bssid, match->network->ssid_len,
match->network->ssid, match->network->bssid);
return 0;
}
if (network->last_associate &&
time_after(network->last_associate + (HZ * 3UL), jiffies)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of storming (%ums since last assoc attempt).\n",
network->ssid_len, network->ssid,
network->bssid,
jiffies_to_msecs(jiffies -
network->last_associate));
return 0;
}
if (priv->ieee->scan_age != 0 &&
time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of age: %ums.\n",
network->ssid_len, network->ssid,
network->bssid,
jiffies_to_msecs(jiffies -
network->last_scanned));
return 0;
}
if ((priv->config & CFG_STATIC_CHANNEL) &&
(network->channel != priv->channel)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n",
network->ssid_len, network->ssid,
network->bssid,
network->channel, priv->channel);
return 0;
}
if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n",
network->ssid_len, network->ssid,
network->bssid,
priv->capability & CAP_PRIVACY_ON ? "on" :
"off",
network->capability &
WLAN_CAPABILITY_PRIVACY ? "on" : "off");
return 0;
}
if ((priv->config & CFG_STATIC_BSSID) &&
!ether_addr_equal(network->bssid, priv->bssid)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of BSSID mismatch: %pM.\n",
network->ssid_len, network->ssid,
network->bssid, priv->bssid);
return 0;
}
if (!libipw_is_valid_mode(priv->ieee, network->mode)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
if (!libipw_is_valid_channel(priv->ieee, network->channel)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid channel in current GEO\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
if (!ipw_compatible_rates(priv, network, &rates)) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
if (rates.num_rates == 0) {
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of no compatible rates.\n",
network->ssid_len, network->ssid,
network->bssid);
return 0;
}
ipw_copy_rates(&match->rates, &rates);
match->network = network;
IPW_DEBUG_ASSOC("Network '%*pE (%pM)' is a viable match.\n",
network->ssid_len, network->ssid, network->bssid);
return 1;
}
static void ipw_adhoc_create(struct ipw_priv *priv,
struct libipw_network *network)
{
const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
int i;
switch (libipw_is_valid_channel(priv->ieee, priv->channel)) {
case LIBIPW_52GHZ_BAND:
network->mode = IEEE_A;
i = libipw_channel_to_index(priv->ieee, priv->channel);
BUG_ON(i == -1);
if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) {
IPW_WARNING("Overriding invalid channel\n");
priv->channel = geo->a[0].channel;
}
break;
case LIBIPW_24GHZ_BAND:
if (priv->ieee->mode & IEEE_G)
network->mode = IEEE_G;
else
network->mode = IEEE_B;
i = libipw_channel_to_index(priv->ieee, priv->channel);
BUG_ON(i == -1);
if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) {
IPW_WARNING("Overriding invalid channel\n");
priv->channel = geo->bg[0].channel;
}
break;
default:
IPW_WARNING("Overriding invalid channel\n");
if (priv->ieee->mode & IEEE_A) {
network->mode = IEEE_A;
priv->channel = geo->a[0].channel;
} else if (priv->ieee->mode & IEEE_G) {
network->mode = IEEE_G;
priv->channel = geo->bg[0].channel;
} else {
network->mode = IEEE_B;
priv->channel = geo->bg[0].channel;
}
break;
}
network->channel = priv->channel;
priv->config |= CFG_ADHOC_PERSIST;
ipw_create_bssid(priv, network->bssid);
network->ssid_len = priv->essid_len;
memcpy(network->ssid, priv->essid, priv->essid_len);
memset(&network->stats, 0, sizeof(network->stats));
network->capability = WLAN_CAPABILITY_IBSS;
if (!(priv->config & CFG_PREAMBLE_LONG))
network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
if (priv->capability & CAP_PRIVACY_ON)
network->capability |= WLAN_CAPABILITY_PRIVACY;
network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
network->rates_ex_len = priv->rates.num_rates - network->rates_len;
memcpy(network->rates_ex,
&priv->rates.supported_rates[network->rates_len],
network->rates_ex_len);
network->last_scanned = 0;
network->flags = 0;
network->last_associate = 0;
network->time_stamp[0] = 0;
network->time_stamp[1] = 0;
network->beacon_interval = 100;
network->listen_interval = 10;
network->atim_window = 0;
network->wpa_ie_len = 0;
network->rsn_ie_len = 0;
}
static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
{
struct ipw_tgi_tx_key key;
if (!(priv->ieee->sec.flags & (1 << index)))
return;
key.key_id = index;
memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
key.security_type = type;
key.station_index = 0;
key.flags = 0;
key.tx_counter[0] = cpu_to_le32(0);
key.tx_counter[1] = cpu_to_le32(0);
ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key);
}
static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
{
struct ipw_wep_key key;
int i;
key.cmd_id = DINO_CMD_WEP_KEY;
key.seq_num = 0;
for (i = 0; i < 4; i++) {
key.key_index = i | type;
if (!(priv->ieee->sec.flags & (1 << i))) {
key.key_size = 0;
continue;
}
key.key_size = priv->ieee->sec.key_sizes[i];
memcpy(key.key, priv->ieee->sec.keys[i], key.key_size);
ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key);
}
}
static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
{
if (priv->ieee->host_encrypt)
return;
switch (level) {
case SEC_LEVEL_3:
priv->sys_config.disable_unicast_decryption = 0;
priv->ieee->host_decrypt = 0;
break;
case SEC_LEVEL_2:
priv->sys_config.disable_unicast_decryption = 1;
priv->ieee->host_decrypt = 1;
break;
case SEC_LEVEL_1:
priv->sys_config.disable_unicast_decryption = 0;
priv->ieee->host_decrypt = 0;
break;
case SEC_LEVEL_0:
priv->sys_config.disable_unicast_decryption = 1;
break;
default:
break;
}
}
static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
{
if (priv->ieee->host_encrypt)
return;
switch (level) {
case SEC_LEVEL_3:
priv->sys_config.disable_multicast_decryption = 0;
break;
case SEC_LEVEL_2:
priv->sys_config.disable_multicast_decryption = 1;
break;
case SEC_LEVEL_1:
priv->sys_config.disable_multicast_decryption = 0;
break;
case SEC_LEVEL_0:
priv->sys_config.disable_multicast_decryption = 1;
break;
default:
break;
}
}
static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
{
switch (priv->ieee->sec.level) {
case SEC_LEVEL_3:
if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
ipw_send_tgi_tx_key(priv,
DCT_FLAG_EXT_SECURITY_CCM,
priv->ieee->sec.active_key);
if (!priv->ieee->host_mc_decrypt)
ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
break;
case SEC_LEVEL_2:
if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
ipw_send_tgi_tx_key(priv,
DCT_FLAG_EXT_SECURITY_TKIP,
priv->ieee->sec.active_key);
break;
case SEC_LEVEL_1:
ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
break;
case SEC_LEVEL_0:
default:
break;
}
}
static void ipw_adhoc_check(void *data)
{
struct ipw_priv *priv = data;
if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
!(priv->config & CFG_ADHOC_PERSIST)) {
IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
IPW_DL_STATE | IPW_DL_ASSOC,
"Missed beacon: %d - disassociate\n",
priv->missed_adhoc_beacons);
ipw_remove_current_network(priv);
ipw_disassociate(priv);
return;
}
schedule_delayed_work(&priv->adhoc_check,
le16_to_cpu(priv->assoc_request.beacon_interval));
}
static void ipw_bg_adhoc_check(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, adhoc_check.work);
mutex_lock(&priv->mutex);
ipw_adhoc_check(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_debug_config(struct ipw_priv *priv)
{
IPW_DEBUG_INFO("Scan completed, no valid APs matched "
"[CFG 0x%08X]\n", priv->config);
if (priv->config & CFG_STATIC_CHANNEL)
IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
else
IPW_DEBUG_INFO("Channel unlocked.\n");
if (priv->config & CFG_STATIC_ESSID)
IPW_DEBUG_INFO("ESSID locked to '%*pE'\n",
priv->essid_len, priv->essid);
else
IPW_DEBUG_INFO("ESSID unlocked.\n");
if (priv->config & CFG_STATIC_BSSID)
IPW_DEBUG_INFO("BSSID locked to %pM\n", priv->bssid);
else
IPW_DEBUG_INFO("BSSID unlocked.\n");
if (priv->capability & CAP_PRIVACY_ON)
IPW_DEBUG_INFO("PRIVACY on\n");
else
IPW_DEBUG_INFO("PRIVACY off\n");
IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
}
static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
{
struct ipw_fixed_rate fr;
u32 reg;
u16 mask = 0;
u16 new_tx_rates = priv->rates_mask;
switch (priv->ieee->freq_band) {
case LIBIPW_52GHZ_BAND:
if (priv->rates_mask & ~LIBIPW_OFDM_RATES_MASK) {
IPW_DEBUG_WX
("invalid fixed rate mask in ipw_set_fixed_rate\n");
new_tx_rates = 0;
break;
}
new_tx_rates >>= LIBIPW_OFDM_SHIFT_MASK_A;
break;
default:
if (mode == IEEE_B) {
if (new_tx_rates & ~LIBIPW_CCK_RATES_MASK) {
IPW_DEBUG_WX
("invalid fixed rate mask in ipw_set_fixed_rate\n");
new_tx_rates = 0;
}
break;
}
if (new_tx_rates & ~(LIBIPW_CCK_RATES_MASK |
LIBIPW_OFDM_RATES_MASK)) {
IPW_DEBUG_WX
("invalid fixed rate mask in ipw_set_fixed_rate\n");
new_tx_rates = 0;
break;
}
if (LIBIPW_OFDM_RATE_6MB_MASK & new_tx_rates) {
mask |= (LIBIPW_OFDM_RATE_6MB_MASK >> 1);
new_tx_rates &= ~LIBIPW_OFDM_RATE_6MB_MASK;
}
if (LIBIPW_OFDM_RATE_9MB_MASK & new_tx_rates) {
mask |= (LIBIPW_OFDM_RATE_9MB_MASK >> 1);
new_tx_rates &= ~LIBIPW_OFDM_RATE_9MB_MASK;
}
if (LIBIPW_OFDM_RATE_12MB_MASK & new_tx_rates) {
mask |= (LIBIPW_OFDM_RATE_12MB_MASK >> 1);
new_tx_rates &= ~LIBIPW_OFDM_RATE_12MB_MASK;
}
new_tx_rates |= mask;
break;
}
fr.tx_rates = cpu_to_le16(new_tx_rates);
reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
ipw_write_reg32(priv, reg, *(u32 *) & fr);
}
static void ipw_abort_scan(struct ipw_priv *priv)
{
int err;
if (priv->status & STATUS_SCAN_ABORTING) {
IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
return;
}
priv->status |= STATUS_SCAN_ABORTING;
err = ipw_send_scan_abort(priv);
if (err)
IPW_DEBUG_HC("Request to abort scan failed.\n");
}
static void ipw_add_scan_channels(struct ipw_priv *priv,
struct ipw_scan_request_ext *scan,
int scan_type)
{
int channel_index = 0;
const struct libipw_geo *geo;
int i;
geo = libipw_get_geo(priv->ieee);
if (priv->ieee->freq_band & LIBIPW_52GHZ_BAND) {
int start = channel_index;
for (i = 0; i < geo->a_channels; i++) {
if ((priv->status & STATUS_ASSOCIATED) &&
geo->a[i].channel == priv->channel)
continue;
channel_index++;
scan->channels_list[channel_index] = geo->a[i].channel;
ipw_set_scan_type(scan, channel_index,
geo->a[i].
flags & LIBIPW_CH_PASSIVE_ONLY ?
IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
scan_type);
}
if (start != channel_index) {
scan->channels_list[start] = (u8) (IPW_A_MODE << 6) |
(channel_index - start);
channel_index++;
}
}
if (priv->ieee->freq_band & LIBIPW_24GHZ_BAND) {
int start = channel_index;
if (priv->config & CFG_SPEED_SCAN) {
int index;
u8 channels[LIBIPW_24GHZ_CHANNELS] = {
[0] = 0
};
u8 channel;
while (channel_index < IPW_SCAN_CHANNELS - 1) {
channel =
priv->speed_scan[priv->speed_scan_pos];
if (channel == 0) {
priv->speed_scan_pos = 0;
channel = priv->speed_scan[0];
}
if ((priv->status & STATUS_ASSOCIATED) &&
channel == priv->channel) {
priv->speed_scan_pos++;
continue;
}
if (channels[channel - 1] != 0)
break;
channels[channel - 1] = 1;
priv->speed_scan_pos++;
channel_index++;
scan->channels_list[channel_index] = channel;
index =
libipw_channel_to_index(priv->ieee, channel);
ipw_set_scan_type(scan, channel_index,
geo->bg[index].
flags &
LIBIPW_CH_PASSIVE_ONLY ?
IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
: scan_type);
}
} else {
for (i = 0; i < geo->bg_channels; i++) {
if ((priv->status & STATUS_ASSOCIATED) &&
geo->bg[i].channel == priv->channel)
continue;
channel_index++;
scan->channels_list[channel_index] =
geo->bg[i].channel;
ipw_set_scan_type(scan, channel_index,
geo->bg[i].
flags &
LIBIPW_CH_PASSIVE_ONLY ?
IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
: scan_type);
}
}
if (start != channel_index) {
scan->channels_list[start] = (u8) (IPW_B_MODE << 6) |
(channel_index - start);
}
}
}
static int ipw_passive_dwell_time(struct ipw_priv *priv)
{
if (priv->status & STATUS_ASSOCIATED
&& priv->assoc_network->beacon_interval > 10)
return priv->assoc_network->beacon_interval - 10;
else
return 120;
}
static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct)
{
struct ipw_scan_request_ext scan;
int err = 0, scan_type;
if (!(priv->status & STATUS_INIT) ||
(priv->status & STATUS_EXIT_PENDING))
return 0;
mutex_lock(&priv->mutex);
if (direct && (priv->direct_scan_ssid_len == 0)) {
IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n");
priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
goto done;
}
if (priv->status & STATUS_SCANNING) {
IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n");
priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
STATUS_SCAN_PENDING;
goto done;
}
if (!(priv->status & STATUS_SCAN_FORCED) &&
priv->status & STATUS_SCAN_ABORTING) {
IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
STATUS_SCAN_PENDING;
goto done;
}
if (priv->status & STATUS_RF_KILL_MASK) {
IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n");
priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
STATUS_SCAN_PENDING;
goto done;
}
memset(&scan, 0, sizeof(scan));
scan.full_scan_index = cpu_to_le32(libipw_get_scans(priv->ieee));
if (type == IW_SCAN_TYPE_PASSIVE) {
IPW_DEBUG_WX("use passive scanning\n");
scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN;
scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
cpu_to_le16(ipw_passive_dwell_time(priv));
ipw_add_scan_channels(priv, &scan, scan_type);
goto send_request;
}
if (priv->config & CFG_SPEED_SCAN)
scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
cpu_to_le16(30);
else
scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
cpu_to_le16(20);
scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
cpu_to_le16(20);
scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
cpu_to_le16(ipw_passive_dwell_time(priv));
scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
#ifdef CONFIG_IPW2200_MONITOR
if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
u8 channel;
u8 band = 0;
switch (libipw_is_valid_channel(priv->ieee, priv->channel)) {
case LIBIPW_52GHZ_BAND:
band = (u8) (IPW_A_MODE << 6) | 1;
channel = priv->channel;
break;
case LIBIPW_24GHZ_BAND:
band = (u8) (IPW_B_MODE << 6) | 1;
channel = priv->channel;
break;
default:
band = (u8) (IPW_B_MODE << 6) | 1;
channel = 9;
break;
}
scan.channels_list[0] = band;
scan.channels_list[1] = channel;
ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
cpu_to_le16(2000);
} else {
#endif /* CONFIG_IPW2200_MONITOR */
if (direct) {
err = ipw_send_ssid(priv, priv->direct_scan_ssid,
priv->direct_scan_ssid_len);
if (err) {
IPW_DEBUG_HC("Attempt to send SSID command "
"failed\n");
goto done;
}
scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
} else if ((priv->status & STATUS_ROAMING)
|| (!(priv->status & STATUS_ASSOCIATED)
&& (priv->config & CFG_STATIC_ESSID)
&& (le32_to_cpu(scan.full_scan_index) % 2))) {
err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
if (err) {
IPW_DEBUG_HC("Attempt to send SSID command "
"failed.\n");
goto done;
}
scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
} else
scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
ipw_add_scan_channels(priv, &scan, scan_type);
#ifdef CONFIG_IPW2200_MONITOR
}
#endif
send_request:
err = ipw_send_scan_request_ext(priv, &scan);
if (err) {
IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
goto done;
}
priv->status |= STATUS_SCANNING;
if (direct) {
priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
priv->direct_scan_ssid_len = 0;
} else
priv->status &= ~STATUS_SCAN_PENDING;
schedule_delayed_work(&priv->scan_check, IPW_SCAN_CHECK_WATCHDOG);
done:
mutex_unlock(&priv->mutex);
return err;
}
static void ipw_request_passive_scan(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, request_passive_scan.work);
ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0);
}
static void ipw_request_scan(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, request_scan.work);
ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0);
}
static void ipw_request_direct_scan(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, request_direct_scan.work);
ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1);
}
static void ipw_bg_abort_scan(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, abort_scan);
mutex_lock(&priv->mutex);
ipw_abort_scan(priv);
mutex_unlock(&priv->mutex);
}
static int ipw_wpa_enable(struct ipw_priv *priv, int value)
{
priv->ieee->wpa_enabled = value;
return 0;
}
static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
{
struct libipw_device *ieee = priv->ieee;
struct libipw_security sec = {
.flags = SEC_AUTH_MODE,
};
int ret = 0;
if (value & IW_AUTH_ALG_SHARED_KEY) {
sec.auth_mode = WLAN_AUTH_SHARED_KEY;
ieee->open_wep = 0;
} else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
sec.auth_mode = WLAN_AUTH_OPEN;
ieee->open_wep = 1;
} else if (value & IW_AUTH_ALG_LEAP) {
sec.auth_mode = WLAN_AUTH_LEAP;
ieee->open_wep = 1;
} else
return -EINVAL;
if (ieee->set_security)
ieee->set_security(ieee->dev, &sec);
else
ret = -EOPNOTSUPP;
return ret;
}
static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie,
int wpa_ie_len)
{
ipw_wpa_enable(priv, 1);
}
static int ipw_set_rsn_capa(struct ipw_priv *priv,
char *capabilities, int length)
{
IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length,
capabilities);
}
static int ipw_wx_set_genie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct ipw_priv *priv = libipw_priv(dev);
struct libipw_device *ieee = priv->ieee;
u8 *buf;
int err = 0;
if (wrqu->data.length > MAX_WPA_IE_LEN ||
(wrqu->data.length && extra == NULL))
return -EINVAL;
if (wrqu->data.length) {
buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
if (buf == NULL) {
err = -ENOMEM;
goto out;
}
kfree(ieee->wpa_ie);
ieee->wpa_ie = buf;
ieee->wpa_ie_len = wrqu->data.length;
} else {
kfree(ieee->wpa_ie);
ieee->wpa_ie = NULL;
ieee->wpa_ie_len = 0;
}
ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
out:
return err;
}
static int ipw_wx_get_genie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct ipw_priv *priv = libipw_priv(dev);
struct libipw_device *ieee = priv->ieee;
int err = 0;
if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
wrqu->data.length = 0;
goto out;
}
if (wrqu->data.length < ieee->wpa_ie_len) {
err = -E2BIG;
goto out;
}
wrqu->data.length = ieee->wpa_ie_len;
memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
out:
return err;
}
static int wext_cipher2level(int cipher)
{
switch (cipher) {
case IW_AUTH_CIPHER_NONE:
return SEC_LEVEL_0;
case IW_AUTH_CIPHER_WEP40:
case IW_AUTH_CIPHER_WEP104:
return SEC_LEVEL_1;
case IW_AUTH_CIPHER_TKIP:
return SEC_LEVEL_2;
case IW_AUTH_CIPHER_CCMP:
return SEC_LEVEL_3;
default:
return -1;
}
}
static int ipw_wx_set_auth(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct ipw_priv *priv = libipw_priv(dev);
struct libipw_device *ieee = priv->ieee;
struct iw_param *param = &wrqu->param;
struct lib80211_crypt_data *crypt;
unsigned long flags;
int ret = 0;
switch (param->flags & IW_AUTH_INDEX) {
case IW_AUTH_WPA_VERSION:
break;
case IW_AUTH_CIPHER_PAIRWISE:
ipw_set_hw_decrypt_unicast(priv,
wext_cipher2level(param->value));
break;
case IW_AUTH_CIPHER_GROUP:
ipw_set_hw_decrypt_multicast(priv,
wext_cipher2level(param->value));
break;
case IW_AUTH_KEY_MGMT:
break;
case IW_AUTH_TKIP_COUNTERMEASURES:
crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
break;
flags = crypt->ops->get_flags(crypt->priv);
if (param->value)
flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
else
flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
crypt->ops->set_flags(flags, crypt->priv);
break;
case IW_AUTH_DROP_UNENCRYPTED:{
struct libipw_security sec = {
.flags = SEC_ENABLED,
.enabled = param->value,
};
priv->ieee->drop_unencrypted = param->value;
if (!param->value) {
sec.flags |= SEC_LEVEL;
sec.level = SEC_LEVEL_0;
} else {
sec.flags |= SEC_LEVEL;
sec.level = SEC_LEVEL_1;
}
if (priv->ieee->set_security)
priv->ieee->set_security(priv->ieee->dev, &sec);
break;
}
case IW_AUTH_80211_AUTH_ALG:
ret = ipw_wpa_set_auth_algs(priv, param->value);
break;
case IW_AUTH_WPA_ENABLED:
ret = ipw_wpa_enable(priv, param->value);
ipw_disassociate(priv);
break;
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
ieee->ieee802_1x = param->value;
break;
case IW_AUTH_PRIVACY_INVOKED:
ieee->privacy_invoked = param->value;
break;
default:
return -EOPNOTSUPP;
}
return ret;
}