ipw2200.c 326 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
#ifdef CONFIG_IPW2200_QOS
135 136 137 138 139 140
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;

141
static struct libipw_qos_parameters def_qos_parameters_OFDM = {
142 143 144 145 146 147 148 149 150 151
	{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}
};

152
static struct libipw_qos_parameters def_qos_parameters_CCK = {
153 154 155 156 157 158 159 160 161 162
	{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}
};

163
static struct libipw_qos_parameters def_parameters_OFDM = {
164 165 166 167 168 169 170 171 172 173
	{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}
};

174
static struct libipw_qos_parameters def_parameters_CCK = {
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	{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);

194
static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters
195
				       *qos_param);
196
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element
197
				     *qos_param);
198
#endif				/* CONFIG_IPW2200_QOS */
199

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

225 226
static int snprint_line(char *buf, size_t count,
			const u8 * data, u32 len, u32 ofs)
James Ketrenos's avatar
James Ketrenos committed
227 228 229
{
	int out, i, j, l;
	char c;
230

James Ketrenos's avatar
James Ketrenos committed
231 232 233 234
	out = snprintf(buf, count, "%08X", ofs);

	for (l = 0, i = 0; i < 2; i++) {
		out += snprintf(buf + out, count - out, " ");
235 236
		for (j = 0; j < 8 && l < len; j++, l++)
			out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos's avatar
James Ketrenos committed
237 238 239 240
					data[(i * 8 + j)]);
		for (; j < 8; j++)
			out += snprintf(buf + out, count - out, "   ");
	}
241

James Ketrenos's avatar
James Ketrenos committed
242 243 244 245 246 247 248
	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 = '.';
249

James Ketrenos's avatar
James Ketrenos committed
250 251 252 253 254 255
			out += snprintf(buf + out, count - out, "%c", c);
		}

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

257
	return out;
James Ketrenos's avatar
James Ketrenos committed
258 259
}

260
static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos's avatar
James Ketrenos committed
261 262 263 264 265 266 267
{
	char line[81];
	u32 ofs = 0;
	if (!(ipw_debug_level & level))
		return;

	while (len) {
268 269 270
		snprint_line(line, sizeof(line), &data[ofs],
			     min(len, 16U), ofs);
		printk(KERN_DEBUG "%s\n", line);
James Ketrenos's avatar
James Ketrenos committed
271 272 273 274 275
		ofs += 16;
		len -= min(len, 16U);
	}
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
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;
}

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

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

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

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

321
/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
322 323 324
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)
{
325 326
	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
327 328 329
	_ipw_write_reg32(a, b, c);
}

330
/* 8-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
331 332 333 334 335
static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs,
		u8 val)
{
	writeb(val, ipw->hw_base + ofs);
}
336 337

/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
338
#define ipw_write8(ipw, ofs, val) do { \
Jiri Slaby's avatar
Jiri Slaby committed
339 340 341 342
	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
343

344
/* 16-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
345 346 347 348 349
static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs,
		u16 val)
{
	writew(val, ipw->hw_base + ofs);
}
350 351

/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
352 353 354 355 356
#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
357

358
/* 32-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
359 360 361 362 363
static inline void _ipw_write32(struct ipw_priv *ipw, unsigned long ofs,
		u32 val)
{
	writel(val, ipw->hw_base + ofs);
}
364 365

/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
366 367 368 369 370
#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
371

372
/* 8-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
373
static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs)
374
{
Jiri Slaby's avatar
Jiri Slaby committed
375
	return readb(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
376
}
377

378
/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
379 380 381 382 383
#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
384

385
/* 16-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
386
static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs)
387
{
Jiri Slaby's avatar
Jiri Slaby committed
388
	return readw(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
389
}
390

391
/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
392 393 394 395 396
#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
397

398
/* 32-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
399
static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs)
400
{
Jiri Slaby's avatar
Jiri Slaby committed
401
	return readl(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
402
}
403

404
/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
405 406 407 408 409
#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
410 411

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

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

428
/* 32-bit indirect write (above 4K) */
429
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos's avatar
James Ketrenos committed
430
{
431
	IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
432 433
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	_ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos's avatar
James Ketrenos committed
434 435
}

