ipw2200.c 327 KB
Newer Older
James Ketrenos's avatar
James Ketrenos committed
1
/******************************************************************************
2

3
  Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
James Ketrenos's avatar
James Ketrenos committed
4 5 6 7 8 9 10

  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

11 12
  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
James Ketrenos's avatar
James Ketrenos committed
13
  published by the Free Software Foundation.
14 15 16 17

  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
James Ketrenos's avatar
James Ketrenos committed
18
  more details.
19

James Ketrenos's avatar
James Ketrenos committed
20
  You should have received a copy of the GNU General Public License along with
21
  this program; if not, write to the Free Software Foundation, Inc., 59
James Ketrenos's avatar
James Ketrenos committed
22
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23

James Ketrenos's avatar
James Ketrenos committed
24 25
  The full GNU General Public License is included in this distribution in the
  file called LICENSE.
26

James Ketrenos's avatar
James Ketrenos committed
27
  Contact Information:
28
  Intel Linux Wireless <ilw@linux.intel.com>
James Ketrenos's avatar
James Ketrenos committed
29 30 31 32
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

******************************************************************************/

33
#include <linux/sched.h>
34
#include <linux/slab.h>
35
#include <net/cfg80211-wext.h>
James Ketrenos's avatar
James Ketrenos committed
36 37
#include "ipw2200.h"

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

#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

63
#ifdef CONFIG_IPW2200_RADIOTAP
64 65 66 67 68 69 70 71 72 73 74
#define VR "r"
#else
#define VR
#endif

#ifdef CONFIG_IPW2200_QOS
#define VQ "q"
#else
#define VQ
#endif

75
#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ
James Ketrenos's avatar
James Ketrenos committed
76
#define DRV_DESCRIPTION	"Intel(R) PRO/Wireless 2200/2915 Network Driver"
77
#define DRV_COPYRIGHT	"Copyright(c) 2003-2006 Intel Corporation"
James Ketrenos's avatar
James Ketrenos committed
78 79
#define DRV_VERSION     IPW2200_VERSION

80 81
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)

James Ketrenos's avatar
James Ketrenos committed
82 83 84 85
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
86 87 88 89 90
MODULE_FIRMWARE("ipw2200-ibss.fw");
#ifdef CONFIG_IPW2200_MONITOR
MODULE_FIRMWARE("ipw2200-sniffer.fw");
#endif
MODULE_FIRMWARE("ipw2200-bss.fw");
James Ketrenos's avatar
James Ketrenos committed
91

92
static int cmdlog = 0;
James Ketrenos's avatar
James Ketrenos committed
93
static int debug = 0;
Reinette Chatre's avatar
Reinette Chatre committed
94 95
static int default_channel = 0;
static int network_mode = 0;
James Ketrenos's avatar
James Ketrenos committed
96 97

static u32 ipw_debug_level;
98
static int associate;
James Ketrenos's avatar
James Ketrenos committed
99
static int auto_create = 1;
100
static int led_support = 1;
James Ketrenos's avatar
James Ketrenos committed
101
static int disable = 0;
102
static int bt_coexist = 0;
103
static int hwcrypto = 0;
104
static int roaming = 1;
James Ketrenos's avatar
James Ketrenos committed
105 106 107
static const char ipw_modes[] = {
	'a', 'b', 'g', '?'
};
108
static int antenna = CFG_SYS_ANTENNA_BOTH;
James Ketrenos's avatar
James Ketrenos committed
109

110 111 112 113
#ifdef CONFIG_IPW2200_PROMISCUOUS
static int rtap_iface = 0;     /* def: 0 -- do not create rtap interface */
#endif

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
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
133

134 135 136 137 138 139 140 141
/* Ugly macro to convert literal channel numbers into their mhz equivalents
 * There are certianly some conditions that will break this (like feeding it '30')
 * but they shouldn't arise since nothing talks on channel 30. */
