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

ipw2200.c 322 KB
Newer Older
James Ketrenos's avatar
James Ketrenos committed
1
/******************************************************************************
2

3
  Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
James Ketrenos's avatar
James Ketrenos committed
4 5 6 7 8 9 10

  802.11 status code portion of this file from ethereal-0.10.6:
    Copyright 2000, Axis Communications AB
    Ethereal - Network traffic analyzer
    By Gerald Combs <gerald@ethereal.com>
    Copyright 1998 Gerald Combs

11 12
  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License as
James Ketrenos's avatar
James Ketrenos committed
13
  published by the Free Software Foundation.
14 15 16 17

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
James Ketrenos's avatar
James Ketrenos committed
18
  more details.
19

James Ketrenos's avatar
James Ketrenos committed
20
  You should have received a copy of the GNU General Public License along with
21
  this program; if not, write to the Free Software Foundation, Inc., 59
James Ketrenos's avatar
James Ketrenos committed
22
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23

James Ketrenos's avatar
James Ketrenos committed
24 25
  The full GNU General Public License is included in this distribution in the
  file called LICENSE.
26

James Ketrenos's avatar
James Ketrenos committed
27 28 29 30 31 32 33 34
  Contact Information:
  James P. Ketrenos <ipw2100-admin@linux.intel.com>
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

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

#include "ipw2200.h"

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

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

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

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

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

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

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

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

77 78
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)

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

84
static int cmdlog = 0;
James Ketrenos's avatar
James Ketrenos committed
85
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 106
#ifdef CONFIG_IPW2200_PROMISCUOUS
static int rtap_iface = 0;     /* def: 0 -- do not create rtap interface */
#endif


107
#ifdef CONFIG_IPW2200_QOS
108 109 110 111 112 113
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;

114
static struct libipw_qos_parameters def_qos_parameters_OFDM = {
115 116 117 118 119 120 121 122 123 124
	{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}
};

125
static struct libipw_qos_parameters def_qos_parameters_CCK = {
126 127 128 129 130 131 132 133 134 135
	{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}
};

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

147
static struct libipw_qos_parameters def_parameters_CCK = {
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
	{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);

167
static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters
168
				       *qos_param);
169
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element
170
				     *qos_param);
171
#endif				/* CONFIG_IPW2200_QOS */
172

173
static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
174
static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos's avatar
James Ketrenos committed
175
static void ipw_rx(struct ipw_priv *priv);
176
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos's avatar
James Ketrenos committed
177 178 179 180 181 182 183 184 185 186 187 188
				struct clx2_tx_queue *txq, int qindex);
static int ipw_queue_reset(struct ipw_priv *priv);

static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
			     int len, int sync);

static void ipw_tx_queue_free(struct ipw_priv *);

