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 325 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>
James Ketrenos's avatar
James Ketrenos committed
34 35
#include "ipw2200.h"

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

#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

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

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

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

78 79
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)

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

85
static int cmdlog = 0;
James Ketrenos's avatar
James Ketrenos committed
86
static int debug = 0;
Reinette Chatre's avatar
Reinette Chatre committed
87 88
static int default_channel = 0;
static int network_mode = 0;
James Ketrenos's avatar
James Ketrenos committed
89 90

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

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

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
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
126

127
#ifdef CONFIG_IPW2200_QOS
128 129 130 131 132 133
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;

134
static struct libipw_qos_parameters def_qos_parameters_OFDM = {
135 136 137 138 139 140 141 142 143 144
	{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}
};

145
static struct libipw_qos_parameters def_qos_parameters_CCK = {
146 147 148 149 150 151 152 153 154 155
	{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}
};

156
static struct libipw_qos_parameters def_parameters_OFDM = {
157 158 159 160 161 162 163 164 165 166
	{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}
};

167
static struct libipw_qos_parameters def_parameters_CCK = {
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	{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);

187
static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters
188
				       *qos_param);
189
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element
190
				     *qos_param);
191
#endif				/* CONFIG_IPW2200_QOS */
192

193
static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
194
static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos's avatar
James Ketrenos committed
195
static void ipw_rx(struct ipw_priv *priv);
196
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos's avatar
James Ketrenos committed
197 198 199 200 201 202 203 204 205 206 207 208
				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
209
static void ipw_bg_up(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
210
static void ipw_down(struct ipw_priv *);
David Howells's avatar
David Howells committed
211
static void ipw_bg_down(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
212
static int ipw_config(struct ipw_priv *);
213 214
static int init_supported_rates(struct ipw_priv *priv,
				struct ipw_supported_rates *prates);
215 216
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
217

218 219
static int snprint_line(char *buf, size_t count,
			const u8 * data, u32 len, u32 ofs)
James Ketrenos's avatar
James Ketrenos committed
220 221 222
{
	int out, i, j, l;
	char c;
223

James Ketrenos's avatar
James Ketrenos committed
224 225 226 227
	out = snprintf(buf, count, "%08X", ofs);

	for (l = 0, i = 0; i < 2; i++) {
		out += snprintf(buf + out, count - out, " ");
228 229
		for (j = 0; j < 8 && l < len; j++, l++)
			out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos's avatar
James Ketrenos committed
230 231 232 233
					data[(i * 8 + j)]);
		for (; j < 8; j++)
			out += snprintf(buf + out, count - out, "   ");
	}
234

James Ketrenos's avatar
James Ketrenos committed
235 236 237 238 239 240 241
	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 = '.';
242

James Ketrenos's avatar
James Ketrenos committed
243 244 245 246 247 248
			out += snprintf(buf + out, count - out, "%c", c);
		}

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

250
	return out;
James Ketrenos's avatar
James Ketrenos committed
251 252
}

253
static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos's avatar
James Ketrenos committed
254 255 256 257 258 259 260
{
	char line[81];
	u32 ofs = 0;
	if (!(ipw_debug_level & level))
		return;

	while (len) {
261 262 263
		snprint_line(line, sizeof(line), &data[ofs],
			     min(len, 16U), ofs);
		printk(KERN_DEBUG "%s\n", line);
James Ketrenos's avatar
James Ketrenos committed
264 265 266 267 268
		ofs += 16;
		len -= min(len, 16U);
	}
}

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
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;
}

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

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

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

305
/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
306 307 308
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)
{
309 310
	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
311 312 313
	_ipw_write_reg16(a, b, c);
}

314
/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
315 316 317
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)
{
318 319
	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
320 321 322
	_ipw_write_reg32(a, b, c);
}