436
/* 8-bit indirect write (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
437 438
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
439
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
440 441
	u32 dif_len = reg - aligned_addr;

James Ketrenos's avatar
James Ketrenos committed
442
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
443 444
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
445 446
}

447
/* 16-bit indirect write (above 4K) */
448
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos's avatar
James Ketrenos committed
449
{
450
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
451 452
	u32 dif_len = (reg - aligned_addr) & (~0x1ul);

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

458
/* 8-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
459 460 461
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
	u32 word;
462
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
463
	IPW_DEBUG_IO(" reg = 0x%8X :\n", reg);
464
	word = _ipw_read32(priv, IPW_INDIRECT_DATA);
465
	return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos's avatar
James Ketrenos committed
466 467
}

468
/* 32-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
469 470 471 472 473 474
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
{
	u32 value;

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

475 476
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	value = _ipw_read32(priv, IPW_INDIRECT_DATA);
477
	IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x\n", reg, value);
James Ketrenos's avatar
James Ketrenos committed
478 479 480
	return value;
}

481 482
/* 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
483 484 485
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
			       int num)
{
486
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
487 488
	u32 dif_len = addr - aligned_addr;
	u32 i;
489

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

492 493 494 495
	if (num <= 0) {
		return;
	}

496
	/* Read the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
497
	if (unlikely(dif_len)) {
498
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenos's avatar
James Ketrenos committed
499
		/* Start reading at aligned_addr + dif_len */
500
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
501
			*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos's avatar
James Ketrenos committed
502 503 504
		aligned_addr += 4;
	}

505
	/* Read all of the middle dwords as dwords, with auto-increment */
506
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
507
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
508
		*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
509

510
	/* Read the last dword (or portion) byte by byte */
511
	if (unlikely(num)) {
512
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
513
		for (i = 0; num > 0; i++, num--)
514
			*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
515
	}
James Ketrenos's avatar
James Ketrenos committed
516 517
}

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

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

529 530 531 532
	if (num <= 0) {
		return;
	}

533
	/* Write the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
534
	if (unlikely(dif_len)) {
535
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
536
		/* Start writing at aligned_addr + dif_len */
537
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
538
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos's avatar
James Ketrenos committed
539 540
		aligned_addr += 4;
	}
541

542
	/* Write all of the middle dwords as dwords, with auto-increment */
543
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
544
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
545
		_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
546

547
	/* Write the last dword (or portion) byte by byte */
548
	if (unlikely(num)) {
549
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
550
		for (i = 0; num > 0; i++, num--, buf++)
551
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
552
	}
James Ketrenos's avatar
James Ketrenos committed
553 554
}

555 556
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for 1st 4K of SRAM/regs space */
557
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos's avatar
James Ketrenos committed
558 559 560 561 562
			     int num)
{
	memcpy_toio((priv->hw_base + addr), buf, num);
}

563
/* Set bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
564 565 566 567 568
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
	ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}

569
/* Clear bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
570 571 572 573 574
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
575
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
576 577 578 579
{
	if (priv->status & STATUS_INT_ENABLED)
		return;
	priv->status |= STATUS_INT_ENABLED;
580
	ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
581 582
}

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

647 648
static void ipw_dump_error_log(struct ipw_priv *priv,
			       struct ipw_fw_error *error)
James Ketrenos's avatar
James Ketrenos committed
649
{
650
	u32 i;
651

652 653 654 655
	if (!error) {
		IPW_ERROR("Error allocating and capturing error log.  "
			  "Nothing to dump.\n");
		return;
James Ketrenos's avatar
James Ketrenos committed
656 657
	}

658 659 660
	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
661

662
	for (i = 0; i < error->elem_len; i++)
663
		IPW_ERROR("%s %i 0x%08x  0x%08x  0x%08x  0x%08x  0x%08x\n",
664 665 666 667 668 669 670 671 672
			  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,
673
			  error->log[i].data, error->log[i].event);
James Ketrenos's avatar
James Ketrenos committed
674 675
}

676
static inline int ipw_is_init(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
677
{
678
	return (priv->status & STATUS_INIT) ? 1 : 0;
James Ketrenos's avatar
James Ketrenos committed
679 680
}

681
static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos's avatar
James Ketrenos committed
682 683 684 685 686 687 688 689 690
{
	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;
	}
691

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

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

		*len = sizeof(u32);
		ord <<= 2;
729
		*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos's avatar
James Ketrenos committed
730 731 732 733 734
		break;

	case IPW_ORD_TABLE_1_MASK:
		/*
		 * TABLE 1: Indirect access to a table of 32 bit values
735 736
		 *
		 * This is a fairly large table of u32 values each
James Ketrenos's avatar
James Ketrenos committed
737 738 739 740 741 742
		 * representing starting addr for the data (which is
		 * also a u32)
		 */

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