static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
static void ipw_rx_queue_replenish(void *);
static int ipw_up(struct ipw_priv *);
David Howells's avatar
David Howells committed
189
static void ipw_bg_up(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
190
static void ipw_down(struct ipw_priv *);
David Howells's avatar
David Howells committed
191
static void ipw_bg_down(struct work_struct *work);
James Ketrenos's avatar
James Ketrenos committed
192
static int ipw_config(struct ipw_priv *);
193 194
static int init_supported_rates(struct ipw_priv *priv,
				struct ipw_supported_rates *prates);
195 196
static void ipw_set_hwcrypto_keys(struct ipw_priv *);
static void ipw_send_wep_keys(struct ipw_priv *, int);
James Ketrenos's avatar
James Ketrenos committed
197

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

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

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

James Ketrenos's avatar
James Ketrenos committed
215 216 217 218 219 220 221
	out += snprintf(buf + out, count - out, " ");
	for (l = 0, i = 0; i < 2; i++) {
		out += snprintf(buf + out, count - out, " ");
		for (j = 0; j < 8 && l < len; j++, l++) {
			c = data[(i * 8 + j)];
			if (!isascii(c) || !isprint(c))
				c = '.';
222

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

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

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

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

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

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
{
	size_t out = size;
	u32 ofs = 0;
	int total = 0;

	while (size && len) {
		out = snprint_line(output, size, &data[ofs],
				   min_t(size_t, len, 16U), ofs);

		ofs += 16;
		output += out;
		size -= out;
		len -= min_t(size_t, len, 16U);
		total += out;
	}
	return total;
}

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

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

276
/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
277 278 279
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
{
280 281
	IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
		     __LINE__, (u32) (b), (u32) (c));
James Ketrenos's avatar
James Ketrenos committed
282 283 284
	_ipw_write_reg8(a, b, c);
}

285
/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
286 287 288
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
{
289 290
	IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
		     __LINE__, (u32) (b), (u32) (c));
James Ketrenos's avatar
James Ketrenos committed
291 292 293
	_ipw_write_reg16(a, b, c);
}

294
/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos's avatar
James Ketrenos committed
295 296 297
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
{
298 299
	IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
		     __LINE__, (u32) (b), (u32) (c));
James Ketrenos's avatar
James Ketrenos committed
300 301 302
	_ipw_write_reg32(a, b, c);
}

303
/* 8-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
304 305 306 307 308
static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs,
		u8 val)
{
	writeb(val, ipw->hw_base + ofs);
}
309 310

/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
311
#define ipw_write8(ipw, ofs, val) do { \
Jiri Slaby's avatar
Jiri Slaby committed
312 313 314 315
	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
316

317
/* 16-bit direct write (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
318 319 320 321 322
static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs,
		u16 val)
{
	writew(val, ipw->hw_base + ofs);
}
323 324

/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
325 326 327 328 329
#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
330

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

/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
339 340 341 342 343
#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
344

345
/* 8-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
346
static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs)
347
{
Jiri Slaby's avatar
Jiri Slaby committed
348
	return readb(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
349
}
350

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

358
/* 16-bit direct read (low 4K) */
Jiri Slaby's avatar
Jiri Slaby committed
359
static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs)
360
{
Jiri Slaby's avatar
Jiri Slaby committed
361
	return readw(ipw->hw_base + ofs);
James Ketrenos's avatar
James Ketrenos committed
362
}
363

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

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

377
/* alias to 32-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_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
383 384

static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
385
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
Jiri Slaby's avatar
Jiri Slaby committed
386 387 388 389 390
#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
391

392
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
393 394
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
				int num);
Jiri Slaby's avatar
Jiri Slaby committed
395 396 397 398 399
#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
400

401
/* 32-bit indirect write (above 4K) */
402
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos's avatar
James Ketrenos committed
403
{
404
	IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
405 406
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	_ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos's avatar
James Ketrenos committed
407 408
}

409
/* 8-bit indirect write (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
410 411
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
412
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
413 414
	u32 dif_len = reg - aligned_addr;

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

420
/* 16-bit indirect write (above 4K) */
421
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos's avatar
James Ketrenos committed
422
{
423
	u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK;	/* dword align */
424 425
	u32 dif_len = (reg - aligned_addr) & (~0x1ul);

James Ketrenos's avatar
James Ketrenos committed
426
	IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
427 428
	_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
	_ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos's avatar
James Ketrenos committed
429 430
}

431
/* 8-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
432 433 434
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
	u32 word;
435
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
James Ketrenos's avatar
James Ketrenos committed
436
	IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
437
	word = _ipw_read32(priv, IPW_INDIRECT_DATA);
438
	return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos's avatar
James Ketrenos committed
439 440
}

441
/* 32-bit indirect read (above 4K) */
James Ketrenos's avatar
James Ketrenos committed
442 443 444 445 446 447
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
{
	u32 value;

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

448 449
	_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
	value = _ipw_read32(priv, IPW_INDIRECT_DATA);
James Ketrenos's avatar
James Ketrenos committed
450 451 452 453
	IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
	return value;
}