#define ieee80211chan2mhz(x) \
	(((x) <= 14) ? \
	(((x) == 14) ? 2484 : ((x) * 5) + 2407) : \
	((x) + 1000) * 5)

142
#ifdef CONFIG_IPW2200_QOS
143 144 145 146 147 148
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;

149
static struct libipw_qos_parameters def_qos_parameters_OFDM = {
150 151 152 153 154 155 156 157 158 159
	{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}
};

160
static struct libipw_qos_parameters def_qos_parameters_CCK = {
161 162 163 164 165 166 167 168 169 170
	{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}
};

171
static struct libipw_qos_parameters def_parameters_OFDM = {
172 173 174 175 176 177 178 179 180 181
	{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}
};

182
static struct libipw_qos_parameters def_parameters_CCK = {
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	{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);

202
static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters
203
				       *qos_param);
204
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element
205
				     *qos_param);
206
#endif				/* CONFIG_IPW2200_QOS */
207

208
static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
209
static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos's avatar
James Ketrenos committed
210
static void ipw_rx(struct ipw_priv *priv);
211
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos's avatar
James Ketrenos committed
212 213 214 215 216 217 218 219 220 221 222 223
				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 *);
David Howells's avatar
David Howells committed
224
static void ipw_bg_up(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
225
static void ipw_down(struct ipw_priv *);
David Howells's avatar
David Howells committed
226
static void ipw_bg_down(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
227
static int ipw_config(struct ipw_priv *);
228 229
static int init_supported_rates(struct ipw_priv *priv,
				struct ipw_supported_rates *prates);
230 231
static void ipw_set_hwcrypto_keys(struct ipw_priv *);
static void ipw_send_wep_keys(struct ipw_priv *, int);
James Ketrenos's avatar
James Ketrenos committed
232

233 234
static int snprint_line(char *buf, size_t count,
			const u8 * data, u32 len, u32 ofs)
James Ketrenos's avatar
James Ketrenos committed
235 236 237
{
	int out, i, j, l;
	char c;
238

James Ketrenos's avatar
James Ketrenos committed
239 240 241 242
	out = snprintf(buf, count, "%08X", ofs);

	for (l = 0, i = 0; i < 2; i++) {
		out += snprintf(buf + out, count - out, " ");
243 244
		for (j = 0; j < 8 && l < len; j++, l++)
			out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos's avatar
James Ketrenos committed
245 246 247 248
					data[(i * 8 + j)]);
		for (; j < 8; j++)
			out += snprintf(buf + out, count - out, "   ");
	}
249

James Ketrenos's avatar
James Ketrenos committed
250 251 252 253 254 255 256
	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 = '.';
257

James Ketrenos's avatar
James Ketrenos committed
258 259 260 261 262 263
			out += snprintf(buf + out, count - out, "%c", c);
		}

		for (; j < 8; j++)
			out += snprintf(buf + out, count - out, " ");
	}
264

265
	return out;
James Ketrenos's avatar
James Ketrenos committed
266 267
}

268
static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos's avatar
James Ketrenos committed
269 270 271 272 273 274 275
{
	char line[81];
	u32 ofs = 0;
	if (!(ipw_debug_level & level))
		return;

	while (len) {
276 277 278
		snprint_line(line, sizeof(line), &data[ofs],
			     min(len, 16U), ofs);
		printk(KERN_DEBUG "%s\n", line);
James Ketrenos's avatar
James Ketrenos committed
279 280 281 282 283
		ofs += 16;
		len -= min(len, 16U);
	}
}

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
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;
}

303
/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
304 305 306
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)

307
/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
308 309 310
static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)

311
/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
312 313 314
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)
{
315 316
	IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
		     __LINE__, (u32) (b), (u32) (c));
James Ketrenos's avatar
James Ketrenos committed
317 318 319
	_ipw_write_reg8(a, b, c);
}

320
/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
321 322 323
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)
{
324 325
	IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
		     __LINE__, (u32) (b), (u32) (c));
