We are currently experiencing downtime impacting viewing & cloning the Mesa repo, and some GitLab pages returning 503. Please see #freedesktop on IRC for more updates.

ipw2200.c 324 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 28 29 30 31 32 33 34
  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"

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

#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

60
#ifdef CONFIG_IPW2200_RADIOTAP
61 62 63 64 65 66 67 68 69 70 71
#define VR "r"
#else
#define VR
#endif

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

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

77 78
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)

James Ketrenos's avatar
James Ketrenos committed
79 80 81 82 83
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");

84
static int cmdlog = 0;
James Ketrenos's avatar
James Ketrenos committed
85 86 87 88 89
static int debug = 0;
static int channel = 0;
static int mode = 0;

static u32 ipw_debug_level;
90
static int associate;
James Ketrenos's avatar
James Ketrenos committed
91
static int auto_create = 1;
92
static int led = 0;
James Ketrenos's avatar
James Ketrenos committed
93
static int disable = 0;
94
static int bt_coexist = 0;
95
static int hwcrypto = 0;
96
static int roaming = 1;
James Ketrenos's avatar
James Ketrenos committed
97 98 99
static const char ipw_modes[] = {
	'a', 'b', 'g', '?'
};
100
static int antenna = CFG_SYS_ANTENNA_BOTH;
James Ketrenos's avatar
James Ketrenos committed
101

102 103 104 105 106
#ifdef CONFIG_IPW2200_PROMISCUOUS
static int rtap_iface = 0;     /* def: 0 -- do not create rtap interface */
#endif


107
#ifdef CONFIG_IPW2200_QOS
108 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
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);
171
#endif				/* CONFIG_IPW2200_QOS */
172

173
static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
174
static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos's avatar
James Ketrenos committed
175
static void ipw_rx(struct ipw_priv *priv);
176
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos's avatar
James Ketrenos committed
177 178 179 180 181 182 183 184 185 186 187 188
				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
189
static void ipw_bg_up(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
190
static void ipw_down(struct ipw_priv *);
David Howells's avatar
David Howells committed
191
static void ipw_bg_down(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
192
static int ipw_config(struct ipw_priv *);
193 194
static int init_supported_rates(struct ipw_priv *priv,
				struct ipw_supported_rates *prates);
195 196
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
197

198 199
static int snprint_line(char *buf, size_t count,
			const u8 * data, u32 len, u32 ofs)
James Ketrenos's avatar
James Ketrenos committed
200 201 202
{
	int out, i, j, l;
	char c;
203

James Ketrenos's avatar
James Ketrenos committed
204 205 206 207
	out = snprintf(buf, count, "%08X", ofs);

	for (l = 0, i = 0; i < 2; i++) {
		out += snprintf(buf + out, count - out, " ");
208 209
		for (j = 0; j < 8 && l < len; j++, l++)
			out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos's avatar
James Ketrenos committed
210 211 212 213
					data[(i * 8 + j)]);
		for (; j < 8; j++)
			out += snprintf(buf + out, count - out, "   ");
	}
214

James Ketrenos's avatar
James Ketrenos committed
215 216 217 218 219 220 221
	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 = '.';
222

James Ketrenos's avatar
James Ketrenos committed
223 224 225 226 227 228
			out += snprintf(buf + out, count - out, "%c", c);
		}

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

230
	return out;
James Ketrenos's avatar
James Ketrenos committed
231 232
}

233
static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos's avatar
James Ketrenos committed
234 235 236 237 238 239 240
{
	char line[81];
	u32 ofs = 0;
	if (!(ipw_debug_level & level))
		return;

	while (len) {
241 242 243
		snprint_line(line, sizeof(line), &data[ofs],
			     min(len, 16U), ofs);
		printk(KERN_DEBUG "%s\n", line);
James Ketrenos's avatar
James Ketrenos committed
244 245 246 247 248
		ofs += 16;
		len -= min(len, 16U);
	}
}

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
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;
}

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

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