323
/* 8-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
324 325 326 327 328
static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs,
		u8 val)
{
	writeb(val, ipw->hw_base + ofs);
}
329 330

/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
331
#define ipw_write8(ipw, ofs, val) do { \
Jiri Slaby's avatar
Jiri Slaby committed
332 333 334 335
	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
336

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

/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
345 346 347 348 349
#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
350

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

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

365
/* 8-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
366
static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs)
367
{
Jiri Slaby's avatar
Jiri Slaby committed
368
	return readb(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
369
}
370

371
/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
372 373 374 375 376
#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
377

378
/* 16-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
379
static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs)
380
{
Jiri Slaby's avatar
Jiri Slaby committed
381
	return readw(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
382
}
383

384
/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
385 386 387 388 389
#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
390

391
/* 32-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
392
static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs)
393
{
Jiri Slaby's avatar
Jiri Slaby committed
394
	return readl(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
395
}
396

397
/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
398 399 400 401 402
#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
403 404

static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
405
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
406 407 408 409 410
#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
411

412
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
413 414
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
				int num);
Jiri Slaby's avatar
Jiri Slaby committed
415 416 417 418 419
#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
420

421
/* 32-bit indirect write (above 4K) */
422
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos's avatar
James Ketrenos committed
423
{
424
	IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
425 426
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	_ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos's avatar
James Ketrenos committed
427 428
}

429
/* 8-bit indirect write (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
430 431
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
432
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
433 434
	u32 dif_len = reg - aligned_addr;

James Ketrenos's avatar
James Ketrenos committed
435
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
436 437
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
438 439
}

440
/* 16-bit indirect write (above 4K) */
441
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos's avatar
James Ketrenos committed
442
{
443
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
444 445
	u32 dif_len = (reg - aligned_addr) & (~0x1ul);

James Ketrenos's avatar
James Ketrenos committed
446
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
447 448
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
449 450
}

451
/* 8-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
452 453 454
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
	u32 word;
455
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
James Ketrenos's avatar
James Ketrenos committed
456
	IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
457
	word = _ipw_read32(priv, IPW_INDIRECT_DATA);
458
	return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos's avatar
James Ketrenos committed
459 460
}

461
/* 32-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
462 463 464 465 466 467
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
{
	u32 value;

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

468 469
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	value = _ipw_read32(priv, IPW_INDIRECT_DATA);
James Ketrenos's avatar
James Ketrenos committed
470 471 472 473
	IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
	return value;
}

474 475
/* 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
476 477 478
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
			       int num)
{
479
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
480 481
	u32 dif_len = addr - aligned_addr;
	u32 i;
482

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

485 486 487 488
	if (num <= 0) {
		return;
	}

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

498
	/* Read all of the middle dwords as dwords, with auto-increment */
499
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
500
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
501
		*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
502

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

511 512
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for area above 1st 4K of SRAM/reg space */
513
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos's avatar
James Ketrenos committed
514 515
				int num)
{
516
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
517 518
	u32 dif_len = addr - aligned_addr;
	u32 i;
519

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

522 523 524 525
	if (num <= 0) {
		return;
	}

526
	/* Write the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
527
	if (unlikely(dif_len)) {
528
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
529
		/* Start writing at aligned_addr + dif_len */
530
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
531
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos's avatar
James Ketrenos committed
532 533
		aligned_addr += 4;
	}
534

535
	/* Write all of the middle dwords as dwords, with auto-increment */
536
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
537
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
538
		_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
539

540
	/* Write the last dword (or portion) byte by byte */
541
	if (unlikely(num)) {
542
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
543
		for (i = 0; num > 0; i++, num--, buf++)
544
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
545
	}
James Ketrenos's avatar
James Ketrenos committed
546 547
}

548 549
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for 1st 4K of SRAM/regs space */
550
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos's avatar
James Ketrenos committed
551 552 553 554 555
			     int num)
{
	memcpy_toio((priv->hw_base + addr), buf, num);
}