James Ketrenos's avatar
James Ketrenos committed
326 327 328
	_ipw_write_reg16(a, b, c);
}

329
/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
330 331 332
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)
{
333 334
	IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
		     __LINE__, (u32) (b), (u32) (c));
James Ketrenos's avatar
James Ketrenos committed
335 336 337
	_ipw_write_reg32(a, b, c);
}

338
/* 8-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
339 340 341 342 343
static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs,
		u8 val)
{
	writeb(val, ipw->hw_base + ofs);
}
344 345

/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
346
#define ipw_write8(ipw, ofs, val) do { \
Jiri Slaby's avatar
Jiri Slaby committed
347 348 349 350
	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)
James Ketrenos's avatar
James Ketrenos committed
351

352
/* 16-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
353 354 355 356 357
static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs,
		u16 val)
{
	writew(val, ipw->hw_base + ofs);
}
358 359

/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
360 361 362 363 364
#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)
James Ketrenos's avatar
James Ketrenos committed
365

366
/* 32-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
367 368 369 370 371
static inline void _ipw_write32(struct ipw_priv *ipw, unsigned long ofs,
		u32 val)
{
	writel(val, ipw->hw_base + ofs);
}
372 373

/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
374 375 376 377 378
#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)
James Ketrenos's avatar
James Ketrenos committed
379

380
/* 8-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
381
static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs)
382
{
Jiri Slaby's avatar
Jiri Slaby committed
383
	return readb(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
384
}
385

386
/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
387 388 389 390 391
#define ipw_read8(ipw, ofs) ({ \
	IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", __FILE__, __LINE__, \
			(u32)(ofs)); \
	_ipw_read8(ipw, ofs); \
})
James Ketrenos's avatar
James Ketrenos committed
392

393
/* 16-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
394
static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs)
395
{
Jiri Slaby's avatar
Jiri Slaby committed
396
	return readw(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
397
}
398

399
/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
400 401 402 403 404
#define ipw_read16(ipw, ofs) ({ \
	IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", __FILE__, __LINE__, \
			(u32)(ofs)); \
	_ipw_read16(ipw, ofs); \
})
James Ketrenos's avatar
James Ketrenos committed
405

406
/* 32-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
407
static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs)
408
{
Jiri Slaby's avatar
Jiri Slaby committed
409
	return readl(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
410
}
411

412
/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
413 414 415 416 417
#define ipw_read32(ipw, ofs) ({ \
	IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", __FILE__, __LINE__, \
			(u32)(ofs)); \
	_ipw_read32(ipw, ofs); \
})
James Ketrenos's avatar
James Ketrenos committed
418 419

static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
420
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
421 422 423 424 425
#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); \
})
James Ketrenos's avatar
James Ketrenos committed
426

427
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
428 429
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
				int num);
Jiri Slaby's avatar
Jiri Slaby committed
430 431 432 433 434
#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)
James Ketrenos's avatar
James Ketrenos committed
435

436
/* 32-bit indirect write (above 4K) */
437
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos's avatar
James Ketrenos committed
438
{
439
	IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
440 441
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	_ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos's avatar
James Ketrenos committed
442 443
}

444
/* 8-bit indirect write (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
445 446
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
447
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
448 449
	u32 dif_len = reg - aligned_addr;

James Ketrenos's avatar
James Ketrenos committed
450
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
451 452
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
453 454
}

455
/* 16-bit indirect write (above 4K) */
456
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos's avatar
James Ketrenos committed
457
{
458
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
459 460
	u32 dif_len = (reg - aligned_addr) & (~0x1ul);

James Ketrenos's avatar
James Ketrenos committed
461
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
462 463
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
464 465
}