276
/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
277 278 279
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)
{
280 281
	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
282 283 284
	_ipw_write_reg8(a, b, c);
}

285
/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
286 287 288
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)
{
289 290
	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
291 292 293
	_ipw_write_reg16(a, b, c);
}

294
/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
295 296 297
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)
{
298 299
	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
300 301 302
	_ipw_write_reg32(a, b, c);
}

303
/* 8-bit direct write (low 4K) */
James Ketrenos's avatar
James Ketrenos committed
304
#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
305 306

/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
307
#define ipw_write8(ipw, ofs, val) do { \
James Ketrenos's avatar
James Ketrenos committed
308
 IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
309 310
 _ipw_write8(ipw, ofs, val); \
 } while (0)
James Ketrenos's avatar
James Ketrenos committed
311

312
/* 16-bit direct write (low 4K) */
James Ketrenos's avatar
James Ketrenos committed
313
#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
314 315

/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
316 317 318 319
#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)

320
/* 32-bit direct write (low 4K) */
James Ketrenos's avatar
James Ketrenos committed
321
#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
322 323

/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
324 325 326 327
#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)

328
/* 8-bit direct read (low 4K) */
James Ketrenos's avatar
James Ketrenos committed
329
#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
330 331

/* 8-bit direct read (low 4K), with debug wrapper */
332 333 334
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));
James Ketrenos's avatar
James Ketrenos committed
335 336
	return _ipw_read8(ipw, ofs);
}
337

338
/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
339 340
#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)

341
/* 16-bit direct read (low 4K) */
James Ketrenos's avatar
James Ketrenos committed
342
#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
343 344

/* 16-bit direct read (low 4K), with debug wrapper */
345 346 347
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));
James Ketrenos's avatar
James Ketrenos committed
348 349
	return _ipw_read16(ipw, ofs);
}
350

351
/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
352 353
#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)

354
/* 32-bit direct read (low 4K) */
James Ketrenos's avatar
James Ketrenos committed
355
#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
356 357

/* 32-bit direct read (low 4K), with debug wrapper */
358 359 360
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));
James Ketrenos's avatar
James Ketrenos committed
361 362
	return _ipw_read32(ipw, ofs);
}
363

364
/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
365 366
#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)

367
/* multi-byte read (above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
368
static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
369 370 371 372 373 374 375 376
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);
}

377
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
378
#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
James Ketrenos's avatar
James Ketrenos committed
379

380
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
381 382
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
				int num);
James Ketrenos's avatar
James Ketrenos committed
383 384
#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); \
385
	_ipw_write_indirect(a, b, c, d)
James Ketrenos's avatar
James Ketrenos committed
386

387
/* 32-bit indirect write (above 4K) */
388
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos's avatar
James Ketrenos committed
389
{
390
	IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
391 392
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	_ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos's avatar
James Ketrenos committed
393 394
}

395
/* 8-bit indirect write (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
396 397
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
398
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
399 400
	u32 dif_len = reg - aligned_addr;

James Ketrenos's avatar
James Ketrenos committed
401
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
402 403
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
404 405
}

406
/* 16-bit indirect write (above 4K) */
407
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos's avatar
James Ketrenos committed
408
{
409
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
410 411
	u32 dif_len = (reg - aligned_addr) & (~0x1ul);

James Ketrenos's avatar
James Ketrenos committed
412
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
413 414
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
415 416
}

417
/* 8-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
418 419 420
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
	u32 word;
421
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
James Ketrenos's avatar
James Ketrenos committed
422
	IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
423
	word = _ipw_read32(priv, IPW_INDIRECT_DATA);
424
	return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos's avatar
James Ketrenos committed
425 426
}

427
/* 32-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
428 429 430 431 432 433
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
{
	u32 value;

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

434 435
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	value = _ipw_read32(priv, IPW_INDIRECT_DATA);
James Ketrenos's avatar
James Ketrenos committed
436 437 438 439
	IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
	return value;
}

440 441
/* 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
442 443 444
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
			       int num)
{
445
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
446 447
	u32 dif_len = addr - aligned_addr;
	u32 i;
448

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

451 452 453 454
	if (num <= 0) {
		return;
	}

455
	/* Read the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
456
	if (unlikely(dif_len)) {
457
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenos's avatar
James Ketrenos committed
458
		/* Start reading at aligned_addr + dif_len */