James Ketrenos's avatar
James Ketrenos committed
744 745 746 747 748 749 750 751 752
		/* 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, "
753
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
754 755 756
			return -EINVAL;
		}

757 758
		*((u32 *) val) =
		    ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos's avatar
James Ketrenos committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
		*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));
783 784

		/* get the second DW of statistics ;
James Ketrenos's avatar
James Ketrenos committed
785
		 * two 16-bit words - first is length, second is count */
786 787 788 789
		field_info =
		    ipw_read_reg32(priv,
				   priv->table2_addr + (ord << 3) +
				   sizeof(u32));
790

James Ketrenos's avatar
James Ketrenos committed
791
		/* get each entry length */
792
		field_len = *((u16 *) & field_info);
793

James Ketrenos's avatar
James Ketrenos committed
794
		/* get number of entries */
795
		field_count = *(((u16 *) & field_info) + 1);
796

797
		/* abort if not enough memory */
James Ketrenos's avatar
James Ketrenos committed
798 799 800 801 802
		total_len = field_len * field_count;
		if (total_len > *len) {
			*len = total_len;
			return -EINVAL;
		}
803

James Ketrenos's avatar
James Ketrenos committed
804 805 806 807 808
		*len = total_len;
		if (!total_len)
			return 0;

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

	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);
839
	priv->table2_len &= 0x0000ffff;	/* use first two bytes */
James Ketrenos's avatar
James Ketrenos committed
840 841 842 843 844 845

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

}

846
static u32 ipw_register_toggle(u32 reg)
847
{
848 849 850 851 852 853 854
	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;
855 856 857 858 859 860 861 862 863 864 865 866
	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
 *
 */
867 868 869
#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)
870

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

		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

		priv->status |= STATUS_LED_LINK_ON;

		/* If we aren't associated, schedule turning the LED off */
		if (!(priv->status & STATUS_ASSOCIATED))
898 899
			schedule_delayed_work(&priv->led_link_off,
					      LD_TIME_LINK_ON);
900 901 902 903 904
	}

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

David Howells's avatar
David Howells committed
905
static void ipw_bg_led_link_on(struct work_struct *work)
906
{
David Howells's avatar
David Howells committed
907 908
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_on.work);
909
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
910
	ipw_led_link_on(priv);
911
	mutex_unlock(&priv->mutex);
912 913
}

914
static void ipw_led_link_off(struct ipw_priv *priv)
915 916 917 918 919 920 921 922 923 924 925 926
{
	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) {
927
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
928 929 930 931
		led &= priv->led_association_off;
		led = ipw_register_toggle(led);

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

		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))
942 943
			schedule_delayed_work(&priv->led_link_on,
					      LD_TIME_LINK_OFF);
944 945 946 947 948 949

	}

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

David Howells's avatar
David Howells committed
950
static void ipw_bg_led_link_off(struct work_struct *work)
951
{
David Howells's avatar
David Howells committed
952 953
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_off.work);
954
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
955
	ipw_led_link_off(priv);
956
	mutex_unlock(&priv->mutex);
957 958
}

959
static void __ipw_led_activity_on(struct ipw_priv *priv)
960 961 962 963 964 965
{
	u32 led;

	if (priv->config & CFG_NO_LED)
		return;

966
	if (priv->status & STATUS_RF_KILL_MASK)
967 968 969
		return;

	if (!(priv->status & STATUS_LED_ACT_ON)) {
970
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
971 972 973 974 975
		led |= priv->led_activity_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
976
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
977 978 979 980 981

		IPW_DEBUG_LED("Activity LED On\n");

		priv->status |= STATUS_LED_ACT_ON;

982
		cancel_delayed_work(&priv->led_act_off);
983
		schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
984 985 986
	} else {
		/* Reschedule LED off for full time period */
		cancel_delayed_work(&priv->led_act_off);
987
		schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);