466
/* 8-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
467 468 469
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
	u32 word;
470
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
471
	IPW_DEBUG_IO(" reg = 0x%8X :\n", reg);
472
	word = _ipw_read32(priv, IPW_INDIRECT_DATA);
473
	return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos's avatar
James Ketrenos committed
474 475
}

476
/* 32-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
477 478 479 480 481 482
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
{
	u32 value;

	IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);

483 484
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	value = _ipw_read32(priv, IPW_INDIRECT_DATA);
485
	IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x\n", reg, value);
James Ketrenos's avatar
James Ketrenos committed
486 487 488
	return value;
}

489 490
/* General purpose, no alignment requirement, iterative (multi-byte) read, */
/*    for area above 1st 4K of SRAM/reg space */
James Ketrenos's avatar
James Ketrenos committed
491 492 493
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
			       int num)
{
494
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
495 496
	u32 dif_len = addr - aligned_addr;
	u32 i;
497

James Ketrenos's avatar
James Ketrenos committed
498 499
	IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);

500 501 502 503
	if (num <= 0) {
		return;
	}

504
	/* Read the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
505
	if (unlikely(dif_len)) {
506
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenos's avatar
James Ketrenos committed
507
		/* Start reading at aligned_addr + dif_len */
508
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
509
			*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos's avatar
James Ketrenos committed
510 511 512
		aligned_addr += 4;
	}

513
	/* Read all of the middle dwords as dwords, with auto-increment */
514
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
515
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
516
		*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
517

518
	/* Read the last dword (or portion) byte by byte */
519
	if (unlikely(num)) {
520
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
521
		for (i = 0; num > 0; i++, num--)
522
			*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
523
	}
James Ketrenos's avatar
James Ketrenos committed
524 525
}

526 527
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for area above 1st 4K of SRAM/reg space */
528
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos's avatar
James Ketrenos committed
529 530
				int num)
{
531
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
532 533
	u32 dif_len = addr - aligned_addr;
	u32 i;
534

James Ketrenos's avatar
James Ketrenos committed
535
	IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
536

537 538 539 540
	if (num <= 0) {
		return;
	}

541
	/* Write the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
542
	if (unlikely(dif_len)) {
543
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
544
		/* Start writing at aligned_addr + dif_len */
545
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
546
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos's avatar
James Ketrenos committed
547 548
		aligned_addr += 4;
	}
549

550
	/* Write all of the middle dwords as dwords, with auto-increment */
551
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
552
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
553
		_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
554

555
	/* Write the last dword (or portion) byte by byte */
556
	if (unlikely(num)) {
557
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
558
		for (i = 0; num > 0; i++, num--, buf++)
559
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
560
	}
James Ketrenos's avatar
James Ketrenos committed
561 562
}

563 564
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for 1st 4K of SRAM/regs space */
565
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos's avatar
James Ketrenos committed
566 567 568 569 570
			     int num)
{
	memcpy_toio((priv->hw_base + addr), buf, num);
}