556
/* Set bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
557 558 559 560 561
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
	ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}

562
/* Clear bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
563 564 565 566 567
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
568
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
569 570 571 572
{
	if (priv->status & STATUS_INT_ENABLED)
		return;
	priv->status |= STATUS_INT_ENABLED;
573
	ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
574 575
}

Zhu Yi's avatar
Zhu Yi committed
576
static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
577 578 579 580
{
	if (!(priv->status & STATUS_INT_ENABLED))
		return;
	priv->status &= ~STATUS_INT_ENABLED;
581
	ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
582 583
}

Zhu Yi's avatar
Zhu Yi committed
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
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
602 603 604
static char *ipw_error_desc(u32 val)
{
	switch (val) {
605
	case IPW_FW_ERROR_OK:
James Ketrenos's avatar
James Ketrenos committed
606
		return "ERROR_OK";
607
	case IPW_FW_ERROR_FAIL:
James Ketrenos's avatar
James Ketrenos committed
608
		return "ERROR_FAIL";
609
	case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos's avatar
James Ketrenos committed
610
		return "MEMORY_UNDERFLOW";
611
	case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos's avatar
James Ketrenos committed
612
		return "MEMORY_OVERFLOW";
613
	case IPW_FW_ERROR_BAD_PARAM:
614
		return "BAD_PARAM";
615
	case IPW_FW_ERROR_BAD_CHECKSUM:
616
		return "BAD_CHECKSUM";
617
	case IPW_FW_ERROR_NMI_INTERRUPT:
618
		return "NMI_INTERRUPT";
619
	case IPW_FW_ERROR_BAD_DATABASE:
620
		return "BAD_DATABASE";
621
	case IPW_FW_ERROR_ALLOC_FAIL:
622
		return "ALLOC_FAIL";
623
	case IPW_FW_ERROR_DMA_UNDERRUN:
624
		return "DMA_UNDERRUN";
625
	case IPW_FW_ERROR_DMA_STATUS:
626 627 628 629 630
		return "DMA_STATUS";
	case IPW_FW_ERROR_DINO_ERROR:
		return "DINO_ERROR";
	case IPW_FW_ERROR_EEPROM_ERROR:
		return "EEPROM_ERROR";
631
	case IPW_FW_ERROR_SYSASSERT:
632
		return "SYSASSERT";
633
	case IPW_FW_ERROR_FATAL_ERROR:
634
		return "FATAL_ERROR";
635
	default:
636
		return "UNKNOWN_ERROR";
James Ketrenos's avatar
James Ketrenos committed
637 638 639
	}
}

640 641
static void ipw_dump_error_log(struct ipw_priv *priv,
			       struct ipw_fw_error *error)
James Ketrenos's avatar
James Ketrenos committed
642
{
643
	u32 i;
644

645 646 647 648
	if (!error) {
		IPW_ERROR("Error allocating and capturing error log.  "
			  "Nothing to dump.\n");
		return;
James Ketrenos's avatar
James Ketrenos committed
649 650
	}

651 652 653
	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
654

655
	for (i = 0; i < error->elem_len; i++)
656
		IPW_ERROR("%s %i 0x%08x  0x%08x  0x%08x  0x%08x  0x%08x\n",
657 658 659 660 661 662 663 664 665
			  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,
666
			  error->log[i].data, error->log[i].event);
James Ketrenos's avatar
James Ketrenos committed
667 668
}

669
static inline int ipw_is_init(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
670
{
671
	return (priv->status & STATUS_INIT) ? 1 : 0;
James Ketrenos's avatar
James Ketrenos committed
672 673
}

674
static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos's avatar
James Ketrenos committed
675 676 677 678 679 680 681 682 683
{
	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;
	}
684

James Ketrenos's avatar
James Ketrenos committed
685 686 687 688 689 690 691 692 693 694 695
	/* 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
		 *
696
		 * This is a very simple table with the data directly
James Ketrenos's avatar
James Ketrenos committed
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
		 * 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, "
713
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
714 715 716 717
			return -EINVAL;
		}

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

		*len = sizeof(u32);
		ord <<= 2;
722
		*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos's avatar
James Ketrenos committed
723 724 725 726 727
		break;

	case IPW_ORD_TABLE_1_MASK:
		/*
		 * TABLE 1: Indirect access to a table of 32 bit values
728 729
		 *
		 * This is a fairly large table of u32 values each
James Ketrenos's avatar
James Ketrenos committed
730 731 732 733 734 735
		 * representing starting addr for the data (which is
		 * also a u32)
		 */

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