459
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
460
			*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos's avatar
James Ketrenos committed
461 462 463
		aligned_addr += 4;
	}

464
	/* Read all of the middle dwords as dwords, with auto-increment */
465
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
466
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
467
		*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
468

469
	/* Read the last dword (or portion) byte by byte */
470
	if (unlikely(num)) {
471
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
472
		for (i = 0; num > 0; i++, num--)
473
			*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
474
	}
James Ketrenos's avatar
James Ketrenos committed
475 476
}

477 478
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for area above 1st 4K of SRAM/reg space */
479
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos's avatar
James Ketrenos committed
480 481
				int num)
{
482
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
483 484
	u32 dif_len = addr - aligned_addr;
	u32 i;
485

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

488 489 490 491
	if (num <= 0) {
		return;
	}

492
	/* Write the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
493
	if (unlikely(dif_len)) {
494
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
495
		/* Start writing at aligned_addr + dif_len */
496
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
497
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos's avatar
James Ketrenos committed
498 499
		aligned_addr += 4;
	}
500

501
	/* Write all of the middle dwords as dwords, with auto-increment */
502
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
503
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
504
		_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
505

506
	/* Write the last dword (or portion) byte by byte */
507
	if (unlikely(num)) {
508
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
509
		for (i = 0; num > 0; i++, num--, buf++)
510
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
511
	}
James Ketrenos's avatar
James Ketrenos committed
512 513
}

514 515
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for 1st 4K of SRAM/regs space */
516
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos's avatar
James Ketrenos committed
517 518 519 520 521
			     int num)
{
	memcpy_toio((priv->hw_base + addr), buf, num);
}

