"README.md" did not exist on "87a3b89f34fb20f644b42fa57d579b1f2833fd4d"
Newer
Older
/******************************************************************************
Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
802.11 status code portion of this file from ethereal-0.10.6:
Copyright 2000, Axis Communications AB
Ethereal - Network traffic analyzer
By Gerald Combs <gerald@ethereal.com>
Copyright 1998 Gerald Combs
This program is free software; you can redistribute it and/or modify it
under the terms of version 2 of the GNU General Public License as
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The full GNU General Public License is included in this distribution in the
file called LICENSE.
Contact Information:
James P. Ketrenos <ipw2100-admin@linux.intel.com>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
******************************************************************************/
#include "ipw2200.h"
#include <linux/version.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 ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
static int debug = 0;
static int channel = 0;
static int mode = 0;
static u32 ipw_debug_level;
static int associate = 1;
static int auto_create = 1;
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; /* def: 0 -- do not create rtap interface */
#endif
#ifdef CONFIG_IPW2200_QOS
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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 ieee80211_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 ieee80211_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 ieee80211_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 ieee80211_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 ieee80211_qos_parameters
*qos_param);
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
*qos_param);
#endif /* CONFIG_IPW2200_QOS */
Benoit Boissinot
committed
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, 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_bg_down(struct work_struct *work);
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)
out = snprintf(buf, count, "%08X", ofs);
for (l = 0, i = 0; i < 2; i++) {
out += snprintf(buf + out, count - out, " ");
for (j = 0; j < 8 && l < len; j++, l++)
out += snprintf(buf + out, count - out, "%02X ",
data[(i * 8 + j)]);
for (; j < 8; j++)
out += snprintf(buf + out, count - out, " ");
}
out += snprintf(buf + out, count - out, " ");
for (l = 0, i = 0; i < 2; i++) {
out += snprintf(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 += snprintf(buf + out, count - out, "%c", c);
}
for (; j < 8; j++)
out += snprintf(buf + out, count - 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;
}
/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
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));
/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
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));
/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
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));
/* 8-bit direct write (low 4K) */
#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
#define ipw_write8(ipw, ofs, val) \
IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write8(ipw, ofs, val)
/* 16-bit direct write (low 4K) */
#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
#define ipw_write16(ipw, ofs, val) \
IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write16(ipw, ofs, val)
/* 32-bit direct write (low 4K) */
#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
#define ipw_write32(ipw, ofs, val) \
IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write32(ipw, ofs, val)
/* 8-bit direct read (low 4K) */
#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
/* 8-bit direct read (low 4K), with debug wrapper */
static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
{
IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
/* 16-bit direct read (low 4K) */
#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
/* 16-bit direct read (low 4K), with debug wrapper */
static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
{
IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
/* 32-bit direct read (low 4K) */
#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
/* 32-bit direct read (low 4K), with debug wrapper */
static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
{
IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
/* multi-byte read (above 4K), with debug wrapper */
static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
static inline void __ipw_read_indirect(const char *f, int l,
struct ipw_priv *a, u32 b, u8 * c, int d)
{
IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
d);
_ipw_read_indirect(a, b, c, d);
}
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
int num);
#define ipw_write_indirect(a, b, c, d) \
IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
_ipw_write_indirect(a, b, c, d)
/* 32-bit indirect write (above 4K) */
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);
/* 8-bit indirect write (above 4K) */
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
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);
/* 16-bit indirect write (above 4K) */
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
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);
/* 8-bit indirect read (above 4K) */
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
u32 word;
_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
word = _ipw_read32(priv, IPW_INDIRECT_DATA);
return (word >> ((reg & 0x3) * 8)) & 0xff;
/* 32-bit indirect read (above 4K) */
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;
}
/* General purpose, no alignment requirement, iterative (multi-byte) read, */
/* for area above 1st 4K of SRAM/reg space */
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
int num)
{
u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
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;
}
/* Read the first dword (or portion) byte by byte */
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
/* Start reading at aligned_addr + dif_len */
for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
/* Read all of the middle dwords as dwords, with auto-increment */
_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);
/* Read the last dword (or portion) byte by byte */
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = 0; num > 0; i++, num--)
*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/* for area above 1st 4K of SRAM/reg space */
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
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;
}
/* Write the first dword (or portion) byte by byte */
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
/* Start writing at aligned_addr + dif_len */
for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
/* Write all of the middle dwords as dwords, with auto-increment */
_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);
/* Write the last dword (or portion) byte by byte */
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = 0; num > 0; i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/* for 1st 4K of SRAM/regs space */
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
int num)
{
memcpy_toio((priv->hw_base + addr), buf, num);
}
/* Set bit(s) in low 4K of SRAM/regs */
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}
/* Clear bit(s) in low 4K of SRAM/regs */
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_MEMORY_UNDERFLOW:
case IPW_FW_ERROR_MEMORY_OVERFLOW:
case IPW_FW_ERROR_BAD_CHECKSUM:
case IPW_FW_ERROR_NMI_INTERRUPT:
case IPW_FW_ERROR_BAD_DATABASE:
case IPW_FW_ERROR_DMA_UNDERRUN:
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_FATAL_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;
}
/* verify device ordinal tables have been initialized */
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:
/*
* TABLE 0: Direct access to a table of 32 bit values
*
* This is a very simple table with the data directly
* read from the table
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */
if (ord > priv->table0_len) {
IPW_DEBUG_ORD("ordinal value (%i) longer then "
"max (%i)\n", ord, priv->table0_len);
return -EINVAL;
}
/* verify we have enough room to store the value */
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));
*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
break;
case IPW_ORD_TABLE_1_MASK:
/*
* TABLE 1: Indirect access to a table of 32 bit values
*
* This is a fairly large table of u32 values each
* representing starting addr for the data (which is
* also a u32)
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */
if (ord > priv->table1_len) {
IPW_DEBUG_ORD("ordinal value too long\n");
return -EINVAL;
}
/* verify we have enough room to store the value */
if (*len < sizeof(u32)) {
IPW_DEBUG_ORD("ordinal buffer length too small, "
"need %zd\n", sizeof(u32));
*((u32 *) val) =
ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
*len = sizeof(u32);
break;
case IPW_ORD_TABLE_2_MASK:
/*
* TABLE 2: Indirect access to a table of variable sized values
*
* This table consist of six values, each containing
* - dword containing the starting offset of the data
* - dword containing the lengh in the first 16bits
* and the count in the second 16bits
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */
if (ord > priv->table2_len) {
IPW_DEBUG_ORD("ordinal value too long\n");
return -EINVAL;
}
/* get the address of statistic */
addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
/* get the second DW of statistics ;
* two 16-bit words - first is length, second is count */
field_info =
ipw_read_reg32(priv,
priv->table2_addr + (ord << 3) +
sizeof(u32));
field_len = *((u16 *) & field_info);
field_count = *(((u16 *) & field_info) + 1);
/* abort if not enought memory */
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, "
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; /* use first two bytes */
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;
}
/*
* LED behavior:
* - On radio ON, turn on any LEDs that require to be on during start
* - On initialization, start unassociated blink
* - On association, disable unassociated blink
* - On disassociation, start unassociated blink
* - On radio OFF, turn off any LEDs started during radio on
*
*/
#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 configured to not use LEDs, or nic_type is 1,
* then we don't toggle a LINK 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 we aren't associated, schedule turning the LED off */
if (!(priv->status & STATUS_ASSOCIATED))
queue_delayed_work(priv->workqueue,
&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);
static void ipw_led_link_off(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
/* If configured not to use LEDs, or nic type is 1,
* then we don't goggle the LINK 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 we aren't associated and the radio is on, schedule
* turning the LED on (blink while unassociated) */
if (!(priv->status & STATUS_RF_KILL_MASK) &&
!(priv->status & STATUS_ASSOCIATED))
queue_delayed_work(priv->workqueue, &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);
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);
queue_delayed_work(priv->workqueue, &priv->led_act_off,
LD_TIME_ACT_ON);
} else {
/* Reschedule LED off for full time period */
cancel_delayed_work(&priv->led_act_off);
queue_delayed_work(priv->workqueue, &priv->led_act_off,
LD_TIME_ACT_ON);
}
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);
}
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);
static void ipw_led_band_on(struct ipw_priv *priv)