James Ketrenos's avatar
James Ketrenos committed
737 738 739 740 741 742 743 744 745
		/* 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, "
746
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
747 748 749
			return -EINVAL;
		}

750 751
		*((u32 *) val) =
		    ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos's avatar
James Ketrenos committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
		*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));
776 777

		/* get the second DW of statistics ;
James Ketrenos's avatar
James Ketrenos committed
778
		 * two 16-bit words - first is length, second is count */
779 780 781 782
		field_info =
		    ipw_read_reg32(priv,
				   priv->table2_addr + (ord << 3) +
				   sizeof(u32));
783

James Ketrenos's avatar
James Ketrenos committed
784
		/* get each entry length */
785
		field_len = *((u16 *) & field_info);
786

James Ketrenos's avatar
James Ketrenos committed
787
		/* get number of entries */
788
		field_count = *(((u16 *) & field_info) + 1);
789

James Ketrenos's avatar
James Ketrenos committed
790 791 792 793 794 795
		/* abort if not enought memory */
		total_len = field_len * field_count;
		if (total_len > *len) {
			*len = total_len;
			return -EINVAL;
		}
796

James Ketrenos's avatar
James Ketrenos committed
797 798 799 800 801
		*len = total_len;
		if (!total_len)
			return 0;

		IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
802
			      "field_info = 0x%08x\n",
James Ketrenos's avatar
James Ketrenos committed
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
			      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;
819
	priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos's avatar
James Ketrenos committed
820 821 822 823 824 825 826 827 828 829 830 831

	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);
832
	priv->table2_len &= 0x0000ffff;	/* use first two bytes */
James Ketrenos's avatar
James Ketrenos committed
833 834 835 836 837 838

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

}

839
static u32 ipw_register_toggle(u32 reg)
840
{
841 842 843 844 845 846 847
	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;
848 849 850 851 852 853 854 855 856 857 858 859
	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
 *
 */
860 861 862
#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)
863

864
static void ipw_led_link_on(struct ipw_priv *priv)
865 866 867 868 869 870 871 872 873 874 875 876 877 878
{
	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");
879
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
880 881 882 883 884
		led |= priv->led_association_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
885
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
886 887 888 889 890 891 892 893 894 895 896 897 898

		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
899
static void ipw_bg_led_link_on(struct work_struct *work)
900
{
David Howells's avatar
David Howells committed
901 902
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_on.work);
903
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
904
	ipw_led_link_on(priv);
905
	mutex_unlock(&priv->mutex);
906 907
}

908
static void ipw_led_link_off(struct ipw_priv *priv)
909 910 911 912 913 914 915 916 917 918 919 920
{
	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) {
921
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
922 923 924 925
		led &= priv->led_association_off;
		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
926
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943

		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
944
static void ipw_bg_led_link_off(struct work_struct *work)
945
{
David Howells's avatar
David Howells committed
946 947
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_off.work);
948
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
949
	ipw_led_link_off(priv);
950
	mutex_unlock(&priv->mutex);
951 952
}

953
static void __ipw_led_activity_on(struct ipw_priv *priv)
954 955 956 957 958 959
{
	u32 led;

	if (priv->config & CFG_NO_LED)
		return;

960
	if (priv->status & STATUS_RF_KILL_MASK)
961 962 963
		return;

	if (!(priv->status & STATUS_LED_ACT_ON)) {
964
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
965 966 967 968 969
		led |= priv->led_activity_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
970
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
971 972 973 974 975

		IPW_DEBUG_LED("Activity LED On\n");

		priv->status |= STATUS_LED_ACT_ON;

976
		cancel_delayed_work(&priv->led_act_off);
977 978 979 980 981 982 983 984
		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);
	}
985
}
986

987
#if 0
988 989 990 991 992
void ipw_led_activity_on(struct ipw_priv *priv)
{
	unsigned long flags;
	spin_lock_irqsave(&priv->lock, flags);
	__ipw_led_activity_on(priv);
993 994
	spin_unlock_irqrestore(&priv->lock, flags);
}
995
#endif  /*  0  */
996

997
static void ipw_led_activity_off(struct ipw_priv *priv)