522
/* Set bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
523 524 525 526 527
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
	ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}

528
/* Clear bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
529 530 531 532 533
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
534
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
535 536 537 538
{
	if (priv->status & STATUS_INT_ENABLED)
		return;
	priv->status |= STATUS_INT_ENABLED;
539
	ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
540 541
}

Zhu Yi's avatar
Zhu Yi committed
542
static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
543 544 545 546
{
	if (!(priv->status & STATUS_INT_ENABLED))
		return;
	priv->status &= ~STATUS_INT_ENABLED;
547
	ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
548 549
}

Zhu Yi's avatar
Zhu Yi committed
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
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
568 569 570
static char *ipw_error_desc(u32 val)
{
	switch (val) {
571
	case IPW_FW_ERROR_OK:
James Ketrenos's avatar
James Ketrenos committed
572
		return "ERROR_OK";
573
	case IPW_FW_ERROR_FAIL:
James Ketrenos's avatar
James Ketrenos committed
574
		return "ERROR_FAIL";
575
	case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos's avatar
James Ketrenos committed
576
		return "MEMORY_UNDERFLOW";
577
	case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos's avatar
James Ketrenos committed
578
		return "MEMORY_OVERFLOW";
579
	case IPW_FW_ERROR_BAD_PARAM:
580
		return "BAD_PARAM";
581
	case IPW_FW_ERROR_BAD_CHECKSUM:
582
		return "BAD_CHECKSUM";
583
	case IPW_FW_ERROR_NMI_INTERRUPT:
584
		return "NMI_INTERRUPT";
585
	case IPW_FW_ERROR_BAD_DATABASE:
586
		return "BAD_DATABASE";
587
	case IPW_FW_ERROR_ALLOC_FAIL:
588
		return "ALLOC_FAIL";
589
	case IPW_FW_ERROR_DMA_UNDERRUN:
590
		return "DMA_UNDERRUN";
591
	case IPW_FW_ERROR_DMA_STATUS:
592 593 594 595 596
		return "DMA_STATUS";
	case IPW_FW_ERROR_DINO_ERROR:
		return "DINO_ERROR";
	case IPW_FW_ERROR_EEPROM_ERROR:
		return "EEPROM_ERROR";
597
	case IPW_FW_ERROR_SYSASSERT:
598
		return "SYSASSERT";
599
	case IPW_FW_ERROR_FATAL_ERROR:
600
		return "FATAL_ERROR";
601
	default:
602
		return "UNKNOWN_ERROR";
James Ketrenos's avatar
James Ketrenos committed
603 604 605
	}
}

606 607
static void ipw_dump_error_log(struct ipw_priv *priv,
			       struct ipw_fw_error *error)
James Ketrenos's avatar
James Ketrenos committed
608
{
609
	u32 i;
610

611 612 613 614
	if (!error) {
		IPW_ERROR("Error allocating and capturing error log.  "
			  "Nothing to dump.\n");
		return;
James Ketrenos's avatar
James Ketrenos committed
615 616
	}

617 618 619
	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
620

621
	for (i = 0; i < error->elem_len; i++)
622
		IPW_ERROR("%s %i 0x%08x  0x%08x  0x%08x  0x%08x  0x%08x\n",
623 624 625 626 627 628 629 630 631
			  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,
632
			  error->log[i].data, error->log[i].event);
James Ketrenos's avatar
James Ketrenos committed
633 634
}

635
static inline int ipw_is_init(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
636
{
637
	return (priv->status & STATUS_INIT) ? 1 : 0;
James Ketrenos's avatar
James Ketrenos committed
638 639
}

640
static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos's avatar
James Ketrenos committed
641 642 643 644 645 646 647 648 649
{
	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;
	}
650

James Ketrenos's avatar
James Ketrenos committed
651 652 653 654 655 656 657 658 659 660 661
	/* 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
		 *
662
		 * This is a very simple table with the data directly
James Ketrenos's avatar
James Ketrenos committed
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
		 * 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, "
679
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
680 681 682 683
			return -EINVAL;
		}

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

		*len = sizeof(u32);
		ord <<= 2;
688
		*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos's avatar
James Ketrenos committed
689 690 691 692 693
		break;

	case IPW_ORD_TABLE_1_MASK:
		/*
		 * TABLE 1: Indirect access to a table of 32 bit values
694 695
		 *
		 * This is a fairly large table of u32 values each
James Ketrenos's avatar
James Ketrenos committed
696 697 698 699 700 701
		 * representing starting addr for the data (which is
		 * also a u32)
		 */

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

James Ketrenos's avatar
James Ketrenos committed
703 704 705 706 707 708 709 710 711
		/* 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, "
712
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
713 714 715
			return -EINVAL;
		}

716 717
		*((u32 *) val) =
		    ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos's avatar
James Ketrenos committed
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
		*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));
742 743

		/* get the second DW of statistics ;
James Ketrenos's avatar
James Ketrenos committed
744
		 * two 16-bit words - first is length, second is count */
745 746 747 748
		field_info =
		    ipw_read_reg32(priv,
				   priv->table2_addr + (ord << 3) +
				   sizeof(u32));
749

James Ketrenos's avatar
James Ketrenos committed
750
		/* get each entry length */
751
		field_len = *((u16 *) & field_info);
752

James Ketrenos's avatar
James Ketrenos committed
753
		/* get number of entries */
754
		field_count = *(((u16 *) & field_info) + 1);
755

James Ketrenos's avatar
James Ketrenos committed
756 757 758 759 760 761
		/* abort if not enought memory */
		total_len = field_len * field_count;
		if (total_len > *len) {
			*len = total_len;
			return -EINVAL;
		}
762

