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 33 34
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

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

#include "ipw2200.h"

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

#ifndef KBUILD_EXTMOD
#define VK "k"
#else
#define VK
#endif

#ifdef CONFIG_IPW2200_DEBUG
#define VD "d"
#else
#define VD
#endif

#ifdef CONFIG_IPW2200_MONITOR
#define VM "m"
#else
#define VM
#endif

#ifdef CONFIG_IPW2200_PROMISCUOUS
#define VP "p"
#else
#define VP
#endif

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

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

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

77 78
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
884
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
885 886 887 888 889 890 891 892 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))
			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
898
static void ipw_bg_led_link_on(struct work_struct *work)
899
{
David Howells's avatar
David Howells committed
900 901
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_on.work);
902
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
903
	ipw_led_link_on(priv);
904
	mutex_unlock(&priv->mutex);
905 906
}

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

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

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

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

	if (priv->config & CFG_NO_LED)
		return;

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

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

		led = ipw_register_toggle(led);

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

		IPW_DEBUG_LED("Activity LED On\n");

		priv->status |= STATUS_LED_ACT_ON;

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

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

996
static void ipw_led_activity_off(struct ipw_priv *priv)
997 998 999 1000 1001 1002 1003 1004 1005 1006
{
	unsigned long flags;
	u32 led;

	if (priv->config & CFG_NO_LED)
		return;

	spin_lock_irqsave(&priv->lock, flags);

	if (priv->status & STATUS_LED_ACT_ON) {