454 455
/* 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
456 457 458
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
			       int num)
{
459
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
460 461
	u32 dif_len = addr - aligned_addr;
	u32 i;
462

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

465 466 467 468
	if (num <= 0) {
		return;
	}

469
	/* Read the first dword (or portion) byte by byte */
James Ketrenos's avatar
James Ketrenos committed
470
	if (unlikely(dif_len)) {
471
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenos's avatar
James Ketrenos committed
472
		/* Start reading at aligned_addr + dif_len */
473
		for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
474
			*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos's avatar
James Ketrenos committed
475 476 477
		aligned_addr += 4;
	}

478
	/* Read all of the middle dwords as dwords, with auto-increment */
479
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
480
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
481
		*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
482

483
	/* Read the last dword (or portion) byte by byte */
484
	if (unlikely(num)) {
485
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
486
		for (i = 0; num > 0; i++, num--)
487
			*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
488
	}
James Ketrenos's avatar
James Ketrenos committed
489 490
}

491 492
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for area above 1st 4K of SRAM/reg space */
493
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos's avatar
James Ketrenos committed
494 495
				int num)
{
496
	u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK;	/* dword align */
James Ketrenos's avatar
James Ketrenos committed
497 498
	u32 dif_len = addr - aligned_addr;
	u32 i;
499

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

502 503 504 505
	if (num <= 0) {
		return;
	}

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

515
	/* Write all of the middle dwords as dwords, with auto-increment */
516
	_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
517
	for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
518
		_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
519

520
	/* Write the last dword (or portion) byte by byte */
521
	if (unlikely(num)) {
522
		_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
523
		for (i = 0; num > 0; i++, num--, buf++)
524
			_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
525
	}
James Ketrenos's avatar
James Ketrenos committed
526 527
}

528 529
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/*    for 1st 4K of SRAM/regs space */
530
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos's avatar
James Ketrenos committed
531 532 533 534 535
			     int num)
{
	memcpy_toio((priv->hw_base + addr), buf, num);
}

536
/* Set bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
537 538 539 540 541
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
	ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}

542
/* Clear bit(s) in low 4K of SRAM/regs */
James Ketrenos's avatar
James Ketrenos committed
543 544 545 546 547
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
548
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
549 550 551 552
{
	if (priv->status & STATUS_INT_ENABLED)
		return;
	priv->status |= STATUS_INT_ENABLED;
553
	ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
554 555
}

Zhu Yi's avatar
Zhu Yi committed
556
static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
557 558 559 560
{
	if (!(priv->status & STATUS_INT_ENABLED))
		return;
	priv->status &= ~STATUS_INT_ENABLED;
561
	ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos's avatar
James Ketrenos committed
562 563
}

Zhu Yi's avatar
Zhu Yi committed
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
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
582 583 584
static char *ipw_error_desc(u32 val)
{
	switch (val) {
585
	case IPW_FW_ERROR_OK:
James Ketrenos's avatar
James Ketrenos committed
586
		return "ERROR_OK";
587
	case IPW_FW_ERROR_FAIL:
James Ketrenos's avatar
James Ketrenos committed
588
		return "ERROR_FAIL";
589
	case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos's avatar
James Ketrenos committed
590
		return "MEMORY_UNDERFLOW";
591
	case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos's avatar
James Ketrenos committed
592
		return "MEMORY_OVERFLOW";
593
	case IPW_FW_ERROR_BAD_PARAM:
594
		return "BAD_PARAM";
595
	case IPW_FW_ERROR_BAD_CHECKSUM:
596
		return "BAD_CHECKSUM";
597
	case IPW_FW_ERROR_NMI_INTERRUPT:
598
		return "NMI_INTERRUPT";
599
	case IPW_FW_ERROR_BAD_DATABASE:
600
		return "BAD_DATABASE";
601
	case IPW_FW_ERROR_ALLOC_FAIL:
602
		return "ALLOC_FAIL";
603
	case IPW_FW_ERROR_DMA_UNDERRUN:
604
		return "DMA_UNDERRUN";
605
	case IPW_FW_ERROR_DMA_STATUS:
606 607 608 609 610
		return "DMA_STATUS";
	case IPW_FW_ERROR_DINO_ERROR:
		return "DINO_ERROR";
	case IPW_FW_ERROR_EEPROM_ERROR:
		return "EEPROM_ERROR";
611
	case IPW_FW_ERROR_SYSASSERT:
612
		return "SYSASSERT";
613
	case IPW_FW_ERROR_FATAL_ERROR:
614
		return "FATAL_ERROR";
615
	default:
616
		return "UNKNOWN_ERROR";
James Ketrenos's avatar
James Ketrenos committed
617 618 619
	}
}