571
/* Set bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
572 573 574 575 576
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
	ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}

577
/* Clear bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
578 579 580 581 582
static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
	ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
}

Zhu Yi's avatar
Zhu Yi committed
583
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
584 585 586 587
{
	if (priv->status & STATUS_INT_ENABLED)
		return;
	priv->status |= STATUS_INT_ENABLED;
588
	ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
589 590
}

Zhu Yi's avatar
Zhu Yi committed
591
static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
592 593 594 595
{
	if (!(priv->status & STATUS_INT_ENABLED))
		return;
	priv->status &= ~STATUS_INT_ENABLED;
596
	ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
597 598
}

Zhu Yi's avatar
Zhu Yi committed
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
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);
}

James Ketrenos's avatar
James Ketrenos committed
617 618 619
static char *ipw_error_desc(u32 val)
{
	switch (val) {
620
	case IPW_FW_ERROR_OK:
James Ketrenos's avatar
James Ketrenos committed
621
		return "ERROR_OK";
622
	case IPW_FW_ERROR_FAIL:
James Ketrenos's avatar
James Ketrenos committed
623
		return "ERROR_FAIL";
624
	case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos's avatar
James Ketrenos committed
625
		return "MEMORY_UNDERFLOW";
626
	case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos's avatar
James Ketrenos committed
627
		return "MEMORY_OVERFLOW";
628
	case IPW_FW_ERROR_BAD_PARAM:
629
		return "BAD_PARAM";
630
	case IPW_FW_ERROR_BAD_CHECKSUM:
631
		return "BAD_CHECKSUM";
632
	case IPW_FW_ERROR_NMI_INTERRUPT:
633
		return "NMI_INTERRUPT";
634
	case IPW_FW_ERROR_BAD_DATABASE:
635
		return "BAD_DATABASE";
636
	case IPW_FW_ERROR_ALLOC_FAIL:
637
		return "ALLOC_FAIL";
638
	case IPW_FW_ERROR_DMA_UNDERRUN:
639
		return "DMA_UNDERRUN";
640
	case IPW_FW_ERROR_DMA_STATUS:
641 642 643 644 645
		return "DMA_STATUS";
	case IPW_FW_ERROR_DINO_ERROR:
		return "DINO_ERROR";
	case IPW_FW_ERROR_EEPROM_ERROR:
		return "EEPROM_ERROR";
646
	case IPW_FW_ERROR_SYSASSERT:
647
		return "SYSASSERT";
648
	case IPW_FW_ERROR_FATAL_ERROR:
649
		return "FATAL_ERROR";
650
	default:
651
		return "UNKNOWN_ERROR";
James Ketrenos's avatar
James Ketrenos committed
652 653 654
	}
}

655 656
static void ipw_dump_error_log(struct ipw_priv *priv,
			       struct ipw_fw_error *error)
James Ketrenos's avatar
James Ketrenos committed
657
{
658
	u32 i;
659

660 661 662 663
	if (!error) {
		IPW_ERROR("Error allocating and capturing error log.  "
			  "Nothing to dump.\n");
		return;
James Ketrenos's avatar
James Ketrenos committed
664 665
	}

666 667 668
	IPW_ERROR("Start IPW Error Log Dump:\n");
	IPW_ERROR("Status: 0x%08X, Config: %08X\n",
		  error->status, error->config);
James Ketrenos's avatar
James Ketrenos committed
669

670
	for (i = 0; i < error->elem_len; i++)
671
		IPW_ERROR("%s %i 0x%08x  0x%08x  0x%08x  0x%08x  0x%08x\n",
672 673 674 675 676 677 678 679 680
			  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,
681
			  error->log[i].data, error->log[i].event);
James Ketrenos's avatar
James Ketrenos committed
682 683
}

684
static inline int ipw_is_init(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
685
{
686
	return (priv->status & STATUS_INIT) ? 1 : 0;
James Ketrenos's avatar
James Ketrenos committed
687 688
}

689
static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos's avatar
James Ketrenos committed
690 691 692 693 694 695 696 697 698
{
	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;
	}
699

James Ketrenos's avatar
James Ketrenos committed
700 701 702 703 704 705 706 707 708 709 710
	/* 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
		 *
711
		 * This is a very simple table with the data directly
James Ketrenos's avatar
James Ketrenos committed
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
		 * 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, "
728
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
729 730 731 732
			return -EINVAL;
		}

		IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
733
			      ord, priv->table0_addr + (ord << 2));
James Ketrenos's avatar
James Ketrenos committed
734 735 736

		*len = sizeof(u32);
		ord <<= 2;
737
		*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos's avatar
James Ketrenos committed
738 739 740 741 742
		break;

	case IPW_ORD_TABLE_1_MASK:
		/*
		 * TABLE 1: Indirect access to a table of 32 bit values
743 744
		 *
		 * This is a fairly large table of u32 values each
James Ketrenos's avatar
James Ketrenos committed
745 746 747 748 749 750
		 * representing starting addr for the data (which is
		 * also a u32)
		 */

		/* remove the table id from the ordinal */
		ord &= IPW_ORD_TABLE_VALUE_MASK;
751

