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

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

#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

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

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

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

79 80
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)

James Ketrenos's avatar
James Ketrenos committed
81 82 83 84
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
85 86 87 88 89
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
90

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

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

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

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

133
#ifdef CONFIG_IPW2200_QOS
134 135 136 137 138 139
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;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

311
/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
312 313 314
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
{
315 316
	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
317 318 319
	_ipw_write_reg16(a, b, c);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
891
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
892 893 894 895 896

		priv->status |= STATUS_LED_LINK_ON;

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

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

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

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

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

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

	}

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

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

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

	if (priv->config & CFG_NO_LED)
		return;

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

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

		led = ipw_register_toggle(led);

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

		IPW_DEBUG_LED("Activity LED On\n");

		priv->status |= STATUS_LED_ACT_ON;

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