620 621
static void ipw_dump_error_log(struct ipw_priv *priv,
			       struct ipw_fw_error *error)
James Ketrenos's avatar
James Ketrenos committed
622
{
623
	u32 i;
624

625 626 627 628
	if (!error) {
		IPW_ERROR("Error allocating and capturing error log.  "
			  "Nothing to dump.\n");
		return;
James Ketrenos's avatar
James Ketrenos committed
629 630
	}

631 632 633
	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
634

635
	for (i = 0; i < error->elem_len; i++)
636
		IPW_ERROR("%s %i 0x%08x  0x%08x  0x%08x  0x%08x  0x%08x\n",
637 638 639 640 641 642 643 644 645
			  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,
646
			  error->log[i].data, error->log[i].event);
James Ketrenos's avatar
James Ketrenos committed
647 648
}

649
static inline int ipw_is_init(struct ipw_priv *priv)
James Ketrenos's avatar
James Ketrenos committed
650
{
651
	return (priv->status & STATUS_INIT) ? 1 : 0;
James Ketrenos's avatar
James Ketrenos committed
652 653
}

654
static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos's avatar
James Ketrenos committed
655 656 657 658 659 660 661 662 663
{
	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;
	}
664

James Ketrenos's avatar
James Ketrenos committed
665 666 667 668 669 670 671 672 673 674 675
	/* 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
		 *
676
		 * This is a very simple table with the data directly
James Ketrenos's avatar
James Ketrenos committed
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
		 * 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, "
693
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
694 695 696 697
			return -EINVAL;
		}

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

		*len = sizeof(u32);
		ord <<= 2;
702
		*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos's avatar
James Ketrenos committed
703 704 705 706 707
		break;

	case IPW_ORD_TABLE_1_MASK:
		/*
		 * TABLE 1: Indirect access to a table of 32 bit values
708 709
		 *
		 * This is a fairly large table of u32 values each
James Ketrenos's avatar
James Ketrenos committed
710 711 712 713 714 715
		 * representing starting addr for the data (which is
		 * also a u32)
		 */

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

James Ketrenos's avatar
James Ketrenos committed
717 718 719 720 721 722 723 724 725
		/* 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, "
726
				      "need %zd\n", sizeof(u32));
James Ketrenos's avatar
James Ketrenos committed
727 728 729
			return -EINVAL;
		}

730 731
		*((u32 *) val) =
		    ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos's avatar
James Ketrenos committed
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
		*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));
756 757

		/* get the second DW of statistics ;
James Ketrenos's avatar
James Ketrenos committed
758
		 * two 16-bit words - first is length, second is count */
759 760 761 762
		field_info =
		    ipw_read_reg32(priv,
				   priv->table2_addr + (ord << 3) +
				   sizeof(u32));
763

James Ketrenos's avatar
James Ketrenos committed
764
		/* get each entry length */
765
		field_len = *((u16 *) & field_info);
766

James Ketrenos's avatar
James Ketrenos committed
767
		/* get number of entries */
768
		field_count = *(((u16 *) & field_info) + 1);
