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

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

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

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

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

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

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

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

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

78 79
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

		led = ipw_register_toggle(led);

		IPW_DEBUG_LED("Reg: 0x%08X\n", led);
890
		ipw_write_reg32(priv, IPW_EVENT_REG, led);
891 892 893 894 895 896 897 898 899 900 901 902 903

		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
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 941 942 943 944 945 946 947 948

		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
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 983 984 985 986 987 988 989
		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);
	}
990
}
991

992
#if 0
993 994 995 996 997
void ipw_led_activity_on(struct ipw_priv *priv)
{
	unsigned long flags;
	spin_lock_irqsave(&priv->lock, flags);
	__ipw_led_activity_on(priv);
998 999
	spin_unlock_irqrestore(&priv->lock, flags);
}
1000
#endif  /*  0  */