James Ketrenos's avatar
James Ketrenos committed
752 753 754 755 756 757 758 759 760
		/* 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, "
761
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
762 763 764
			return -EINVAL;
		}

765 766
		*((u32 *) val) =
		    ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos's avatar
James Ketrenos committed
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
		*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));
791 792

		/* get the second DW of statistics ;
James Ketrenos's avatar
James Ketrenos committed
793
		 * two 16-bit words - first is length, second is count */
794 795 796 797
		field_info =
		    ipw_read_reg32(priv,
				   priv->table2_addr + (ord << 3) +
				   sizeof(u32));
798

James Ketrenos's avatar
James Ketrenos committed
799
		/* get each entry length */
800
		field_len = *((u16 *) & field_info);
801

James Ketrenos's avatar
James Ketrenos committed
802
		/* get number of entries */
803
		field_count = *(((u16 *) & field_info) + 1);
804

805
		/* abort if not enough memory */
James Ketrenos's avatar
James Ketrenos committed
806 807 808 809 810
		total_len = field_len * field_count;
		if (total_len > *len) {
			*len = total_len;
			return -EINVAL;
		}
811

James Ketrenos's avatar
James Ketrenos committed
812 813 814 815 816
		*len = total_len;
		if (!total_len)
			return 0;

		IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
817
			      "field_info = 0x%08x\n",
James Ketrenos's avatar
James Ketrenos committed
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
			      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;
834
	priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos's avatar
James Ketrenos committed
835 836 837 838 839 840 841 842 843 844 845 846

	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);
847
	priv->table2_len &= 0x0000ffff;	/* use first two bytes */
James Ketrenos's avatar
James Ketrenos committed
848 849 850 851 852 853

	IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
		      priv->table2_addr, priv->table2_len);

}

854
static u32 ipw_register_toggle(u32 reg)
855
{
856 857 858 859 860 861 862
	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;
863 864 865 866 867 868 869 870 871 872 873 874
	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
 *
 */
875 876 877
#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)
878

879
static void ipw_led_link_on(struct ipw_priv *priv)
880 881 882 883 884 885 886 887 888 889 890 891 892 893
{
	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");
894
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
895 896 897 898 899
		led |= priv->led_association_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
900
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
901 902 903 904 905

		priv->status |= STATUS_LED_LINK_ON;

		/* If we aren't associated, schedule turning the LED off */
		if (!(priv->status & STATUS_ASSOCIATED))
906 907
			schedule_delayed_work(&priv->led_link_off,
					      LD_TIME_LINK_ON);
908 909 910 911 912
	}

	spin_unlock_irqrestore(&priv->lock, flags);
}

David Howells's avatar
David Howells committed
913
static void ipw_bg_led_link_on(struct work_struct *work)
914
{
David Howells's avatar
David Howells committed
915 916
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_on.work);
917
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
918
	ipw_led_link_on(priv);
919
	mutex_unlock(&priv->mutex);
920 921
}

922
static void ipw_led_link_off(struct ipw_priv *priv)
923 924 925 926 927 928 929 930 931 932 933 934
{
	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) {
935
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
936 937 938 939
		led &= priv->led_association_off;
		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
940
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
941 942 943 944 945 946 947 948 949

		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))
950 951
			schedule_delayed_work(&priv->led_link_on,
					      LD_TIME_LINK_OFF);
952 953 954 955 956 957

	}

	spin_unlock_irqrestore(&priv->lock, flags);
}

David Howells's avatar
David Howells committed
958
static void ipw_bg_led_link_off(struct work_struct *work)
959
{
David Howells's avatar
David Howells committed
960 961
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_off.work);
962
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
963
	ipw_led_link_off(priv);
964
	mutex_unlock(&priv->mutex);
965 966
}