769

James Ketrenos's avatar
James Ketrenos committed
770 771 772 773 774 775
		/* abort if not enought memory */
		total_len = field_len * field_count;
		if (total_len > *len) {
			*len = total_len;
			return -EINVAL;
		}
776

James Ketrenos's avatar
James Ketrenos committed
777 778 779 780 781
		*len = total_len;
		if (!total_len)
			return 0;

		IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
782
			      "field_info = 0x%08x\n",
James Ketrenos's avatar
James Ketrenos committed
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
			      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;
799
	priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos's avatar
James Ketrenos committed
800 801 802 803 804 805 806 807 808 809 810 811

	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);
812
	priv->table2_len &= 0x0000ffff;	/* use first two bytes */
James Ketrenos's avatar
James Ketrenos committed
813 814 815 816 817 818

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

}

819
static u32 ipw_register_toggle(u32 reg)
820
{
821 822 823 824 825 826 827
	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;
828 829 830 831 832 833 834 835 836 837 838 839
	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
 *
 */
840 841 842
#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)
843

844
static void ipw_led_link_on(struct ipw_priv *priv)
845 846 847 848 849 850 851 852 853 854 855 856 857 858
{
	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");
859
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
860 861 862 863 864
		led |= priv->led_association_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
865
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
866 867 868 869 870 871 872 873 874 875 876 877 878

		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
879
static void ipw_bg_led_link_on(struct work_struct *work)
880
{
David Howells's avatar
David Howells committed
881 882
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_on.work);
883
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
884
	ipw_led_link_on(priv);
885
	mutex_unlock(&priv->mutex);
886 887
}

888
static void ipw_led_link_off(struct ipw_priv *priv)
889 890 891 892 893 894 895 896 897 898 899 900
{
	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) {
901
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
902 903 904 905
		led &= priv->led_association_off;
		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
906
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923

		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
924
static void ipw_bg_led_link_off(struct work_struct *work)
925
{
David Howells's avatar
David Howells committed
926 927
	struct ipw_priv *priv =
		container_of(work, struct ipw_priv, led_link_off.work);
928
	mutex_lock(&priv->mutex);
David Howells's avatar
David Howells committed
929
	ipw_led_link_off(priv);
930
	mutex_unlock(&priv->mutex);
931 932
}

933
static void __ipw_led_activity_on(struct ipw_priv *priv)
934 935 936 937 938 939
{
	u32 led;

	if (priv->config & CFG_NO_LED)
		return;

940
	if (priv->status & STATUS_RF_KILL_MASK)
941 942 943
		return;

	if (!(priv->status & STATUS_LED_ACT_ON)) {
944
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
945 946 947 948 949
		led |= priv->led_activity_on;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
950
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
951 952 953 954 955

		IPW_DEBUG_LED("Activity LED On\n");

		priv->status |= STATUS_LED_ACT_ON;

956
		cancel_delayed_work(&priv->led_act_off);
957 958 959 960 961 962 963 964
		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);
	}
965
}
966

967
#if 0
968 969 970 971 972
void ipw_led_activity_on(struct ipw_priv *priv)
{
	unsigned long flags;
	spin_lock_irqsave(&priv->lock, flags);
	__ipw_led_activity_on(priv);
973 974
	spin_unlock_irqrestore(&priv->lock, flags);
}
975
#endif  /*  0  */
976

977
static void ipw_led_activity_off(struct ipw_priv *priv)
978 979 980 981 982 983 984 985 986 987
{
	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) {
988
		led = ipw_read_reg32(priv, IPW_EVENT_REG);
989 990 991 992 993
		led &= priv->led_activity_off;

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
994
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
995 996 997 998 999 1000 1001 1002 1003

		IPW_DEBUG_LED("Activity LED Off\n");

		priv->status &= ~STATUS_LED_ACT_ON;
	}

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

David Howells's avatar
David Howells committed