James Ketrenos's avatar
James Ketrenos committed
763 764 765 766 767
		*len = total_len;
		if (!total_len)
			return 0;

		IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
768
			      "field_info = 0x%08x\n",
James Ketrenos's avatar
James Ketrenos committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
			      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;
785
	priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos's avatar
James Ketrenos committed
786 787 788 789 790 791 792 793 794 795 796 797

	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);
798
	priv->table2_len &= 0x0000ffff;	/* use first two bytes */
James Ketrenos's avatar
James Ketrenos committed
799 800 801 802 803 804

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

}

805
static u32 ipw_register_toggle(u32 reg)
806
{
807 808 809 810 811 812 813
	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;
814 815 816 817 818 819 820 821 822 823 824 825
	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
 *
 */
826 827 828
#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)
829

830
static void ipw_led_link_on(struct ipw_priv *priv)
831 832 833 834 835 836 837 838 839 840 841 842 843 844
{
	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");
845
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
846 847 848 849 850
		led |= priv->led_association_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
851
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
852 853 854 855 856 857 858 859 860 861 862 863 864

		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);
}

David Howells's avatar
David Howells committed
865
static void ipw_bg_led_link_on(struct work_struct *work)
866
{
David Howells's avatar
David Howells committed
867 868
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_on.work);
869
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
870
	ipw_led_link_on(priv);
871
	mutex_unlock(&priv->mutex);
872 873
}

874
static void ipw_led_link_off(struct ipw_priv *priv)
875 876 877 878 879 880 881 882 883 884 885 886
{
	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) {
887
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
888 889 890 891
		led &= priv->led_association_off;
		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
892
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909

		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);
}

David Howells's avatar
David Howells committed
910
static void ipw_bg_led_link_off(struct work_struct *work)
911
{
David Howells's avatar
David Howells committed
912 913
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_off.work);
914
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
915
	ipw_led_link_off(priv);
916
	mutex_unlock(&priv->mutex);
917 918
}

919
static void __ipw_led_activity_on(struct ipw_priv *priv)
920 921 922 923 924 925
{
	u32 led;

	if (priv->config & CFG_NO_LED)
		return;

926
	if (priv->status & STATUS_RF_KILL_MASK)
927 928 929
		return;

	if (!(priv->status & STATUS_LED_ACT_ON)) {
930
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
931 932 933 934 935
		led |= priv->led_activity_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
936
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
937 938 939 940 941

		IPW_DEBUG_LED("Activity LED On\n");

		priv->status |= STATUS_LED_ACT_ON;

942
		cancel_delayed_work(&priv->led_act_off);
943 944 945 946 947 948 949 950
		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);
	}
951
}
952

953
#if 0
954 955 956 957 958
void ipw_led_activity_on(struct ipw_priv *priv)
{
	unsigned long flags;
	spin_lock_irqsave(&priv->lock, flags);
	__ipw_led_activity_on(priv);
959 960
	spin_unlock_irqrestore(&priv->lock, flags);
}
961
#endif  /*  0  */
962

963
static void ipw_led_activity_off(struct ipw_priv *priv)
964 965 966 967 968 969 970 971 972 973
{
	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) {
974
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
975 976 977 978 979
		led &= priv->led_activity_off;

		led = ipw_register_toggle(led);

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

		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
990
static void ipw_bg_led_activity_off(struct work_struct *work)
991
{
David Howells's avatar
David Howells committed
992 993
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_act_off.work);
994
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
995
	ipw_led_activity_off(priv);
996
	mutex_unlock(&priv->mutex);
997 998
}

999
static void ipw_led_band_on(struct ipw_priv *priv)
1000 1001 1002 1003 1004
{
	unsigned long flags;
	u32 led;

	/* Only nic type 1 supports mode LEDs */
1005 1006
	if (priv->config & CFG_NO_LED ||
	    priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
1007 1008 1009 1010
		return;

	spin_lock_irqsave(&priv->lock, flags);

James Ketrenos's avatar