967
static void __ipw_led_activity_on(struct ipw_priv *priv)
968 969 970 971 972 973
{
	u32 led;

	if (priv->config & CFG_NO_LED)
		return;

974
	if (priv->status & STATUS_RF_KILL_MASK)
975 976 977
		return;

	if (!(priv->status & STATUS_LED_ACT_ON)) {
978
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
979 980 981 982 983
		led |= priv->led_activity_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
984
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
985 986 987 988 989

		IPW_DEBUG_LED("Activity LED On\n");

		priv->status |= STATUS_LED_ACT_ON;

990
		cancel_delayed_work(&priv->led_act_off);
991
		schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
992 993 994
	} else {
		/* Reschedule LED off for full time period */
		cancel_delayed_work(&priv->led_act_off);
995
		schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
996
	}
997
}
998

999
#if 0
1000 1001 1002 1003 1004
void ipw_led_activity_on(struct ipw_priv *priv)
{
	unsigned long flags;
	spin_lock_irqsave(&priv->lock, flags);
	__ipw_led_activity_on(priv);
1005 1006
	spin_unlock_irqrestore(&priv->lock, flags);
}
1007
#endif  /*  0  */
1008

1009
static void ipw_led_activity_off(struct ipw_priv *priv)
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
{
	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) {
1020
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
1021 1022 1023 1024 1025
		led &= priv->led_activity_off;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
1026
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
1027 1028 1029 1030 1031 1032 1033 1034 1035

		IPW_DEBUG_LED("Activity LED Off\n");

		priv->status &= ~STATUS_LED_ACT_ON;
	}

	spin_unlock_irqrestore(&priv->lock, flags);
}

David Howells's avatar
David Howells committed
1036
static void ipw_bg_led_activity_off(struct work_struct *work)
1037
{
David Howells's avatar
David Howells committed
1038 1039
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_act_off.work);
1040
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
1041
	ipw_led_activity_off(priv);
1042
	mutex_unlock(&priv->mutex);
1043 1044
}

1045
static void ipw_led_band_on(struct ipw_priv *priv)
1046 1047 1048 1049 1050
{
	unsigned long flags;
	u32 led;

	/* Only nic type 1 supports mode LEDs */
1051 1052
	if (priv->config & CFG_NO_LED ||
	    priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
1053 1054 1055 1056
		return;

	spin_lock_irqsave(&priv->lock, flags);

1057
	led = ipw_read_reg32(priv, IPW_EVENT_REG);
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
	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);
1075
	ipw_write_reg32(priv, IPW_EVENT_REG, led);
1076 1077 1078 1079

	spin_unlock_irqrestore(&priv->lock, flags);
}

1080
static void ipw_led_band_off(struct ipw_priv *priv)
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
{
	unsigned long flags;
	u32 led;

	/* Only nic type 1 supports mode LEDs */
	if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
		return;

	spin_lock_irqsave(&priv->lock, flags);

1091
	led = ipw_read_reg32(priv, IPW_EVENT_REG);
1092 1093 1094 1095 1096 1097
	led &= priv->led_ofdm_off;
	led &= priv->led_association_off;

	led = ipw_register_toggle(led);

	IPW_DEBUG_LED("Reg: 0x%08X\n", led);
1098
	ipw_write_reg32(priv, IPW_EVENT_REG, led);
1099 1100 1101 1102

	spin_unlock_irqrestore(&priv->lock, flags);
}

1103
static void ipw_led_radio_on(struct ipw_priv *priv)
1104 1105 1106 1107
{
	ipw_led_link_on(priv);
}

1108
static void ipw_led_radio_off(struct ipw_priv *priv)
1109 1110 1111 1112 1113
{
	ipw_led_activity_off(priv);
	ipw_led_link_off(priv);
}

1114
static void ipw_led_link_up(struct ipw_priv *priv)
1115 1116 1117 1118 1119
{
	/* Set the Link Led on for all nic types */
	ipw_led_link_on(priv);
}

1120
static void ipw_led_link_down(struct ipw_priv *priv)
1121 1122 1123 1124 1125 1126 1127 1128
{
	ipw_led_activity_off(priv);
	ipw_led_link_off(priv);

	if (priv->status & STATUS_RF_KILL_MASK)
		ipw_led_radio_off(priv);
}

1129
static void